82 lines
2.1 KiB
TypeScript
82 lines
2.1 KiB
TypeScript
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<string, unknown>).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'));
|