Files
pikasTech-HWLAB/cmd/hwlab-device-pod/main.ts
T
2026-05-29 08:10:19 +08:00

175 lines
6.2 KiB
JavaScript

#!/usr/bin/env node
import { createServer } from "node:http";
import { randomUUID } from "node:crypto";
import { jsonResponse, listen, parsePort, readJson } from "../../internal/sim/http.mjs";
const SERVICE_ID = "hwlab-device-pod";
const CONTRACT_VERSION = "device-pod-executor-v1";
const DEFAULT_DEVICE_POD_ID = "device-pod-71-freq";
const port = parsePort(process.env.HWLAB_DEVICE_POD_PORT, parsePort(process.env.PORT, 7601));
const devicePodId = process.env.HWLAB_DEVICE_POD_ID || DEFAULT_DEVICE_POD_ID;
const environment = process.env.HWLAB_ENVIRONMENT || process.env.HWLAB_GITOPS_PROFILE || "dev";
listen(createServer(async (request, response) => {
try {
const url = new URL(request.url ?? "/", "http://localhost");
if (request.method === "GET" && (url.pathname === "/health" || url.pathname === "/health/live")) {
jsonResponse(response, 200, healthPayload());
return;
}
if (url.pathname === "/health" || url.pathname === "/health/live") {
jsonResponse(response, 405, { error: "method_not_allowed", method: request.method ?? "UNKNOWN", path: url.pathname });
return;
}
if (request.method === "GET" && url.pathname === "/v1/device-pods") {
jsonResponse(response, 200, listPayload());
return;
}
const route = parseDevicePodPath(url.pathname);
if (!route) {
jsonResponse(response, 404, { error: "not_found", path: url.pathname });
return;
}
if (request.method === "GET" && route.subpath === "status") {
jsonResponse(response, 200, statusPayload(route.devicePodId));
return;
}
if (request.method === "GET" && route.subpath === "events") {
jsonResponse(response, 200, eventsPayload(route.devicePodId, url.searchParams));
return;
}
if (request.method === "POST" && route.subpath === "jobs") {
await handleInternalJob(request, response, route.devicePodId);
return;
}
jsonResponse(response, 405, { error: "method_not_allowed", method: request.method ?? "UNKNOWN", path: url.pathname });
} catch (error) {
jsonResponse(response, 500, { error: "internal_error", message: error?.message ?? String(error), valuesRedacted: true });
}
}), port);
function healthPayload() {
return {
serviceId: SERVICE_ID,
environment,
status: "live",
ready: true,
contractVersion: CONTRACT_VERSION,
devicePodId,
observedAt: new Date().toISOString(),
role: "device-pod-internal-executor",
authority: "hwlab-cloud-api",
acceptsUserAuthority: false,
fake: false,
source: sourcePayload(),
note: "Profile, grant, lease, and user-facing job authority live in hwlab-cloud-api; this service only exposes the internal executor boundary."
};
}
function listPayload() {
return {
serviceId: SERVICE_ID,
contractVersion: CONTRACT_VERSION,
status: "ok",
source: sourcePayload(),
selectedDevicePodId: devicePodId,
devicePods: [{
devicePodId,
status: "executor-ready",
authority: "hwlab-cloud-api",
fake: false,
routes: {
cloudAuthority: "/v1/device-pods",
internalJob: `/v1/device-pods/${encodeURIComponent(devicePodId)}/jobs`
}
}]
};
}
function statusPayload(id) {
return {
serviceId: SERVICE_ID,
contractVersion: CONTRACT_VERSION,
status: "blocked",
devicePodId: id,
observedAt: new Date().toISOString(),
source: sourcePayload(),
summary: {
devicePodId: id,
status: "blocked",
blocker: gatewayAdapterBlocker("device-host-cli dispatch is owned by hwlab-cloud-api gateway routing")
}
};
}
function eventsPayload(id, searchParams) {
const limit = Math.min(Math.max(Number.parseInt(searchParams.get("limit") ?? "80", 10) || 80, 1), 1000);
const event = {
eventId: `evt_${id}_executor_boundary`,
devicePodId: id,
ts: new Date().toISOString(),
level: "warn",
scope: "executor",
status: "blocked",
summary: "No standalone fake device events are emitted by hwlab-device-pod; use hwlab-cloud-api job authority.",
blocker: gatewayAdapterBlocker("standalone device-pod executor has no gateway session")
};
return {
serviceId: SERVICE_ID,
contractVersion: CONTRACT_VERSION,
status: "ok",
devicePodId: id,
source: sourcePayload(),
events: limit > 0 ? [event] : [],
truncation: { limit, returned: limit > 0 ? 1 : 0, truncated: false }
};
}
async function handleInternalJob(request, response, id) {
if (!isInternalCaller(request)) {
jsonResponse(response, 403, {
accepted: false,
status: "blocked",
error: {
code: "cloud_api_authority_required",
message: "Device Pod jobs must be authorized by hwlab-cloud-api before reaching the internal executor."
},
source: sourcePayload()
});
return;
}
const body = await readJson(request);
const traceId = typeof body.traceId === "string" && body.traceId.startsWith("trc_") ? body.traceId : `trc_devicepod_${randomUUID()}`;
jsonResponse(response, 409, {
accepted: false,
status: "blocked",
contractVersion: CONTRACT_VERSION,
devicePodId: id,
traceId,
operationId: typeof body.operationId === "string" ? body.operationId : `op_devicepod_${randomUUID()}`,
blocker: gatewayAdapterBlocker("gateway/device-host-cli adapter is not connected to this standalone executor service"),
source: sourcePayload()
});
}
function parseDevicePodPath(pathname) {
const prefix = "/v1/device-pods/";
if (!pathname.startsWith(prefix)) return null;
const [id, ...rest] = pathname.slice(prefix.length).split("/").filter(Boolean).map((part) => decodeURIComponent(part));
return id ? { devicePodId: id, subpath: rest.join("/") } : null;
}
function isInternalCaller(request) {
const header = request.headers["x-hwlab-internal-service"];
return String(Array.isArray(header) ? header[0] : header ?? "").trim() === "hwlab-cloud-api";
}
function sourcePayload() {
return { kind: "INTERNAL_EXECUTOR", serviceId: SERVICE_ID, authority: "hwlab-cloud-api", fake: false, devLiveEvidence: false };
}
function gatewayAdapterBlocker(summary) {
return { code: "gateway_dispatch_unavailable", layer: "device-pod", retryable: true, summary, userMessage: "Device Pod executor boundary is present, but gateway/device-host-cli dispatch is not connected here." };
}