(app)
| 34 | * what happened. |
| 35 | */ |
| 36 | export const codingSpawnRoute: FastifyPluginAsync = async (app) => { |
| 37 | app.post<{ |
| 38 | Body: {agent?: string; dir?: string; sessionId?: string}; |
| 39 | }>('/spawn', async (req, reply) => { |
| 40 | const body = (req.body ?? {}) as { |
| 41 | agent?: string; |
| 42 | dir?: string; |
| 43 | sessionId?: string; |
| 44 | }; |
| 45 | |
| 46 | if (!body.agent || !body.dir) { |
| 47 | reply.code(400); |
| 48 | return {ok: false, error: 'agent and dir are required'}; |
| 49 | } |
| 50 | if (!existsSync(body.dir)) { |
| 51 | reply.code(400); |
| 52 | return {ok: false, error: `dir "${body.dir}" does not exist`}; |
| 53 | } |
| 54 | if (listFolders().length > 0) { |
| 55 | const folder = resolveFolderForPath(body.dir, 'coding'); |
| 56 | if (!folder) { |
| 57 | reply.code(403); |
| 58 | return { |
| 59 | ok: false, |
| 60 | error: `dir "${body.dir}" is not inside a coding-scoped folder`, |
| 61 | }; |
| 62 | } |
| 63 | } |
| 64 | const config = getAgentConfig(body.agent); |
| 65 | if (!config) { |
| 66 | reply.code(400); |
| 67 | return { |
| 68 | ok: false, |
| 69 | error: `agent "${body.agent}" is not configured on this gateway`, |
| 70 | }; |
| 71 | } |
| 72 | |
| 73 | const isResume = Boolean(body.sessionId); |
| 74 | |
| 75 | // Reconnect short-circuit: if the PTY is live for this id, don't respawn |
| 76 | // — xterm `attach` will replay the scrollback and keep streaming. Stale |
| 77 | // (exited) handles at the same id are dropped by `ptyManager.spawn`. |
| 78 | if (isResume && ptyManager.isRunning(body.sessionId!)) { |
| 79 | return { |
| 80 | ok: true, |
| 81 | sessionId: body.sessionId, |
| 82 | pid: ptyManager.getPid(body.sessionId!), |
| 83 | status: 'reconnect', |
| 84 | }; |
| 85 | } |
| 86 | |
| 87 | let sessionId = body.sessionId ?? randomUUID(); |
| 88 | const args = isResume |
| 89 | ? buildResumeArgs(config, {sessionId}) |
| 90 | : buildStartArgs(config, {sessionId}); |
| 91 | |
| 92 | if (isResume && args.length === 0) { |
| 93 | reply.code(400); |
nothing calls this directly
no test coverage detected