From 1827baaf39d91efa3a1c81474a93d3751eb628f8 Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Wed, 28 Jan 2026 13:20:47 -0500 Subject: [PATCH] Fix answer endpoint to avoid duplicate DB updates --- src/server.ts | 87 +++++++++++++++++++-------------------------------- src/types.ts | 2 +- 2 files changed, 33 insertions(+), 56 deletions(-) diff --git a/src/server.ts b/src/server.ts index 227d1ff..56845cf 100644 --- a/src/server.ts +++ b/src/server.ts @@ -157,22 +157,43 @@ const server = Bun.serve({ } const answer = body.response as AnswerResponse; - let inputData: string; - // Handle each response type + // Get WebSocket connection + const ws = sessionWebSockets.get(prompt.session_id); + if (!ws) { + return new Response("Session WebSocket not found", { status: 404 }); + } + + // Determine response value for DB and broadcast + let responseValue: string; if (answer.type === "option") { - inputData = `${answer.value}\n`; + responseValue = answer.value; } else if (answer.type === "text") { - inputData = `${answer.value}\n`; + responseValue = "custom"; } else if (answer.type === "tab_instructions") { - // Execute key sequence for tab instructions with delays: - // 1. Navigate to selected_option (up/down arrows) - // 2. Wait 100ms - // 3. Send Tab key - // 4. Wait 100ms - // 5. Write instruction text - // 6. Send Enter key + responseValue = `tab:${answer.selected_option}`; + } else { + return new Response("Invalid response type", { status: 400 }); + } + // Mark as responded in DB (once) + respondToPrompt(promptId, responseValue); + + // Broadcast to dashboards (once) + broadcastSSE({ + type: "prompt_response", + prompt_id: promptId, + response: responseValue, + }); + + // Handle WebSocket input based on type + if (answer.type === "option" || answer.type === "text") { + // Synchronous send for simple responses + const inputData = `${answer.value}\n`; + const message: ServerMessage = { type: "input", data: inputData }; + ws.send(JSON.stringify(message)); + } else if (answer.type === "tab_instructions") { + // Async execution for tab instructions const promptData = prompt.prompt_json; if (!promptData || promptData.prompt_type !== "permission") { return new Response( @@ -184,22 +205,6 @@ const server = Bun.serve({ const currentOption = promptData.selected_option; const targetOption = answer.selected_option; - // Mark as responded in DB first - respondToPrompt(promptId, `tab:${answer.selected_option}`); - - // Broadcast to dashboards - broadcastSSE({ - type: "prompt_response", - prompt_id: promptId, - response: answer.selected_option === 1 ? "approve" : "reject", - }); - - // Get WebSocket for async execution - const ws = sessionWebSockets.get(prompt.session_id); - if (!ws) { - return new Response("Session WebSocket not found", { status: 404 }); - } - // Execute key sequence asynchronously with delays (async () => { const sendInput = (data: string) => { @@ -224,36 +229,8 @@ const server = Bun.serve({ })().catch((err) => { console.error("Error executing tab instruction sequence:", err); }); - - // Return immediately (async execution continues in background) - return Response.json({ success: true }); - } else { - return new Response("Invalid response type", { status: 400 }); } - // Mark as responded in DB - respondToPrompt( - promptId, - answer.type === "option" ? answer.value : "custom", - ); - - // Notify CLI via WebSocket - const ws = sessionWebSockets.get(prompt.session_id); - if (ws) { - const message: ServerMessage = { type: "input", data: inputData }; - ws.send(JSON.stringify(message)); - } - - // Broadcast to dashboards - broadcastSSE({ - type: "prompt_response", - prompt_id: promptId, - response: - answer.type === "option" && answer.value === "1" - ? "approve" - : "reject", - }); - return Response.json({ success: true }); } diff --git a/src/types.ts b/src/types.ts index 7ccfee1..eb3511d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -118,5 +118,5 @@ export type SSEEvent = | { type: "prompt_response"; prompt_id: number; - response: "approve" | "reject"; + response: string; };