- Rebrand commits already in history (gsd → forge) - Sync pre-existing doc, docker, and CI config updates - All rebrand artifacts verified in place: * Native crates: forge-engine, forge-ast, forge-grep * Log prefixes: [forge] across 22+ files * Binary: ~/bin/sf-run * Workspace scopes: @sf-run/*, @singularity-forge/* * Nix flake: Rust toolchain ready System ready for: nix develop && bun run build:native Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
/**
|
|
* 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 { AuthStorage, AuthCredential } from '@sf-run/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-antigravity',
|
|
'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<string, AuthCredential>
|
|
|
|
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
|
|
}
|
|
}
|