import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai"; import { sanitizeApiKey } from "./env.js"; import { resolveClineAuthCredentials, refreshWorkosToken, credentialsFromWorkos, WORKOS_REFRESH_MARGIN_MS, } from "./workos.js"; const DASHBOARD_URL = "https://app.cline.bot/settings/api-keys"; const TEN_YEARS_MS = 10 * 365 * 24 * 60 * 60 * 1000; function credentialsFromApiKey(apiKey: string): OAuthCredentials { return { refresh: apiKey, access: apiKey, expires: Date.now() + TEN_YEARS_MS, }; } async function loginWithManualApiKey( callbacks: OAuthLoginCallbacks, reason?: string, ): Promise { callbacks.onAuth({ url: DASHBOARD_URL }); const apiKey = sanitizeApiKey( await callbacks.onPrompt({ message: (reason ? `${reason} ` : "No Cline CLI login detected. ") + "Paste your Cline API key " + "(create one at the dashboard that just opened, under Settings → API Keys, " + "or run `cline auth` first to reuse your Cline CLI login):", }), ); if (!apiKey) throw new Error("No Cline API key provided"); return credentialsFromApiKey(apiKey); } async function loginWithWorkosCredentials( clineAuth: NonNullable>, ): Promise { if (clineAuth.expiresAt <= Date.now() + WORKOS_REFRESH_MARGIN_MS) { const tempCred = credentialsFromWorkos( clineAuth.accessToken, clineAuth.refreshToken, clineAuth.expiresAt, ); return refreshWorkosToken(tempCred); } return credentialsFromWorkos(clineAuth.accessToken, clineAuth.refreshToken, clineAuth.expiresAt); } export async function login(callbacks: OAuthLoginCallbacks): Promise { const clineAuth = resolveClineAuthCredentials(); if (clineAuth) { try { return await loginWithWorkosCredentials(clineAuth); } catch (err) { const message = err instanceof Error ? err.message : String(err); console.warn(`[cline] WorkOS auto-login failed: ${message}`); return loginWithManualApiKey( callbacks, "Cline login failed (refresh token may be expired or network is unreachable).", ); } } return loginWithManualApiKey(callbacks); } export async function refreshToken(credentials: OAuthCredentials): Promise { if (credentials.access.startsWith("workos:")) { return refreshWorkosToken(credentials); } return credentialsFromApiKey(credentials.refresh); } export function getApiKey(credentials: OAuthCredentials): string { return credentials.access; }