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 { const header = request.headers.get("authorization") ?? ""; const runner = header.startsWith("Bearer ") ? await authenticateRunner(header.slice(7).trim()) : null; if (!runner) return json({ error: "Unknown or expired 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) : {}; } 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}`; }