import "server-only";
import { authenticateRunner, type RunnerIdentity } from "./runners";
/*
* The HTTP API that runners (public/vectrum-runner.mjs) talk to. Every request carries the runner's
* token as `Authorization: Bearer vrn_…`.
*
* POST /api/runner/claim { hostname, os, arch, version } → 200 { job } or 204 after waiting a while
* POST /api/runner/jobs/:id { steps?, logs?, status? } → { cancelled }
*/
export const json = (body: unknown, status = 200) => Response.json(body, { status, headers: { "Cache-Control": "no-store" } });
export async function runnerFromRequest(request: Request): Promise<RunnerIdentity | Response> {
const header = request.headers.get("authorization") ?? "";
const runner = header.startsWith("Bearer ") ? await authenticateRunner(header.slice(7).trim()) : null;
if (!runner) return json({ error: "Unknown runner token. Register the runner again in Settings → Runners." }, 401);
return runner;
}
export async function readBody(request: Request) {
try {
const body: unknown = await request.json();
return typeof body === "object" && body !== null ? (body as Record<string, unknown>) : {};
} catch {
return {};
}
}
/** The server's address as the runner reached it, for clone URLs. */
export function originOf(request: Request) {
const url = new URL(request.url);
const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host") ?? url.host;
const proto = request.headers.get("x-forwarded-proto") ?? url.protocol.replace(":", "");
return `${proto}://${host}`;
}