/** * One-time migration of provider credentials from Pi (~/.pi/agent/auth.json) * into SF's auth storage. Runs when SF has no LLM providers configured, * so users with an existing Pi install skip re-authentication. */ import { existsSync, readFileSync } from "node:fs"; import { homedir } from "node:os"; import { join } from "node:path"; import type { AuthCredential, AuthStorage, } from "@singularity-forge/pi-coding-agent"; const PI_AUTH_PATH = join(homedir(), ".pi", "agent", "auth.json"); const PI_SETTINGS_PATH = join(homedir(), ".pi", "agent", "settings.json"); const LLM_PROVIDER_IDS = [ "anthropic", "openai", "github-copilot", "openai-codex", "google-gemini-cli", "google", "groq", "xai", "openrouter", "mistral", ]; /** * Migrate provider credentials from Pi's auth.json into SF's AuthStorage. * * Only runs when SF has no LLM provider configured and Pi's auth.json exists. * Copies any credentials SF doesn't already have. Returns true if an LLM * provider was migrated (so onboarding can be skipped). */ export function migratePiCredentials(authStorage: AuthStorage): boolean { try { const existing = authStorage.list(); const hasLlm = existing.some((id) => LLM_PROVIDER_IDS.includes(id)); if (hasLlm) return false; if (!existsSync(PI_AUTH_PATH)) return false; const raw = readFileSync(PI_AUTH_PATH, "utf-8"); const piData = JSON.parse(raw) as Record; let migratedLlm = false; for (const [providerId, credential] of Object.entries(piData)) { if (authStorage.has(providerId)) continue; authStorage.set(providerId, credential); const isLlm = LLM_PROVIDER_IDS.includes(providerId); if (isLlm) migratedLlm = true; process.stderr.write( `[forge] Migrated ${isLlm ? "LLM provider" : "credential"}: ${providerId} (from Pi)\n`, ); } return migratedLlm; } catch { return false; } } export function getPiDefaultModelAndProvider(): { provider: string; model: string; } | null { try { if (!existsSync(PI_SETTINGS_PATH)) return null; const raw = readFileSync(PI_SETTINGS_PATH, "utf-8"); const data = JSON.parse(raw) as { defaultProvider?: unknown; defaultModel?: unknown; }; if ( typeof data.defaultProvider !== "string" || typeof data.defaultModel !== "string" ) { return null; } return { provider: data.defaultProvider, model: data.defaultModel, }; } catch { return null; } }