import type { SidecarCommand, SidecarEvent } from '../protocol'; import { loadState, flushAndSave, acquireLock, releaseLock, getState } from './state'; import { startAnthropicProxy, getProxySecret, ensureProxySecret } from './proxy'; import { createSidecarConnector } from '../connect'; const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; // ── Startup ── if (!acquireLock()) { console.error('[proxy] another instance is already running (lock file exists with live PID)'); process.exit(1); } loadState(); ensureProxySecret(); // Start Anthropic proxy try { startAnthropicProxy(); } catch (err) { console.error('[proxy] failed to start Anthropic proxy:', err instanceof Error ? err.message : err); } // ── Command handlers ── type ReplyFn = (msg: SidecarEvent) => void; async function handleCommand(cmd: SidecarCommand, reply: ReplyFn) { switch (cmd.type) { case 'ping': reply({ type: 'pong', id: cmd.id }); break; case 'state:sync': reply({ type: 'state:sync', id: cmd.id, state: { proxySecret: getProxySecret(), claudeSessions: { ...getState().claudeSessions }, }, }); break; case 'proxy:secret': reply({ type: 'proxy:secret', id: cmd.id, secret: getProxySecret() }); break; default: reply({ type: 'error', id: (cmd as SidecarCommand).id, error: `Unknown command type: ${(cmd as Record).type}`, }); } } // ── Connect to API server ── const connection = createSidecarConnector({ apiUrl: `${API_URL}/api/sidecar/register`, name: 'proxy', capabilities: ['proxy'], onCommand(cmd, reply) { handleCommand(cmd as SidecarCommand, reply as ReplyFn); }, }); // ── Graceful shutdown ── async function shutdown(signal: string) { console.log(`[proxy] ${signal} received, saving state...`); connection.destroy(); await flushAndSave(); releaseLock(); process.exit(0); } process.on('SIGTERM', () => shutdown('SIGTERM')); process.on('SIGINT', () => shutdown('SIGINT'));