Add /answer endpoint for rich prompt responses

Adds new POST /api/prompts/:id/answer endpoint that handles AnswerResponse types:
- option: sends value + newline to PTY
- text: sends user input + newline to PTY
- tab_instructions: placeholder implementation sends selected_option (TODO: needs key sequences)

Updated /approve and /reject endpoints to use "1\n" and "3\n" instead of "y\n" and "n\n"
for consistency with option-based responses. Marked as legacy endpoints for backward compatibility.
This commit is contained in:
Jared Miller 2026-01-28 12:56:53 -05:00
parent cd7d1c0ea8
commit 5404598a5e
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C

View file

@ -14,7 +14,12 @@ import {
respondToPrompt, respondToPrompt,
updateLastSeen, updateLastSeen,
} from "./db"; } from "./db";
import type { ClientMessage, ServerMessage, SSEEvent } from "./types"; import type {
AnswerResponse,
ClientMessage,
ServerMessage,
SSEEvent,
} from "./types";
// Server state // Server state
const sseClients = new Set<ReadableStreamDefaultController<string>>(); const sseClients = new Set<ReadableStreamDefaultController<string>>();
@ -133,6 +138,67 @@ const server = Bun.serve<SessionData>({
return new Response("Invalid prompt ID", { status: 400 }); return new Response("Invalid prompt ID", { status: 400 });
} }
// New unified answer endpoint
if (url.pathname.endsWith("/answer") && req.method === "POST") {
const prompt = getPrompt(promptId);
if (!prompt) {
return new Response("Prompt not found", { status: 404 });
}
if (prompt.response !== null) {
return new Response("Prompt already responded", { status: 400 });
}
const body = (await req.json()) as { response?: unknown };
if (!body.response || typeof body.response !== "object") {
return new Response("Missing or invalid response", { status: 400 });
}
const answer = body.response as AnswerResponse;
let inputData: string;
// Handle each response type
if (answer.type === "option") {
inputData = `${answer.value}\n`;
} else if (answer.type === "text") {
inputData = `${answer.value}\n`;
} else if (answer.type === "tab_instructions") {
// TODO: Implement proper key sequence handling for tab instructions
// For now, just send the selected option number
inputData = `${answer.selected_option}\n`;
console.debug(
`TODO: tab_instructions needs key sequence implementation - instruction: "${answer.instruction}"`,
);
} 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 });
}
// Legacy approve endpoint (backward compatibility)
if (url.pathname.endsWith("/approve") && req.method === "POST") { if (url.pathname.endsWith("/approve") && req.method === "POST") {
const prompt = getPrompt(promptId); const prompt = getPrompt(promptId);
if (!prompt) { if (!prompt) {
@ -144,10 +210,10 @@ const server = Bun.serve<SessionData>({
respondToPrompt(promptId, "approve"); respondToPrompt(promptId, "approve");
// Notify CLI via WebSocket // Notify CLI via WebSocket (using "1\n" for option 1)
const ws = sessionWebSockets.get(prompt.session_id); const ws = sessionWebSockets.get(prompt.session_id);
if (ws) { if (ws) {
const message: ServerMessage = { type: "input", data: "y\n" }; const message: ServerMessage = { type: "input", data: "1\n" };
ws.send(JSON.stringify(message)); ws.send(JSON.stringify(message));
} }
@ -161,6 +227,7 @@ const server = Bun.serve<SessionData>({
return Response.json({ success: true }); return Response.json({ success: true });
} }
// Legacy reject endpoint (backward compatibility)
if (url.pathname.endsWith("/reject") && req.method === "POST") { if (url.pathname.endsWith("/reject") && req.method === "POST") {
const prompt = getPrompt(promptId); const prompt = getPrompt(promptId);
if (!prompt) { if (!prompt) {
@ -172,10 +239,10 @@ const server = Bun.serve<SessionData>({
respondToPrompt(promptId, "reject"); respondToPrompt(promptId, "reject");
// Notify CLI via WebSocket // Notify CLI via WebSocket (using "3\n" for option 3)
const ws = sessionWebSockets.get(prompt.session_id); const ws = sessionWebSockets.get(prompt.session_id);
if (ws) { if (ws) {
const message: ServerMessage = { type: "input", data: "n\n" }; const message: ServerMessage = { type: "input", data: "3\n" };
ws.send(JSON.stringify(message)); ws.send(JSON.stringify(message));
} }