Files
pikasTech-HWLAB/internal/cloud/server-agent-observer-runs-http.ts
T
2026-07-16 07:50:02 +02:00

103 lines
4.3 KiB
TypeScript

// Responsibility: expose the AgentRun manager durable run collection through the authenticated HWLAB origin.
const DEFAULT_AGENTRUN_MGR_URL = "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080";
const AGENTRUN_MANAGER_HOST_PATTERN = /^agentrun-mgr\.[a-z0-9]([-a-z0-9]*[a-z0-9])?\.svc\.cluster\.local$/u;
export async function handleAgentObserverRunsHttp(request, response, url, options = {}) {
if (request.method !== "GET") {
response.writeHead(405, { allow: "GET", "content-type": "application/json; charset=utf-8" });
response.end(JSON.stringify({ ok: false, error: { code: "method_not_allowed", message: "GET required." } }));
return;
}
const env = options.env ?? process.env;
const managerUrl = resolveManagerUrl(env);
const apiKey = text(env.AGENTRUN_API_KEY) || text(env.HWLAB_CODE_AGENT_AGENTRUN_API_KEY) || text(env.HWLAB_PROVIDER_PROFILE_AGENTRUN_API_KEY) || text(env.HWLAB_API_KEY);
const timeoutMs = positiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 30_000);
const target = new URL("/api/v1/runs", managerUrl);
target.search = url.search;
const startedAt = Date.now();
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const headers: Record<string, string> = {
accept: "application/json",
"x-source-service-id": "hwlab-cloud-api",
"x-hwlab-delegation": "agent-observer-runs-v1",
};
if (apiKey) headers.authorization = `Bearer ${apiKey}`;
const delegated = await (options.fetchImpl ?? fetch)(target, { method: "GET", headers, signal: controller.signal });
const body = await delegated.text();
const payload = parseJson(body);
const data = payload?.data ?? payload;
if (!delegated.ok || payload?.ok === false || !data || typeof data !== "object") {
sendJson(response, delegated.status || 502, {
ok: false,
error: {
code: text(payload?.error?.code) || text(payload?.failureKind) || "agentrun_run_collection_failed",
message: text(payload?.error?.message) || text(payload?.message) || `AgentRun run collection returned HTTP ${delegated.status}.`,
httpStatus: delegated.status,
elapsedMs: Date.now() - startedAt,
valuesPrinted: false,
},
});
return;
}
sendJson(response, 200, {
ok: true,
status: "ok",
authority: "agentrun-manager-durable-store",
...data,
elapsedMs: Date.now() - startedAt,
valuesPrinted: false,
});
} catch (error) {
const timedOut = controller.signal.aborted || error?.name === "AbortError";
sendJson(response, timedOut ? 504 : 502, {
ok: false,
error: {
code: timedOut ? "agentrun_run_collection_timeout" : "agentrun_run_collection_unavailable",
message: timedOut ? `AgentRun run collection exceeded ${timeoutMs}ms.` : (error instanceof Error ? error.message : String(error)),
elapsedMs: Date.now() - startedAt,
valuesPrinted: false,
},
});
} finally {
clearTimeout(timer);
}
}
function resolveManagerUrl(env) {
const raw = text(env.AGENTRUN_MGR_URL) || text(env.HWLAB_CODE_AGENT_AGENTRUN_MGR_URL) || text(env.AGENTRUN_MANAGER_URL) || DEFAULT_AGENTRUN_MGR_URL;
const parsed = new URL(raw);
const allowLocal = truthy(env.HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL);
const localAllowed = allowLocal && ["127.0.0.1", "localhost"].includes(parsed.hostname);
if (!AGENTRUN_MANAGER_HOST_PATTERN.test(parsed.hostname) && !localAllowed) {
throw Object.assign(new Error("AgentRun manager URL must use the declared internal k3s service."), { code: "agentrun_manager_url_invalid" });
}
return raw.replace(/\/+$/u, "");
}
function positiveInteger(value, fallback) {
const number = Number(value);
return Number.isInteger(number) && number > 0 ? number : fallback;
}
function truthy(value) {
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
}
function text(value) {
return typeof value === "string" && value.trim() ? value.trim() : "";
}
function parseJson(value) {
try { return value ? JSON.parse(value) : null; } catch { return null; }
}
function sendJson(response, status, payload) {
response.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
response.end(JSON.stringify(payload));
}