From f065a9a7862744ff08b56d1e211896dcf12d462a Mon Sep 17 00:00:00 2001 From: Claw Date: Fri, 6 Mar 2026 01:09:30 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20auto-refresh=20Kernel=20Managed=20Auth?= =?UTF-8?q?=20session=20on=20login=20failure=20=E2=80=94=20ensureLoggedIn(?= =?UTF-8?q?)=20with=20retry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- job_searcher.mjs | 5 +-- lib/session.mjs | 67 +++++++++++++++++++++++++++++++++++++++++ test_linkedin_login.mjs | 17 +++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 lib/session.mjs create mode 100644 test_linkedin_login.mjs diff --git a/job_searcher.mjs b/job_searcher.mjs index 58a1b2f..3790b02 100644 --- a/job_searcher.mjs +++ b/job_searcher.mjs @@ -19,6 +19,7 @@ import { sendTelegram, formatSearchSummary } from './lib/notify.mjs'; import { DEFAULT_FIRST_RUN_DAYS } from './lib/constants.mjs'; import { generateKeywords } from './lib/keywords.mjs'; import { initProgress, isCompleted, markComplete } from './lib/search_progress.mjs'; +import { ensureLoggedIn } from './lib/session.mjs'; async function main() { const lock = acquireLock('searcher', resolve(__dir, 'data')); @@ -96,7 +97,7 @@ async function main() { let liBrowser; try { liBrowser = await createBrowser(settings, 'linkedin'); - const loggedIn = await liLogin(liBrowser.page); + const loggedIn = await ensureLoggedIn(liBrowser.page, liLogin, 'linkedin', settings.kernel_api_key || process.env.KERNEL_API_KEY); if (!loggedIn) throw new Error('LinkedIn not logged in'); console.log(' ✅ Logged in'); @@ -135,7 +136,7 @@ async function main() { let wfBrowser; try { wfBrowser = await createBrowser(settings, 'wellfound'); - const loggedIn = await wfLogin(wfBrowser.page); + const loggedIn = await ensureLoggedIn(wfBrowser.page, wfLogin, 'wellfound', settings.kernel_api_key || process.env.KERNEL_API_KEY); if (!loggedIn) console.warn(' ⚠️ Wellfound login unconfirmed, proceeding'); else console.log(' ✅ Logged in'); diff --git a/lib/session.mjs b/lib/session.mjs new file mode 100644 index 0000000..4cf4c30 --- /dev/null +++ b/lib/session.mjs @@ -0,0 +1,67 @@ +/** + * session.mjs — Kernel Managed Auth session refresh + * Call refreshSession() before creating a browser to ensure the profile is fresh + */ +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +const KERNEL_SDK_PATH = '/home/ubuntu/.openclaw/workspace/node_modules/@onkernel/sdk/index.js'; + +const CONNECTION_IDS = { + linkedin: 'REDACTED_CONNECTION_ID_LINKEDIN', + wellfound: 'REDACTED_CONNECTION_ID_WELLFOUND', +}; + +export async function refreshSession(platform, apiKey) { + const connectionId = CONNECTION_IDS[platform]; + if (!connectionId) throw new Error(`No connection ID for platform: ${platform}`); + + const Kernel = require(KERNEL_SDK_PATH); + const kernel = new Kernel({ apiKey }); + + console.log(` 🔄 Refreshing ${platform} session...`); + + // Trigger re-auth (uses stored credentials automatically) + const loginResp = await kernel.auth.connections.login(connectionId); + + if (loginResp.status === 'SUCCESS') { + console.log(` ✅ ${platform} session refreshed`); + return true; + } + + // If not immediately successful, poll for up to 30s + const start = Date.now(); + while (Date.now() - start < 30000) { + await new Promise(r => setTimeout(r, 2000)); + const conn = await kernel.auth.connections.retrieve(connectionId); + if (conn.status === 'SUCCESS') { + console.log(` ✅ ${platform} session refreshed`); + return true; + } + if (['FAILED', 'EXPIRED', 'CANCELED'].includes(conn.status)) { + console.warn(` ⚠️ ${platform} session refresh failed: ${conn.status}`); + return false; + } + } + + console.warn(` ⚠️ ${platform} session refresh timed out`); + return false; +} + +/** + * Verify login after browser connects — if not logged in, trigger refresh and retry + */ +export async function ensureLoggedIn(page, verifyFn, platform, apiKey, maxAttempts = 2) { + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + const loggedIn = await verifyFn(page); + if (loggedIn) return true; + + if (attempt < maxAttempts) { + console.warn(` ⚠️ ${platform} not logged in (attempt ${attempt}), refreshing session...`); + await refreshSession(platform, apiKey); + await page.reload({ waitUntil: 'domcontentloaded' }); + await page.waitForTimeout(3000); + } + } + return false; +} diff --git a/test_linkedin_login.mjs b/test_linkedin_login.mjs new file mode 100644 index 0000000..6245e03 --- /dev/null +++ b/test_linkedin_login.mjs @@ -0,0 +1,17 @@ +import { createBrowser } from './lib/browser.mjs'; +import { verifyLogin } from './lib/linkedin.mjs'; +import { loadConfig } from './lib/queue.mjs'; +import { dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; + +const __dir = dirname(fileURLToPath(import.meta.url)); +const settings = loadConfig(resolve(__dir, 'config/settings.json')); + +console.log('Creating Kernel browser with LinkedIn profile...'); +const b = await createBrowser(settings, 'linkedin'); +console.log('Browser created, checking login...'); +const loggedIn = await verifyLogin(b.page); +console.log('Logged in:', loggedIn); +console.log('URL:', b.page.url()); +await b.browser.close(); +console.log('Done.');