process sidecar: independent process manager for long-running work
Introduces a separate Bun process (port 5100) that owns all spawned processes and long-running work, so the API server can restart freely without disrupting active sessions. The sidecar owns: - Anthropic proxy (port 5051) with persisted secret across restarts - Claude Code process spawning and session tracking (--resume support) - Pi agent spawning and RPC lifecycle (prompt/abort/thinking) - Job queue engine (lane processing, retries, notifications) The API server becomes a thin client that forwards commands over a single WebSocket connection with auto-reconnect. send-claude-code.ts goes from 550 lines of spawn logic to 73 lines of sidecar delegation. State persisted to data/sidecar/state.json every 30s and on shutdown. Lockfile prevents duplicate instances. See SIDECAR.md for full docs and manual testing procedures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { createRouter } from '../../create-router';
|
||||
import { enqueue, cancelJob, readJob, listAllJobs } from '../../queue';
|
||||
import * as sidecar from '../../sidecar-client';
|
||||
import { NOT_FOUND } from '../../custom-errors';
|
||||
|
||||
export const queueRouter = createRouter();
|
||||
@@ -10,7 +10,7 @@ queueRouter.get('/jobs', async (ctx) => {
|
||||
const type = ctx.req.query('type');
|
||||
const status = ctx.req.query('status');
|
||||
|
||||
let jobs = await listAllJobs();
|
||||
let jobs = await sidecar.listJobs();
|
||||
jobs = jobs.filter((j) => j.userId === user.email);
|
||||
|
||||
if (lane) jobs = jobs.filter((j) => j.lane === lane);
|
||||
@@ -21,7 +21,7 @@ queueRouter.get('/jobs', async (ctx) => {
|
||||
});
|
||||
|
||||
queueRouter.get('/jobs/:id', async (ctx) => {
|
||||
const job = await readJob(ctx.req.param('id'));
|
||||
const job = await sidecar.getJob(ctx.req.param('id'));
|
||||
if (!job) throw NOT_FOUND('Job not found');
|
||||
return ctx.json(job);
|
||||
});
|
||||
@@ -36,12 +36,12 @@ queueRouter.post('/jobs', async (ctx) => {
|
||||
notify?: boolean;
|
||||
};
|
||||
|
||||
const job = await enqueue({ lane, type, userId: user.email, meta, notify });
|
||||
const job = await sidecar.enqueueJob({ lane, type, userId: user.email, meta, notify });
|
||||
return ctx.json(job, 201);
|
||||
});
|
||||
|
||||
queueRouter.delete('/jobs/:id', async (ctx) => {
|
||||
const job = await cancelJob(ctx.req.param('id'));
|
||||
const job = await sidecar.cancelJob(ctx.req.param('id'));
|
||||
if (!job) throw NOT_FOUND('Job not found');
|
||||
return ctx.json(job);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user