Look up auth connections by domain instead of stored ID
Connection IDs change when re-authing. Instead of storing IDs in
settings.json (which go stale), look up connections by domain at
runtime via kernel.auth.connections.list({ domain }). This keeps
the applier in sync regardless of connection recreation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -137,9 +137,8 @@ async function main() {
|
|||||||
browser = await createBrowser(settings, null); // no profile needed
|
browser = await createBrowser(settings, null); // no profile needed
|
||||||
} else {
|
} else {
|
||||||
// Check auth status before creating browser
|
// Check auth status before creating browser
|
||||||
const connectionIds = settings.kernel?.connection_ids || {};
|
|
||||||
const kernelApiKey = process.env.KERNEL_API_KEY || settings.kernel_api_key;
|
const kernelApiKey = process.env.KERNEL_API_KEY || settings.kernel_api_key;
|
||||||
const authResult = await ensureAuth(platform, kernelApiKey, connectionIds);
|
const authResult = await ensureAuth(platform, kernelApiKey);
|
||||||
if (!authResult.ok) {
|
if (!authResult.ok) {
|
||||||
console.error(` ❌ ${platform} auth failed: ${authResult.reason}`);
|
console.error(` ❌ ${platform} auth failed: ${authResult.reason}`);
|
||||||
await sendTelegram(settings, `⚠️ *${platform}* auth failed — ${authResult.reason}`).catch(() => {});
|
await sendTelegram(settings, `⚠️ *${platform}* auth failed — ${authResult.reason}`).catch(() => {});
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
* session.mjs — Kernel Managed Auth session management
|
* session.mjs — Kernel Managed Auth session management
|
||||||
* Checks auth status before browser creation, triggers re-auth when needed.
|
* Checks auth status before browser creation, triggers re-auth when needed.
|
||||||
*
|
*
|
||||||
|
* Looks up auth connections by domain (not stored ID) so it stays in sync
|
||||||
|
* even when connections are recreated.
|
||||||
|
*
|
||||||
* Flow:
|
* Flow:
|
||||||
* 1. Check connection status via SDK
|
* 1. List connections for domain, find the one matching our profile
|
||||||
* 2. If AUTHENTICATED → good to go
|
* 2. If AUTHENTICATED → good to go
|
||||||
* 3. If NEEDS_AUTH + can_reauth → trigger login(), poll until done
|
* 3. If NEEDS_AUTH + can_reauth → trigger login(), poll until done
|
||||||
* 4. If NEEDS_AUTH + !can_reauth → return false (caller should alert + skip)
|
* 4. If NEEDS_AUTH + !can_reauth → return false (caller should alert + skip)
|
||||||
@@ -14,29 +17,45 @@ import {
|
|||||||
} from './constants.mjs';
|
} from './constants.mjs';
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const PLATFORM_DOMAINS = {
|
||||||
|
linkedin: 'linkedin.com',
|
||||||
|
wellfound: 'wellfound.com',
|
||||||
|
};
|
||||||
|
|
||||||
function getKernel(apiKey) {
|
function getKernel(apiKey) {
|
||||||
const Kernel = require(KERNEL_SDK_PATH);
|
const Kernel = require(KERNEL_SDK_PATH);
|
||||||
return new Kernel({ apiKey });
|
return new Kernel({ apiKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find auth connection for a platform by domain lookup.
|
||||||
|
* Returns the connection object or null.
|
||||||
|
*/
|
||||||
|
async function findConnection(kernel, platform) {
|
||||||
|
const domain = PLATFORM_DOMAINS[platform];
|
||||||
|
if (!domain) return null;
|
||||||
|
|
||||||
|
const page = await kernel.auth.connections.list({ domain });
|
||||||
|
const connections = [];
|
||||||
|
for await (const conn of page) {
|
||||||
|
connections.push(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connections.length === 0) return null;
|
||||||
|
// If multiple connections for same domain, prefer AUTHENTICATED
|
||||||
|
return connections.find(c => c.status === 'AUTHENTICATED') || connections[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check auth connection status and re-auth if needed.
|
* Check auth connection status and re-auth if needed.
|
||||||
* Returns { ok: true } or { ok: false, reason: string }
|
* Returns { ok: true } or { ok: false, reason: string }
|
||||||
*/
|
*/
|
||||||
export async function ensureAuth(platform, apiKey, connectionIds = {}) {
|
export async function ensureAuth(platform, apiKey) {
|
||||||
const connectionId = connectionIds[platform];
|
|
||||||
if (!connectionId) {
|
|
||||||
return { ok: false, reason: `no connection ID configured for ${platform}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
const kernel = getKernel(apiKey);
|
const kernel = getKernel(apiKey);
|
||||||
|
|
||||||
// Check current status
|
const conn = await findConnection(kernel, platform);
|
||||||
let conn;
|
if (!conn) {
|
||||||
try {
|
return { ok: false, reason: `no auth connection found for ${platform} (domain: ${PLATFORM_DOMAINS[platform]})` };
|
||||||
conn = await kernel.auth.connections.retrieve(connectionId);
|
|
||||||
} catch (e) {
|
|
||||||
return { ok: false, reason: `connection ${connectionId} not found: ${e.message}` };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn.status === 'AUTHENTICATED') {
|
if (conn.status === 'AUTHENTICATED') {
|
||||||
@@ -45,13 +64,13 @@ export async function ensureAuth(platform, apiKey, connectionIds = {}) {
|
|||||||
|
|
||||||
// NEEDS_AUTH — can we auto re-auth?
|
// NEEDS_AUTH — can we auto re-auth?
|
||||||
if (!conn.can_reauth) {
|
if (!conn.can_reauth) {
|
||||||
return { ok: false, reason: `${platform} needs manual re-login (can_reauth=false). Go to Kernel dashboard or run: kernel auth connections login ${connectionId}` };
|
return { ok: false, reason: `${platform} needs manual re-login (can_reauth=false). Run: kernel auth connections login ${conn.id}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger re-auth with stored credentials
|
// Trigger re-auth with stored credentials
|
||||||
console.log(` 🔄 ${platform} session expired — re-authenticating...`);
|
console.log(` 🔄 ${platform} session expired — re-authenticating...`);
|
||||||
try {
|
try {
|
||||||
await kernel.auth.connections.login(connectionId);
|
await kernel.auth.connections.login(conn.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { ok: false, reason: `re-auth login() failed: ${e.message}` };
|
return { ok: false, reason: `re-auth login() failed: ${e.message}` };
|
||||||
}
|
}
|
||||||
@@ -60,22 +79,23 @@ export async function ensureAuth(platform, apiKey, connectionIds = {}) {
|
|||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
while (Date.now() - start < SESSION_REFRESH_POLL_TIMEOUT) {
|
while (Date.now() - start < SESSION_REFRESH_POLL_TIMEOUT) {
|
||||||
await new Promise(r => setTimeout(r, SESSION_REFRESH_POLL_WAIT));
|
await new Promise(r => setTimeout(r, SESSION_REFRESH_POLL_WAIT));
|
||||||
|
let updated;
|
||||||
try {
|
try {
|
||||||
conn = await kernel.auth.connections.retrieve(connectionId);
|
updated = await kernel.auth.connections.retrieve(conn.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { ok: false, reason: `polling failed: ${e.message}` };
|
return { ok: false, reason: `polling failed: ${e.message}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn.status === 'AUTHENTICATED') {
|
if (updated.status === 'AUTHENTICATED') {
|
||||||
console.log(` ✅ ${platform} re-authenticated`);
|
console.log(` ✅ ${platform} re-authenticated`);
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn.flow_status === 'FAILED') {
|
if (updated.flow_status === 'FAILED') {
|
||||||
return { ok: false, reason: `re-auth failed: ${conn.error_message || conn.error_code || 'unknown'}` };
|
return { ok: false, reason: `re-auth failed: ${updated.error_message || updated.error_code || 'unknown'}` };
|
||||||
}
|
}
|
||||||
if (conn.flow_status === 'EXPIRED' || conn.flow_status === 'CANCELED') {
|
if (updated.flow_status === 'EXPIRED' || updated.flow_status === 'CANCELED') {
|
||||||
return { ok: false, reason: `re-auth ${conn.flow_status.toLowerCase()}` };
|
return { ok: false, reason: `re-auth ${updated.flow_status.toLowerCase()}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user