import path from "node:path"; import Fastify from "fastify"; import fastifyStatic from "@fastify/static"; import { BossEngine } from "./engine.js"; const engine = new BossEngine(); const app = Fastify({ logger: true }); await app.register(fastifyStatic, { root: path.resolve(process.cwd(), "public"), prefix: "/", }); function ok() { return { ok: true }; } app.get("/api/health", async () => ({ status: "ok", sessions: engine.getState().sessions.length, workers: engine.getState().workers.length, })); app.get("/api/bootstrap", async () => engine.bootstrap()); app.get("/api/sessions", async () => engine.listSessions()); app.post("/api/sessions", async (request) => { const body = (request.body ?? {}) as { title?: string }; return engine.createSession(body.title); }); app.get("/api/sessions/:sessionId", async (request) => { const params = request.params as { sessionId: string }; return engine.getSession(params.sessionId); }); app.post("/api/sessions/:sessionId/messages", async (request) => { const params = request.params as { sessionId: string }; const body = (request.body ?? {}) as { content?: string; channel?: string }; return engine.addMessage(params.sessionId, body.content ?? "", body.channel ?? "web"); }); app.get("/api/events/stream", async (_request, reply) => { reply.raw.setHeader("Content-Type", "text/event-stream"); reply.raw.setHeader("Cache-Control", "no-cache"); reply.raw.setHeader("Connection", "keep-alive"); reply.raw.flushHeaders?.(); const unsubscribe = engine.events.subscribe((event) => { reply.raw.write(`data: ${JSON.stringify(event)}\n\n`); }); const interval = setInterval(() => { reply.raw.write(": ping\n\n"); }, 15_000); reply.raw.on("close", () => { clearInterval(interval); unsubscribe(); reply.raw.end(); }); }); app.get("/api/workers", async () => engine.getState().workers); app.post("/api/workers/register", async (request) => { const body = request.body as { name?: string; os?: "windows" | "macos" | "linux"; capabilities?: string[]; }; return engine.registerWorker({ name: body.name ?? "worker", os: body.os ?? "linux", capabilities: body.capabilities ?? ["terminal"], }); }); app.post("/api/workers/:workerId/heartbeat", async (request) => { const params = request.params as { workerId: string }; const body = (request.body ?? {}) as { load?: number }; return engine.heartbeat(params.workerId, body.load ?? 0); }); app.post("/api/workers/:workerId/claim-next", async (request) => { const params = request.params as { workerId: string }; return { task: engine.claimNextTask(params.workerId), }; }); app.post("/api/tasks/:taskId/progress", async (request) => { const params = request.params as { taskId: string }; const body = request.body as { workerId: string; progressPercent: number; summary: string; currentStep: string; nextStep: string; }; return engine.reportProgress(params.taskId, body.workerId, { progressPercent: body.progressPercent, summary: body.summary, currentStep: body.currentStep, nextStep: body.nextStep, }); }); app.post("/api/tasks/:taskId/complete", async (request) => { const params = request.params as { taskId: string }; const body = request.body as { workerId: string; summary: string; }; return engine.completeTask(params.taskId, body.workerId, body.summary); }); app.post("/api/tasks/:taskId/fail", async (request) => { const params = request.params as { taskId: string }; const body = request.body as { workerId: string; errorMessage: string; }; return engine.failTask(params.taskId, body.workerId, body.errorMessage); }); app.post("/api/tasks/:taskId/pause", async (request) => { const params = request.params as { taskId: string }; return engine.pauseTask(params.taskId); }); app.post("/api/tasks/:taskId/cancel", async (request) => { const params = request.params as { taskId: string }; return engine.cancelTask(params.taskId); }); app.post("/api/approvals/:approvalId/respond", async (request) => { const params = request.params as { approvalId: string }; const body = request.body as { approved: boolean; responder?: string; }; return engine.respondApproval(params.approvalId, body.approved, body.responder ?? "user"); }); app.post("/api/demo/reset", async () => { engine.store.reset(); return ok(); }); const port = Number(process.env.PORT ?? 43210); await app.listen({ port, host: "0.0.0.0" });