Files
pikasTech-HWLAB/web/hwlab-cloud-web/live-status.mjs
T
2026-05-23 13:37:08 +00:00

663 lines
23 KiB
JavaScript

const PASS_STATUSES = Object.freeze(["ok", "ready", "pass", "passed", "healthy", "available", "live"]);
const ERROR_STATUSES = Object.freeze(["error", "failed", "failure"]);
const RAW_DEGRADED_STATUSES = Object.freeze(["degraded"]);
const BLOCKED_STATUSES = Object.freeze(["blocked", "unavailable", "rejected"]);
const UNVERIFIED_STATUSES = Object.freeze(["", "unknown", "pending", "loading", "probing", "unverified", "not_observed"]);
const READ_ONLY_REASON_CODES = Object.freeze([
"read_only",
"read-only",
"readonly",
"permission",
"permissions",
"forbidden",
"unauthorized",
"runtime_durable",
"runtime-durable",
"durability",
"durable",
"db_persistence",
"persistence",
"not-codex-stdio",
"not-write-capable",
"not-durable-session",
"text-chat-only",
"fallback:text-chat-only",
"codex_stdio_blocked_readonly_available",
"controlled_readonly_not_long_lived_stdio",
"codex_stdio_supervisor_disabled",
"security_blocked"
]);
export function classifyWorkbenchLiveStatus(live = {}) {
const probes = collectLiveStatusProbes(live);
const reachable = probes.some((probe) => probe.reachable);
if (!reachable) {
const failedProbe = probes.find((probe) => !probe.reachable) ?? probes[0];
return statusResult({
kind: "unverified",
label: "等待验证",
tone: "pending",
serviceId: failedProbe?.serviceId ?? "hwlab-cloud-api",
apiPath: failedProbe?.apiPath ?? "/health/live",
reasonCode: failedProbe?.reasonCode ?? "api_unverified",
detail: failedProbe
? `等待验证:${probeAttribution(failedProbe)}。`
: "等待同源 API 验证;主工作台不会把未探测状态当作通过。",
probes
});
}
const errorProbe = probes.find((probe) => probe.kind === "error");
if (errorProbe) {
return statusResult({
kind: "error",
label: "API 错误",
tone: "blocked",
serviceId: errorProbe.serviceId,
apiPath: errorProbe.apiPath,
reasonCode: errorProbe.reasonCode,
traceId: errorProbe.traceId,
detail: `错误:${probeAttribution(errorProbe)}。`,
probes
});
}
const readOnlyProbe = probes.find((probe) => probe.kind === "readonly");
if (readOnlyProbe) {
return statusResult({
kind: "readonly",
label: "只读模式",
tone: "source",
serviceId: readOnlyProbe.serviceId,
apiPath: readOnlyProbe.apiPath,
reasonCode: readOnlyProbe.reasonCode,
traceId: readOnlyProbe.traceId,
detail: `只读:${probeAttribution(readOnlyProbe)}。`,
probes
});
}
const pendingProbe = probes.find((probe) => probe.kind === "unverified");
if (pendingProbe) {
return statusResult({
kind: "unverified",
label: "等待验证",
tone: "pending",
serviceId: pendingProbe.serviceId,
apiPath: pendingProbe.apiPath,
reasonCode: pendingProbe.reasonCode,
traceId: pendingProbe.traceId,
detail: `等待验证:${probeAttribution(pendingProbe)}。`,
probes
});
}
return statusResult({
kind: "pass",
label: "API 正常",
tone: "dev-live",
serviceId: "hwlab-cloud-api",
apiPath: "/health/live",
reasonCode: "ready",
detail: "通过:hwlab-cloud-api /health/live ready;关键同源 API 已完成当前工作台只读验证。",
probes
});
}
export function collectLiveStatusProbes(live = {}) {
return [
classifyApiProbe(live.healthLive, {
serviceId: "hwlab-cloud-api",
apiPath: "/health/live",
fallbackReasonCode: "health_live_unverified"
}),
classifyApiProbe(live.restIndex, {
serviceId: "hwlab-cloud-api",
apiPath: "/v1",
fallbackReasonCode: "rest_index_unverified"
}),
classifyApiProbe(live.health, {
serviceId: "hwlab-cloud-api",
apiPath: "/json-rpc system.health",
fallbackReasonCode: "system_health_unverified"
}),
classifyApiProbe(live.adapter, {
serviceId: "hwlab-cloud-api",
apiPath: "/json-rpc cloud.adapter.describe",
fallbackReasonCode: "adapter_describe_unverified"
}),
classifyCodeAgentProbe(live),
classifyM3ControlProbe(live),
classifyM3StatusProbe(live)
].filter(Boolean);
}
function classifyApiProbe(probe, context) {
if (!probe) {
return unverifiedProbe({
...context,
reasonCode: context.fallbackReasonCode,
reason: "not_observed"
});
}
if (probe.ok !== true) {
return errorProbe({
...context,
reasonCode: probe.timeout ? "timeout" : probe.status ? `HTTP ${probe.status}` : context.fallbackReasonCode,
reason: probe.error ?? "request_failed",
httpStatus: probe.status,
traceId: traceIdFrom(probe),
evidenceSummary: evidenceSummaryFrom(probe)
});
}
const payload = probe.data;
const serviceId = payload?.serviceId ?? context.serviceId;
if (context.apiPath === "/v1" && payload?.codeAgent) {
const codeAgentProbe = classifyCodeAgentProbe({ restIndex: probe });
if (codeAgentProbe?.kind === "readonly") {
return {
...codeAgentProbe,
apiPath: context.apiPath,
rawStatus: rawStatusFrom(payload)
};
}
}
const durableReason = durableRuntimeReason(payload);
if (durableReason) {
return readOnlyProbe({
...context,
serviceId,
...durableReason,
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
const blocker = firstBlocker(payload);
if (blocker) {
const reasonCode = String(blocker.code ?? blocker.reason ?? "blocked");
const reason = blocker.summary ?? blocker.message ?? blocker.reason ?? reasonCode;
if (isReadOnlyReason(reasonCode) || isReadOnlyReason(reason)) {
return readOnlyProbe({
...context,
serviceId,
reasonCode,
reason,
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
return errorProbe({
...context,
serviceId,
reasonCode,
reason,
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
const status = normalizedStatus(rawStatusFrom(payload));
if (ERROR_STATUSES.includes(status)) {
return errorProbe({
...context,
serviceId,
reasonCode: status,
reason: "API returned error status",
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
if (RAW_DEGRADED_STATUSES.includes(status) || BLOCKED_STATUSES.includes(status) || readinessFalse(payload)) {
const reasonCode = readinessFalse(payload)
? "ready_false_without_blocker"
: status === "degraded"
? "api_degraded_without_attribution"
: status;
return errorProbe({
...context,
serviceId,
reasonCode,
reason: "API readiness is not pass and no concrete blocker was provided",
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
if (UNVERIFIED_STATUSES.includes(status)) {
return unverifiedProbe({
...context,
serviceId,
reasonCode: context.fallbackReasonCode,
reason: "status_not_observed",
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
return passProbe({
...context,
serviceId,
reasonCode: status || "ready",
reason: "ready",
rawStatus: rawStatusFrom(payload),
traceId: traceIdFrom(payload),
evidenceSummary: evidenceSummaryFrom(payload)
});
}
function classifyCodeAgentProbe(live = {}) {
const availability = live.restIndex?.data?.codeAgent ?? live.healthLive?.data?.codeAgent;
if (!availability) {
return unverifiedProbe({
serviceId: "hwlab-cloud-api",
apiPath: "/v1/agent/chat",
reasonCode: "code_agent_unverified",
reason: "availability_not_observed"
});
}
const serviceId = "hwlab-cloud-api";
const apiPath = availability.endpoint ?? "POST /v1/agent/chat";
const provider = availability.provider ?? "not_observed";
const backend = availability.backend ?? "not_observed";
const capability = availability.capabilityLevel ?? availability.runner?.capabilityLevel;
const providerStatus = availability.error?.providerStatus ?? availability.providerStatus;
const gate = availability.longLivedSessionGate;
const stdio = availability.codexStdio ?? availability.codexStdioFeasibility;
if (gate?.status === "pass" && stdio?.canStartLongLivedCodexStdio === true && availability.ready === true) {
return passProbe({
serviceId,
apiPath,
reasonCode: "codex_stdio_ready",
reason: `provider=codex-stdio; backend=${availability.codexStdio?.backend ?? backend}; capability=long-lived-codex-stdio-session`,
traceId: traceIdFrom(availability),
evidenceSummary: evidenceSummaryFrom(availability),
meta: { provider: "codex-stdio", backend: availability.codexStdio?.backend ?? backend, capability: "long-lived-codex-stdio-session" }
});
}
if (capability === "text-chat-only" || availability.runner?.kind === "openai-responses-fallback") {
return readOnlyProbe({
serviceId,
apiPath,
reasonCode: "fallback_text_chat_only",
reason: `provider=${provider}; backend=${backend}; capability=text-chat-only`,
traceId: traceIdFrom(availability),
evidenceSummary: evidenceSummaryFrom(availability),
meta: { provider, backend, capability }
});
}
if (availability.agentKind === "controlled-readonly-session-registry" || availability.partialReady === true) {
return readOnlyProbe({
serviceId,
apiPath,
reasonCode: "codex_stdio_blocked_readonly_available",
reason: `${availability.summary ?? "controlled-readonly-session-registry available; long-lived Codex stdio blocked"}; blockers=${blockerCodes(availability).join(",") || "not_observed"}`,
traceId: traceIdFrom(availability),
evidenceSummary: evidenceSummaryFrom(availability),
meta: { provider, backend, capability }
});
}
if (availability.status === "blocked" || availability.ready === false) {
return errorProbe({
serviceId,
apiPath,
reasonCode: providerStatus ? `provider_http_${providerStatus}` : availability.reason ?? availability.blocker ?? "code_agent_blocked",
reason: availability.summary ?? `provider=${provider}; backend=${backend}; capability=${capability ?? "unknown"}`,
traceId: traceIdFrom(availability),
meta: { provider, backend, capability }
});
}
if (availability.runner?.ready === true && availability.runner?.writeCapable === false) {
return readOnlyProbe({
serviceId,
apiPath,
reasonCode: "codex_readonly_runner",
reason: `runner=${availability.runner.kind ?? "hwlab-readonly-runner"}; capability=${capability ?? "read-only-tools"}`,
traceId: traceIdFrom(availability),
evidenceSummary: evidenceSummaryFrom(availability),
meta: { provider, backend, capability }
});
}
if (availability.status === "available" || availability.ready === true) {
return passProbe({
serviceId,
apiPath,
reasonCode: capability ?? "available",
reason: `provider=${provider}; backend=${backend}`,
traceId: traceIdFrom(availability),
evidenceSummary: evidenceSummaryFrom(availability),
meta: { provider, backend, capability }
});
}
return unverifiedProbe({
serviceId,
apiPath,
reasonCode: "code_agent_unverified",
reason: `provider=${provider}; backend=${backend}`,
traceId: traceIdFrom(availability),
evidenceSummary: evidenceSummaryFrom(availability),
meta: { provider, backend, capability }
});
}
function blockerCodes(availability) {
if (Array.isArray(availability?.blockerCodes)) return availability.blockerCodes.filter(Boolean);
if (Array.isArray(availability?.blockers)) return availability.blockers.map((blocker) => blocker?.code).filter(Boolean);
return [];
}
function classifyM3ControlProbe(live = {}) {
const probe = live.m3Control;
const context = {
serviceId: "hwlab-cloud-api",
apiPath: "/v1/m3/io",
fallbackReasonCode: "m3_io_unverified"
};
if (!probe) {
return unverifiedProbe({ ...context, reasonCode: context.fallbackReasonCode, reason: "not_observed" });
}
if (probe.ok !== true) {
return errorProbe({
...context,
reasonCode: probe.timeout ? "timeout" : probe.status ? `HTTP ${probe.status}` : "m3_io_request_failed",
reason: probe.error ?? "M3 IO readiness request failed",
httpStatus: probe.status,
traceId: traceIdFrom(probe)
});
}
const data = probe.data;
const readiness = data?.readiness ?? {};
if (data?.status === "available" && readiness.status === "ready" && readiness.controlReady === true) {
return passProbe({
...context,
reasonCode: "m3_io_ready",
reason: "cloud-api -> gateway-simu -> box-simu -> hwlab-patch-panel readiness ready",
rawStatus: data.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
const blocker = readiness.blocker ?? data?.blocker;
return errorProbe({
...context,
serviceId: serviceForM3Layer(blocker?.layer) ?? context.serviceId,
reasonCode: blocker?.code ?? data?.status ?? "m3_io_not_ready",
reason: blocker?.zh ?? data?.blockedReason ?? "M3 IO readiness is not ready",
rawStatus: data?.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
function classifyM3StatusProbe(live = {}) {
const probe = live.m3Status;
const context = {
serviceId: "hwlab-cloud-api",
apiPath: "/v1/m3/status",
fallbackReasonCode: "m3_status_unverified"
};
if (!probe) {
return unverifiedProbe({ ...context, reasonCode: context.fallbackReasonCode, reason: "not_observed" });
}
if (probe.ok !== true) {
return errorProbe({
...context,
reasonCode: probe.timeout ? "timeout" : probe.status ? `HTTP ${probe.status}` : "m3_status_request_failed",
reason: probe.error ?? "M3 status aggregation failed",
httpStatus: probe.status,
traceId: traceIdFrom(probe)
});
}
const data = probe.data;
const blocker = data?.blocker;
if (data?.status === "error") {
return errorProbe({
...context,
serviceId: serviceForM3Layer(blocker?.layer) ?? context.serviceId,
reasonCode: blocker?.code ?? "m3_status_error",
reason: blocker?.zh ?? data?.summary ?? "M3 status returned error",
rawStatus: data.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
if (data?.status === "blocked" && blocker) {
const serviceId = serviceForM3Layer(blocker.layer) ?? (isReadOnlyReason(blocker.code) ? context.serviceId : context.serviceId);
const factory = isReadOnlyReason(blocker.code) || isReadOnlyReason(blocker.zh) ? readOnlyProbe : errorProbe;
return factory({
...context,
serviceId,
reasonCode: blocker.code ?? "m3_status_blocked",
reason: blocker.zh ?? data.summary ?? "M3 status blocked",
rawStatus: data.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
if (data?.status === "blocked") {
return unverifiedProbe({
...context,
reasonCode: "m3_status_waiting_trusted_record",
reason: data.summary ?? "M3 status is waiting for trusted persistence evidence",
rawStatus: data.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
if (data?.status === "live" || data?.sourceKind === "DEV-LIVE") {
return passProbe({
...context,
reasonCode: data.status ?? "dev_live",
reason: data.summary ?? "M3 status observed through cloud-api",
rawStatus: data.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
return unverifiedProbe({
...context,
reasonCode: "m3_status_unverified",
reason: data?.summary ?? "M3 status not yet verified",
rawStatus: data?.status,
traceId: traceIdFrom(data),
evidenceSummary: evidenceSummaryFrom(data)
});
}
function durableRuntimeReason(payload) {
const runtime = payload?.runtime;
const durability = payload?.readiness?.durability;
const reasonCode = firstNonEmpty(
runtime?.blocker,
durability?.blocker,
runtime?.connection?.queryResult,
durability?.queryResult,
runtime?.durabilityContract?.blockedLayer,
durability?.blockedLayer,
blockerCodeStartingWith(payload, "runtime_durable_adapter_")
);
const blockedLayer = firstNonEmpty(runtime?.durabilityContract?.blockedLayer, durability?.blockedLayer);
const runtimeDurable = runtime?.durable === true || durability?.durable === true;
const runtimeExplicitlyNonDurable = runtime?.durable === false || durability?.durable === false;
const runtimeReady = runtime?.ready === true || durability?.ready === true;
const liveRuntimeEvidence = runtime?.liveRuntimeEvidence === true || durability?.liveRuntimeEvidence === true;
if (!reasonCode && runtimeDurable && runtimeReady && liveRuntimeEvidence) return null;
if (runtimeExplicitlyNonDurable) {
return {
reasonCode: "runtime_durable_false",
reason: `runtime durable=false; adapter=${firstNonEmpty(runtime?.adapter, durability?.adapter, "unknown")}`,
layer: "runtime-durable"
};
}
if (
reasonCode &&
(
isReadOnlyReason(reasonCode) ||
String(reasonCode).endsWith("_blocked") ||
normalizedStatus(blockedLayer).includes("durability")
)
) {
return {
reasonCode,
reason: `runtime durable blocked at ${blockedLayer || "durability"}; queryResult=${firstNonEmpty(runtime?.connection?.queryResult, durability?.queryResult, "unknown")}`,
layer: blockedLayer || "runtime-durable"
};
}
return null;
}
function firstBlocker(payload) {
const blockers = [
...(Array.isArray(payload?.blockers) ? payload.blockers : []),
...(Array.isArray(payload?.readiness?.blockers) ? payload.readiness.blockers : [])
].filter(Boolean);
if (blockers.length > 0) return blockers[0];
const code = firstNonEmpty(
...(Array.isArray(payload?.blockerCodes) ? payload.blockerCodes : []),
...(Array.isArray(payload?.readiness?.blockerCodes) ? payload.readiness.blockerCodes : [])
);
return code ? { code, summary: code } : null;
}
function blockerCodeStartingWith(payload, prefix) {
return [
...(Array.isArray(payload?.blockerCodes) ? payload.blockerCodes : []),
...(Array.isArray(payload?.readiness?.blockerCodes) ? payload.readiness.blockerCodes : [])
].find((code) => String(code ?? "").startsWith(prefix));
}
function readinessFalse(payload) {
return [
payload?.ready,
payload?.readiness?.ready,
payload?.readiness?.durability?.ready,
payload?.runtime?.ready
].some((value) => value === false);
}
function rawStatusFrom(payload) {
return payload?.status ?? payload?.readiness?.status ?? payload?.runtime?.status ?? payload?.readiness?.durability?.status ?? "";
}
function statusResult(result) {
return {
serviceId: null,
apiPath: null,
reasonCode: null,
traceId: null,
internalRawStatuses: result.probes
.map((probe) => probe.rawStatus)
.filter((status) => status !== undefined && status !== null && String(status).trim() !== ""),
...result
};
}
function passProbe(options) {
return probe({ kind: "pass", reachable: true, ...options });
}
function readOnlyProbe(options) {
return probe({ kind: "readonly", reachable: true, ...options });
}
function errorProbe(options) {
return probe({ kind: "error", reachable: Boolean(options.httpStatus ? options.httpStatus >= 200 && options.httpStatus < 600 : true), ...options });
}
function unverifiedProbe(options) {
return probe({ kind: "unverified", reachable: false, ...options });
}
function probe(options) {
return {
kind: options.kind,
reachable: options.reachable,
serviceId: options.serviceId,
apiPath: options.apiPath,
reasonCode: String(options.reasonCode ?? options.kind),
reason: String(options.reason ?? options.reasonCode ?? options.kind),
httpStatus: options.httpStatus ?? null,
rawStatus: options.rawStatus ?? null,
traceId: options.traceId ?? null,
evidenceSummary: options.evidenceSummary ?? null,
meta: options.meta ?? null
};
}
function probeAttribution(probe) {
return [
`${probe.serviceId} ${probe.apiPath}`,
probe.httpStatus ? `HTTP ${probe.httpStatus}` : null,
probe.reasonCode,
probe.reason && probe.reason !== probe.reasonCode ? probe.reason : null,
probe.traceId ? `trace=${probe.traceId}` : null,
probe.evidenceSummary
].filter(Boolean).join(" / ");
}
function normalizedStatus(value) {
return String(value ?? "").trim().toLowerCase();
}
function isReadOnlyReason(value) {
const text = normalizedStatus(value);
return READ_ONLY_REASON_CODES.some((code) => text.includes(code));
}
function traceIdFrom(value) {
return firstNonEmpty(
value?.traceId,
value?.meta?.traceId,
value?.data?.traceId,
value?.data?.meta?.traceId,
value?.data?.trust?.traceId,
value?.request?.traceId,
value?.operation?.traceId,
value?.result?.traceId,
value?.trust?.traceId,
value?.evidenceState?.traceId,
value?.metadata?.traceId,
value?.error?.traceId,
value?.session?.lastTraceId,
value?.session?.currentTraceId,
value?.runnerTrace?.traceId,
value?.providerTrace?.traceId
);
}
function evidenceSummaryFrom(value) {
const payload = value?.data && typeof value.data === "object" ? value.data : value;
const parts = [
["operation", firstNonEmpty(payload?.operationId, payload?.operation?.operationId, payload?.result?.operationId, payload?.trust?.operationId)],
["audit", firstNonEmpty(payload?.auditId, payload?.audit?.auditId, payload?.auditEvent?.auditId, payload?.trust?.auditId)],
["evidence", firstNonEmpty(payload?.evidenceId, payload?.evidence?.evidenceId, payload?.evidenceRecord?.evidenceId, payload?.trust?.evidenceId)],
["session", firstNonEmpty(payload?.session?.sessionId, payload?.sessionId)]
]
.filter(([, id]) => id)
.slice(0, 4)
.map(([label, id]) => `${label}=${id}`);
return parts.length > 0 ? parts.join(" ") : null;
}
function serviceForM3Layer(layer) {
const text = normalizedStatus(layer);
if (!text) return null;
if (text.includes("patch")) return "hwlab-patch-panel";
if (text.includes("gateway")) return "hwlab-gateway-simu";
if (text.includes("box") || text.includes("resource")) return "hwlab-box-simu";
if (text.includes("runtime") || text.includes("durable")) return "hwlab-cloud-api";
return null;
}
function firstNonEmpty(...values) {
for (const value of values) {
if (value !== undefined && value !== null && String(value).trim() !== "") return String(value).trim();
}
return "";
}