import { exec, execFile } from 'node:child_process'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { dirname, join, resolve } from 'node:path'; import { homedir } from 'node:os'; import { fileURLToPath } from 'node:url'; import { promisify } from 'node:util'; import { getFrontendDualDbRoutingStatus } from './supabase.js'; const execAsync = promisify(exec); const execFileAsync = promisify(execFile); const AGENT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..'); const HF_PULL_SCRIPT = resolve(AGENT_ROOT, 'scripts', 'hf_pull_script.py'); const RUN_OPENCODE_SCRIPT = resolve(AGENT_ROOT, 'scripts', 'run_opencode.py'); const DEFAULT_STATE_PATH = join(homedir(), '.config', 'sin', 'sin-frontend', 'onboarding-state.json'); type OnboardingState = { ownerEmail?: string; notes?: string; updatedAt?: string; }; export type TemplateAgentAction = | { action: 'agent.help' } | { action: 'sin.frontend.health' } | { action: 'sin.frontend.onboarding.status' } | { action: 'sin.frontend.onboarding.save'; ownerEmail?: string; notes?: string; confirm?: boolean } | { action: 'sin.frontend.code.generate'; prompt: string; contextDir?: string } | { action: 'sin.frontend.code.review'; targetDir: string }; export type FrontendAgentAction = TemplateAgentAction; export async function executeTemplateAgentAction(action: TemplateAgentAction): Promise { switch (action.action) { case 'agent.help': return buildHelpPayload(); case 'sin.frontend.health': return { ok: true, agent: 'sin-code-frontend', primaryModel: 'google/antigravity-gemini-3.1-pro', fallbackModel: 'opencode/nemotron-3-super-free', team: 'Team - Coding', status: 'Elite Coder Runtime Online', dualDb: getFrontendDualDbRoutingStatus(), }; case 'sin.frontend.onboarding.status': return { ok: true, statePath: DEFAULT_STATE_PATH, state: await readState(), }; case 'sin.frontend.onboarding.save': if (!action.confirm) { throw new Error('input_required:confirm=true required'); } return { ok: true, statePath: DEFAULT_STATE_PATH, state: await writeState({ ownerEmail: clean(action.ownerEmail), notes: clean(action.notes), updatedAt: new Date().toISOString(), }), }; case 'sin.frontend.code.generate': return await executeOpenCode(action.prompt, action.contextDir); case 'sin.frontend.code.review': return await executeOpenCode( `Review ${action.targetDir} according to multi-billion dollar Google Workspace level Alpha standards.`, action.targetDir, ); } } export const executeFrontendAgentAction = executeTemplateAgentAction; async function executeOpenCode(prompt: string, dir?: string) { await execFileAsync('python3', [HF_PULL_SCRIPT], { cwd: AGENT_ROOT }).catch(() => { console.warn('Rotator token pull failed, using cached auth if available'); }); const options = dir ? { cwd: dir } : {}; try { const { stdout } = await execFileAsync('python3', [RUN_OPENCODE_SCRIPT, '--model', 'google/antigravity-gemini-3.1-pro', '--agent', 'sin-executor-solo', '--cwd', dir || AGENT_ROOT, '--timeout-sec', '900', '--prompt', prompt], { cwd: AGENT_ROOT }); const result = JSON.parse(stdout) as { ok: boolean; stdout: string; stderr: string; timeout: boolean; returncode: number }; if (!result.ok) { throw new Error(result.timeout ? `${result.stderr || result.stdout || 'opencode_timeout'}\nagent_timeout:900s` : (result.stderr || result.stdout || `opencode_returncode_${result.returncode}`)); } return { ok: true, expertAnalysis: result.stdout, warnings: result.stderr || undefined, }; } catch (error: any) { throw new Error(`Master Coder Execution Failed: ${error.message}`); } } function buildHelpPayload() { return { ok: true, agent: 'sin-code-frontend', mandate: 'CEO Elite Master Boss Coder for multi-billion dollar Alpha frontends.', actions: [ 'sin.frontend.health', 'sin.frontend.onboarding.status', 'sin.frontend.onboarding.save', 'sin.frontend.code.generate', 'sin.frontend.code.review', ], }; } async function readState(): Promise { try { return JSON.parse(await readFile(DEFAULT_STATE_PATH, 'utf8')) as OnboardingState; } catch { return null; } } async function writeState(state: OnboardingState) { await mkdir(dirname(DEFAULT_STATE_PATH), { recursive: true }); await writeFile(DEFAULT_STATE_PATH, `${JSON.stringify(state, null, 2)}\n`, 'utf8'); return state; } function clean(value: string | undefined) { const normalized = String(value || '').trim(); return normalized || undefined; }