- job_searcher.mjs: LinkedIn + Wellfound search, queue population - job_applier.mjs: Easy Apply + Wellfound apply, Mode A/B - lib/form_filler.mjs: config-driven form filling, custom answers.json - lib/linkedin.mjs: two-panel Easy Apply flow - lib/wellfound.mjs: Wellfound search + apply - lib/browser.mjs: Kernel stealth browser factory with local fallback - lib/queue.mjs: jobs_queue.json management - lib/notify.mjs: Telegram notifications - setup.mjs: setup wizard with login verification - Config templates: profile, search_config, answers, settings - SKILL.md: OpenClaw skill definition
67 lines
2.2 KiB
JavaScript
67 lines
2.2 KiB
JavaScript
/**
|
|
* browser.mjs — Browser factory
|
|
* Creates Kernel stealth browsers or falls back to local Playwright
|
|
*/
|
|
import { chromium } from 'playwright';
|
|
|
|
export async function createBrowser(settings, profileKey) {
|
|
const { provider, playwright_path } = settings.browser || {};
|
|
const kernelConfig = settings.kernel || {};
|
|
|
|
if (provider === 'local') {
|
|
return createLocalBrowser();
|
|
}
|
|
|
|
// Default: Kernel
|
|
try {
|
|
return await createKernelBrowser(kernelConfig, profileKey);
|
|
} catch (e) {
|
|
console.warn(`[browser] Kernel failed (${e.message}), falling back to local`);
|
|
return createLocalBrowser();
|
|
}
|
|
}
|
|
|
|
async function createKernelBrowser(kernelConfig, profileKey) {
|
|
// Dynamic import so it doesn't crash if not installed
|
|
let Kernel;
|
|
try {
|
|
const mod = await import('@onkernel/sdk');
|
|
Kernel = mod.default;
|
|
} catch {
|
|
throw new Error('Kernel SDK not installed — run: npm install @onkernel/sdk');
|
|
}
|
|
|
|
if (!process.env.KERNEL_API_KEY) throw new Error('KERNEL_API_KEY not set');
|
|
const kernel = new Kernel({ apiKey: process.env.KERNEL_API_KEY });
|
|
|
|
const profileName = kernelConfig.profiles?.[profileKey];
|
|
if (!profileName) throw new Error(`No Kernel profile configured for "${profileKey}"`);
|
|
|
|
const opts = { stealth: true, profile: { name: profileName } };
|
|
if (kernelConfig.proxy_id) opts.proxy = { id: kernelConfig.proxy_id };
|
|
|
|
const kb = await kernel.browsers.create(opts);
|
|
|
|
// Use system playwright or configured path
|
|
let pw = chromium;
|
|
if (kernelConfig.playwright_path) {
|
|
const mod = await import(kernelConfig.playwright_path);
|
|
pw = mod.chromium;
|
|
}
|
|
|
|
const browser = await pw.connectOverCDP(kb.cdp_ws_url);
|
|
const ctx = browser.contexts()[0] || await browser.newContext();
|
|
const page = ctx.pages()[0] || await ctx.newPage();
|
|
|
|
return { browser, page, type: 'kernel' };
|
|
}
|
|
|
|
async function createLocalBrowser() {
|
|
const browser = await chromium.launch({ headless: true });
|
|
const ctx = await browser.newContext({
|
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
|
|
});
|
|
const page = await ctx.newPage();
|
|
return { browser, page, type: 'local' };
|
|
}
|