pi-remote-control/extensions/remote-control/server/routes/input.ts

106 lines
2.7 KiB
TypeScript

/**
* S-03 — send-keys input route.
*
* POST /sessions/:id/input
*
* Body (IC-1 ClientToServer subset, but as HTTP POST for non-WS clients):
* { type: "key"; name: string }
* { type: "keys"; data: string }
* { type: "paste"; data: string }
*
* Response: 204 No Content on success, 400 on bad input, 404 if session missing.
*
* Note: the primary path for send-keys is via WS (T-1.5 stream route handles
* key/keys/paste messages inline). This HTTP endpoint is for clients that
* don't have an open stream (e.g. one-shot CLI tools).
*
* Owner: T-1.5
*/
import type { IncomingMessage, ServerResponse } from "node:http";
import { sendKey, sendKeys, sendPaste } from "../../tmux/input.js";
import { getSession } from "../../tmux/manager.js";
import { readBody, sendJson } from "../util.js";
export async function handleInput(
req: IncomingMessage,
res: ServerResponse,
sessionId: string,
): Promise<void> {
const session = await getSession(sessionId).catch(() => null);
if (!session) {
sendJson(res, 404, {
error: "session_not_found",
message: `Session "${sessionId}" not found`,
});
return;
}
let body: unknown;
try {
body = JSON.parse(await readBody(req));
} catch {
sendJson(res, 400, { error: "bad_request", message: "Invalid JSON body" });
return;
}
if (!body || typeof body !== "object") {
sendJson(res, 400, {
error: "bad_request",
message: "Body must be a JSON object",
});
return;
}
const m = body as Record<string, unknown>;
try {
switch (m.type) {
case "key": {
if (typeof m.name !== "string") {
sendJson(res, 400, {
error: "bad_request",
message: "key.name must be a string",
});
return;
}
await sendKey(sessionId, m.name);
break;
}
case "keys": {
if (typeof m.data !== "string") {
sendJson(res, 400, {
error: "bad_request",
message: "keys.data must be a string",
});
return;
}
await sendKeys(sessionId, m.data);
break;
}
case "paste": {
if (typeof m.data !== "string") {
sendJson(res, 400, {
error: "bad_request",
message: "paste.data must be a string",
});
return;
}
await sendPaste(sessionId, m.data);
break;
}
default:
sendJson(res, 400, {
error: "bad_request",
message: `Unknown type: ${String(m.type)}`,
});
return;
}
} catch (err) {
sendJson(res, 500, { error: "internal_error", message: String(err) });
return;
}
res.writeHead(204).end();
}