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:
parent
cd7d1c0ea8
commit
5404598a5e
1 changed files with 72 additions and 5 deletions
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue