Files
pikasTech-HWLAB/internal/cloud/m3-io-control.mjs
T
2026-05-23 14:06:11 +00:00

2306 lines
73 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createHash, randomUUID } from "node:crypto";
import {
ENVIRONMENT_DEV,
ERROR_CODES,
HwlabProtocolError,
assertProtocolRecord
} from "../protocol/index.mjs";
import {
CLOUD_API_SERVICE_ID,
createProtocolAuditEvent
} from "../audit/index.mjs";
import { HWLAB_M3_IO_CAPABILITY_LEVELS } from "../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";
export const M3_IO_CONTROL_CONTRACT_VERSION = "m3-io-control-v1";
export const M3_IO_CONTROL_ROUTE = "/v1/m3/io";
export const M3_STATUS_CONTRACT_VERSION = "m3-status-v1";
export const M3_STATUS_ROUTE = "/v1/m3/status";
export const M3_IO_RPC_METHODS = Object.freeze({
doWrite: "m3.io.do.write",
diRead: "m3.io.di.read"
});
export const M3_IO_CHAIN = Object.freeze({
projectId: "prj_m3_hardware_loop",
sourceGatewayId: "gwsimu_1",
sourceGatewaySessionId: "gws_gwsimu_1",
sourceResourceId: "res_boxsimu_1",
sourceBoxId: "boxsimu_1",
sourcePort: "DO1",
targetGatewayId: "gwsimu_2",
targetGatewaySessionId: "gws_gwsimu_2",
targetResourceId: "res_boxsimu_2",
targetBoxId: "boxsimu_2",
targetPort: "DI1",
patchPanelServiceId: "hwlab-patch-panel"
});
export const M3_IO_BLOCKER_CODES = Object.freeze({
gatewayUnavailable: "m3_gateway_session_unavailable",
gatewayIdentityMismatch: "m3_gateway_identity_mismatch",
boxUnavailable: "m3_box_resource_unavailable",
portDirectionInvalid: "m3_port_direction_invalid",
wiringMissing: "m3_wiring_missing",
patchPanelUnavailable: "m3_patch_panel_unavailable",
dispatchFailed: "m3_gateway_dispatch_failed",
runtimeDurableBlocked: "runtime_durable_not_green"
});
const DEFAULT_TIMEOUT_MS = 2200;
const DEV_SERVICE_ENDPOINTS = Object.freeze({
gateway1: "http://hwlab-gateway-simu-1.hwlab-dev.svc.cluster.local:7101",
gateway2: "http://hwlab-gateway-simu-2.hwlab-dev.svc.cluster.local:7101",
patchPanel: "http://hwlab-patch-panel.hwlab-dev.svc.cluster.local:7301"
});
const ACTIONS = new Set(["do.write", "di.read"]);
export function describeM3IoControl(options = {}) {
const env = options.env ?? process.env;
const config = buildM3IoConfig(env);
return {
serviceId: CLOUD_API_SERVICE_ID,
contractVersion: M3_IO_CONTROL_CONTRACT_VERSION,
route: M3_IO_CONTROL_ROUTE,
status: config.enabled ? "available" : "blocked",
capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
readinessStatus: "not_checked",
capabilityLevels: [
HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
HWLAB_M3_IO_CAPABILITY_LEVELS.ready
],
sourceKind: "SOURCE",
chain: M3_IO_CHAIN,
actions: [
{
action: "do.write",
label: "box-simu-1 DO1 write",
gatewayId: M3_IO_CHAIN.sourceGatewayId,
resourceId: M3_IO_CHAIN.sourceResourceId,
boxId: M3_IO_CHAIN.sourceBoxId,
port: M3_IO_CHAIN.sourcePort,
valueType: "boolean"
},
{
action: "di.read",
label: "box-simu-2 DI1 read",
gatewayId: M3_IO_CHAIN.targetGatewayId,
resourceId: M3_IO_CHAIN.targetResourceId,
boxId: M3_IO_CHAIN.targetBoxId,
port: M3_IO_CHAIN.targetPort,
valueType: "boolean"
}
],
boundaries: {
frontendCallsOnly: M3_IO_CONTROL_ROUTE,
cloudApiDispatchesToGateway: true,
patchPanelOwnsPropagation: true,
directFrontendGatewayOrBoxAccess: false,
genericHardwareRpcExposedToFrontend: false
},
endpointsConfigured: {
gateway1: Boolean(config.gateway1Url),
gateway2: Boolean(config.gateway2Url),
patchPanel: Boolean(config.patchPanelUrl)
},
blockedReason: config.enabled ? null : "M3 IO 控制未启用;前端保持 blocked,不直连 gateway/box-simu。"
};
}
export async function describeM3IoControlLive(options = {}) {
const contract = describeM3IoControl(options);
const env = options.env ?? process.env;
const config = buildM3IoConfig(env);
const runtime = await safeRuntimeSummary(options.runtimeStore);
const readiness = await buildM3IoReadiness({
config,
requestJson: options.m3IoRequestJson ?? options.requestJson,
now: options.now
});
const controlReady = readiness.status === "ready" && readiness.controlReady === true;
const capabilityLevel = controlReady
? HWLAB_M3_IO_CAPABILITY_LEVELS.ready
: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked;
const trustReadiness = buildM3IoTrustReadiness(runtime, {
observedAt: readiness.observedAt,
controlReady
});
return {
...contract,
status: controlReady ? "available" : "blocked",
sourceKind: controlReady ? "DEV-LIVE" : "BLOCKED",
capabilityLevel,
readinessStatus: readiness.status,
readiness: {
...readiness,
capabilityLevel,
trust: trustReadiness
},
trustReadiness,
controlReady,
blockedReason: controlReady
? null
: readiness.blocker?.zh ?? contract.blockedReason ?? "M3 IO 只读 readiness 未 green;控制面板保持阻塞。"
};
}
export async function describeM3StatusLive(options = {}) {
const env = options.env ?? process.env;
const config = buildM3IoConfig(env);
const requestJson = options.m3IoRequestJson ?? options.requestJson;
const observedAt = options.now?.() ?? new Date().toISOString();
const runtime = await safeRuntimeSummary(options.runtimeStore);
const trust = await buildM3StatusTrust({
runtimeStore: options.runtimeStore,
runtime
});
const base = {
serviceId: CLOUD_API_SERVICE_ID,
contractVersion: M3_STATUS_CONTRACT_VERSION,
route: M3_STATUS_ROUTE,
observedAt,
chain: M3_IO_CHAIN,
boundaries: {
frontendCallsOnly: M3_STATUS_ROUTE,
cloudApiAggregatesRuntimeStatus: true,
patchPanelOwnsPropagation: true,
directFrontendGatewayOrBoxAccess: false,
sourceTopologyFallbackMustStayUnverified: true
}
};
if (!config.enabled) {
return {
...base,
status: "blocked",
sourceKind: "BLOCKED",
summary: "M3 状态聚合未启用;前端不能绕过 cloud-api 直连 gateway/box-simu。",
gateways: defaultM3Gateways({ sourceKind: "UNVERIFIED", observedAt }),
boxes: defaultM3Boxes({ sourceKind: "UNVERIFIED", observedAt }),
patchPanel: defaultM3PatchPanel({ observedAt }),
trust: {
...trust,
blocker: "m3_control_disabled"
},
blocker: {
code: "m3_control_disabled",
layer: "cloud-api",
zh: "M3 状态聚合未启用;前端不能绕过 cloud-api 直连 gateway/box-simu。"
}
};
}
const [sourceGateway, targetGateway, patchStatus, patchWiring] = await Promise.all([
readGatewaySession(config.gateway1Url, {
gatewayRole: "source",
expectedGatewayId: M3_IO_CHAIN.sourceGatewayId,
expectedGatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
requestJson,
timeoutMs: config.timeoutMs
}),
readGatewaySession(config.gateway2Url, {
gatewayRole: "target",
expectedGatewayId: M3_IO_CHAIN.targetGatewayId,
expectedGatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
requestJson,
timeoutMs: config.timeoutMs
}),
requestRuntimeJson(new URL("/status", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
method: "GET",
requestJson,
timeoutMs: config.timeoutMs
}),
requestRuntimeJson(new URL("/wiring", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
method: "GET",
requestJson,
timeoutMs: config.timeoutMs
})
]);
const sourceCommand = normalizeCommand("do.write", { value: false });
const targetCommand = normalizeCommand("di.read", {});
const sourceBox = sourceGateway.available ? findGatewayBox(sourceGateway.status, sourceCommand) : null;
const targetBox = targetGateway.available ? findGatewayBox(targetGateway.status, targetCommand) : null;
const [sourceDoRead, targetDiRead] = await Promise.all([
sourceGateway.available && sourceBox
? readM3StatusPort({
gateway: sourceGateway,
command: sourceCommand,
requestJson,
timeoutMs: config.timeoutMs,
observedAt,
port: M3_IO_CHAIN.sourcePort,
direction: "output"
})
: Promise.resolve(unverifiedPortRead({
port: M3_IO_CHAIN.sourcePort,
direction: "output",
source: "gateway-simu",
reason: sourceGateway.reason ?? `${M3_IO_CHAIN.sourceResourceId} 未在 source gateway registry 中可见`,
observedAt
})),
targetGateway.available && targetBox
? readM3StatusPort({
gateway: targetGateway,
command: targetCommand,
requestJson,
timeoutMs: config.timeoutMs,
observedAt,
port: M3_IO_CHAIN.targetPort,
direction: "input"
})
: Promise.resolve(unverifiedPortRead({
port: M3_IO_CHAIN.targetPort,
direction: "input",
source: "gateway-simu",
reason: targetGateway.reason ?? `${M3_IO_CHAIN.targetResourceId} 未在 target gateway registry 中可见`,
observedAt
}))
]);
const gateways = [
gatewayStatusEntry(sourceGateway, {
id: M3_IO_CHAIN.sourceGatewayId,
sessionId: M3_IO_CHAIN.sourceGatewaySessionId,
role: "source",
observedAt
}),
gatewayStatusEntry(targetGateway, {
id: M3_IO_CHAIN.targetGatewayId,
sessionId: M3_IO_CHAIN.targetGatewaySessionId,
role: "target",
observedAt
})
];
const boxes = [
boxStatusEntry({
gateway: sourceGateway,
box: sourceBox,
boxId: M3_IO_CHAIN.sourceBoxId,
resourceId: M3_IO_CHAIN.sourceResourceId,
gatewayId: M3_IO_CHAIN.sourceGatewayId,
gatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
observedAt,
ports: {
[M3_IO_CHAIN.sourcePort]: sourceDoRead.port
}
}),
boxStatusEntry({
gateway: targetGateway,
box: targetBox,
boxId: M3_IO_CHAIN.targetBoxId,
resourceId: M3_IO_CHAIN.targetResourceId,
gatewayId: M3_IO_CHAIN.targetGatewayId,
gatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
observedAt,
ports: {
[M3_IO_CHAIN.targetPort]: targetDiRead.port
}
})
];
const patchPanel = patchPanelStatusEntry({
statusResponse: patchStatus,
wiringResponse: patchWiring,
observedAt
});
const blockers = [
...gateways.filter((gateway) => !gateway.online).map((gateway) => ({
code: gateway.blocker,
layer: gateway.id,
zh: gateway.lastError
})),
...boxes.filter((box) => !box.online).map((box) => ({
code: M3_IO_BLOCKER_CODES.boxUnavailable,
layer: box.resourceId,
zh: `${box.resourceId} 未在 gateway-simu registry 中可见`
})),
patchPanel.connectionActive ? null : {
code: patchPanel.observable === false ? M3_IO_BLOCKER_CODES.patchPanelUnavailable : M3_IO_BLOCKER_CODES.wiringMissing,
layer: M3_IO_CHAIN.patchPanelServiceId,
zh: patchPanel.lastError ?? `hwlab-patch-panel 未确认 active ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort} 接线`
},
sourceDoRead.error ? {
code: M3_IO_BLOCKER_CODES.dispatchFailed,
layer: M3_IO_CHAIN.sourceResourceId,
zh: sourceDoRead.error
} : null,
targetDiRead.error ? {
code: M3_IO_BLOCKER_CODES.dispatchFailed,
layer: M3_IO_CHAIN.targetResourceId,
zh: targetDiRead.error
} : null
].filter(Boolean);
const hardwareLive =
blockers.length === 0 &&
sourceDoRead.port.sourceKind === "DEV-LIVE" &&
targetDiRead.port.sourceKind === "DEV-LIVE";
const trustGreen = trust.durableStatus === "green" && trust.evidenceId && trust.auditId && trust.operationId && trust.traceId;
const status = blockers.length > 0
? blockers.some((blocker) => blocker.code === M3_IO_BLOCKER_CODES.dispatchFailed || blocker.code === M3_IO_BLOCKER_CODES.patchPanelUnavailable) ? "error" : "blocked"
: trustGreen ? "live" : hardwareLive ? "blocked" : "unverified";
const sourceKind = hardwareLive ? "DEV-LIVE" : "UNVERIFIED";
return {
...base,
status,
sourceKind,
summary: status === "live"
? "M3 硬件状态与可信持久化均为 DEV-LIVE。"
: hardwareLive
? "M3 DO1/DI1 实况读数来自 cloud-api 聚合入口;可信持久化仍保持 blocked,不得标为 trusted green。"
: "M3 硬件状态尚未完整验证;SOURCE 拓扑只能作为未验证 fallback。",
gateways,
boxes,
patchPanel,
trust,
blocker: blockers[0] ?? (trust.blocker ? {
code: trust.blocker,
layer: "runtime-durable",
zh: `runtime durable 仍 blocked${trust.blocker}`
} : null)
};
}
export async function handleM3IoControl(params = {}, context = {}) {
const env = context.env ?? process.env;
const now = context.now?.() ?? new Date().toISOString();
const config = buildM3IoConfig(env);
const action = normalizeAction(params.action ?? params.operation);
const meta = buildRequestMeta(params, context);
const runtimeBefore = await safeRuntimeSummary(context.runtimeStore);
if (!config.enabled) {
return blockedResult({
action,
meta,
runtime: runtimeBefore,
code: "m3_control_disabled",
reason: "M3 IO 控制未启用;前端不能绕过 cloud-api 直连 gateway/box-simu。",
httpStatus: 503,
now
});
}
if (!ACTIONS.has(action)) {
throw new HwlabProtocolError("M3 IO action must be do.write or di.read", {
code: ERROR_CODES.invalidParams,
data: {
action,
allowedActions: [...ACTIONS]
}
});
}
const command = normalizeCommand(action, params);
const directionIssue = validateM3Command(command);
if (directionIssue) {
return blockedResult({
action,
meta,
runtime: runtimeBefore,
code: M3_IO_BLOCKER_CODES.portDirectionInvalid,
reason: directionIssue,
httpStatus: 400,
now
});
}
const gatewayUrl = action === "do.write" ? config.gateway1Url : config.gateway2Url;
const gatewayRole = action === "do.write" ? "source" : "target";
const gateway = await readGatewaySession(gatewayUrl, {
gatewayRole,
expectedGatewayId: command.gatewayId,
expectedGatewaySessionId: command.gatewaySessionId,
requestJson: context.requestJson,
timeoutMs: config.timeoutMs
});
if (!gateway.available) {
return blockedResult({
action,
command,
meta,
runtime: runtimeBefore,
code: gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
reason: gateway.reason,
httpStatus: 200,
now,
details: {
gateway
}
});
}
const box = findGatewayBox(gateway.status, command);
if (!box) {
return blockedResult({
action,
command,
meta,
runtime: runtimeBefore,
code: M3_IO_BLOCKER_CODES.boxUnavailable,
reason: `${gatewayRole} gateway 已响应,但目标 box resource 未注册/不可用:${command.resourceId}`,
httpStatus: 200,
now,
details: {
gateway: publicGatewayStatus(gateway.status)
}
});
}
const capability = capabilityFor(command, box);
const operation = createOperationRecord({
action,
command,
capability,
gatewaySessionId: gateway.gatewaySessionId,
meta,
now
});
const registration = await persistRuntimeRegistrations({
runtimeStore: context.runtimeStore,
gatewayStatus: gateway.status,
box,
capability,
command,
meta
});
const operationWrite = await tryRuntimeWrite(
"hardware.operation.request",
() => context.runtimeStore?.requestHardwareOperation?.(operation, meta)
);
if (action === "di.read") {
return finalizeControlResult(await readDi({
config,
command,
capability,
gateway,
operation,
meta,
runtimeStore: context.runtimeStore,
runtimeBefore,
registration,
operationWrite,
requestJson: context.requestJson,
now
}));
}
return finalizeControlResult(await writeDo({
config,
command,
capability,
gateway,
operation,
meta,
runtimeStore: context.runtimeStore,
runtimeBefore,
registration,
operationWrite,
requestJson: context.requestJson,
now
}));
}
export async function buildM3IoReadiness({ config, requestJson, now } = {}) {
const observedAt = now?.() ?? new Date().toISOString();
const checks = [];
if (!config?.enabled) {
return blockedReadiness({
checks,
code: "m3_control_disabled",
layer: "cloud-api",
reason: "M3 IO 控制未启用;前端不能绕过 cloud-api 直连 gateway/box-simu。",
observedAt
});
}
const sourceGateway = await readGatewaySession(config.gateway1Url, {
gatewayRole: "source",
expectedGatewayId: M3_IO_CHAIN.sourceGatewayId,
expectedGatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
requestJson,
timeoutMs: config.timeoutMs
});
checks.push(readinessCheckFromGateway("source-gateway", sourceGateway));
if (!sourceGateway.available) {
return blockedReadiness({
checks,
code: sourceGateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
layer: "gateway-simu-1",
reason: sourceGateway.reason,
observedAt
});
}
const sourceCommand = normalizeCommand("do.write", {});
const sourceBox = findGatewayBox(sourceGateway.status, sourceCommand);
checks.push(readinessCheckFromBox("source-box", sourceBox, sourceCommand));
if (!sourceBox) {
return blockedReadiness({
checks,
code: M3_IO_BLOCKER_CODES.boxUnavailable,
layer: "box-simu-1",
reason: `gateway-simu-1 已响应,但 ${M3_IO_CHAIN.sourceResourceId} 未注册/不可用;控制面板保持阻塞。`,
observedAt
});
}
const targetGateway = await readGatewaySession(config.gateway2Url, {
gatewayRole: "target",
expectedGatewayId: M3_IO_CHAIN.targetGatewayId,
expectedGatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
requestJson,
timeoutMs: config.timeoutMs
});
checks.push(readinessCheckFromGateway("target-gateway", targetGateway));
if (!targetGateway.available) {
return blockedReadiness({
checks,
code: targetGateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
layer: "gateway-simu-2",
reason: targetGateway.reason,
observedAt
});
}
const targetCommand = normalizeCommand("di.read", {});
const targetBox = findGatewayBox(targetGateway.status, targetCommand);
checks.push(readinessCheckFromBox("target-box", targetBox, targetCommand));
if (!targetBox) {
return blockedReadiness({
checks,
code: M3_IO_BLOCKER_CODES.boxUnavailable,
layer: "box-simu-2",
reason: `gateway-simu-2 已响应,但 ${M3_IO_CHAIN.targetResourceId} 未注册/不可用;控制面板保持阻塞。`,
observedAt
});
}
const patchStatus = await requestRuntimeJson(new URL("/status", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
method: "GET",
requestJson,
timeoutMs: config.timeoutMs
});
checks.push(readinessCheckFromPatchPanelStatus(patchStatus));
if (!patchStatus.ok) {
return blockedReadiness({
checks,
code: M3_IO_BLOCKER_CODES.patchPanelUnavailable,
layer: "hwlab-patch-panel",
reason: `hwlab-patch-panel 不可用:${patchStatus.error ?? `HTTP ${patchStatus.status}`}`,
observedAt
});
}
const wiring = await requestRuntimeJson(new URL("/wiring", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
method: "GET",
requestJson,
timeoutMs: config.timeoutMs
});
checks.push(readinessCheckFromPatchPanelWiring(wiring));
if (!wiring.ok) {
return blockedReadiness({
checks,
code: M3_IO_BLOCKER_CODES.wiringMissing,
layer: "hwlab-patch-panel",
reason: `hwlab-patch-panel 接线读取失败:${wiring.error ?? `HTTP ${wiring.status}`}`,
observedAt
});
}
if (!hasExpectedPatchPanelWiring(patchStatus.body) || !hasExpectedPatchPanelWiring(wiring.body)) {
return blockedReadiness({
checks,
code: M3_IO_BLOCKER_CODES.wiringMissing,
layer: "hwlab-patch-panel",
reason: `hwlab-patch-panel 未确认 active ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort} 接线;控制面板保持阻塞。`,
observedAt
});
}
return {
status: "ready",
controlReady: true,
capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.ready,
sourceKind: "DEV-LIVE",
evidenceLevel: "DEV-LIVE",
observedAt,
summary: "cloud-api 只读 readiness 已确认 gateway-simu、box-simu 与 hwlab-patch-panel 目标接线;这只表示控制面板可执行受控操作,不等于 M3 验收通过。",
chain: M3_IO_CHAIN,
checks,
blocker: null
};
}
export async function handleM3IoRpc(action, params = {}, envelope = {}, context = {}) {
return handleM3IoControl(
{
...params,
action,
traceId: envelope.meta?.traceId ?? params.traceId,
requestId: envelope.id ?? params.requestId,
actorId: envelope.meta?.actorId ?? params.actorId
},
{
...context,
requestJson: context.requestJson ?? context.m3IoRequestJson,
traceId: envelope.meta?.traceId ?? context.traceId,
requestId: envelope.id ?? context.requestId,
actorId: envelope.meta?.actorId ?? context.actorId
}
);
}
export function buildM3IoConfig(env = process.env) {
const enabledValue = String(env.HWLAB_M3_IO_CONTROL_ENABLED ?? env.HWLAB_M3_CONTROL_ENABLED ?? "true").toLowerCase();
return {
enabled: !["0", "false", "no", "disabled"].includes(enabledValue),
gateway1Url: trimUrl(env.HWLAB_M3_GATEWAY_SIMU_1_URL) ?? DEV_SERVICE_ENDPOINTS.gateway1,
gateway2Url: trimUrl(env.HWLAB_M3_GATEWAY_SIMU_2_URL) ?? DEV_SERVICE_ENDPOINTS.gateway2,
patchPanelUrl: trimUrl(env.HWLAB_M3_PATCH_PANEL_URL) ?? DEV_SERVICE_ENDPOINTS.patchPanel,
timeoutMs: parseTimeout(env.HWLAB_M3_CONTROL_TIMEOUT_MS)
};
}
function blockedReadiness({ checks, code, layer, reason, observedAt }) {
return {
status: "blocked",
controlReady: false,
capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
sourceKind: "BLOCKED",
evidenceLevel: "BLOCKED",
observedAt,
summary: `M3 IO 只读 readiness 未 green${reason}`,
chain: M3_IO_CHAIN,
blocker: {
code,
layer,
zh: reason
},
capabilityBlocker: {
code,
layer,
zh: reason,
category: blockerCategory(code)
},
checks
};
}
function buildM3IoTrustReadiness(runtime, { observedAt, controlReady } = {}) {
const durable = durableStatus(runtime);
const ready = isDurableRuntimeReady(runtime);
const blocker = ready
? null
: {
code: durable.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked,
layer: "runtime-durable",
zh: `runtime durable 未 green${durable.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked}`,
category: "runtime_durable"
};
return {
status: ready ? "ready" : "blocked",
controlReady: Boolean(controlReady),
trustedEvidenceReady: ready,
capabilityLevel: controlReady
? HWLAB_M3_IO_CAPABILITY_LEVELS.ready
: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
evidenceLevel: ready ? "DEV-LIVE" : "BLOCKED",
sourceKind: ready ? "DEV-LIVE" : "BLOCKED",
observedAt,
durableStatus: durable,
blocker,
summary: ready
? "runtime durable 已 greenoperation/audit/evidence 可作为可信记录写入。"
: "受控硬件路径 readiness 与可信持久化分离;runtime durable 未 green 时不得宣称完整硬件控制验收。"
};
}
async function buildM3StatusTrust({ runtimeStore, runtime }) {
const durable = durableStatus(runtime);
const base = {
operationId: null,
traceId: null,
auditId: null,
evidenceId: null,
durableStatus: isDurableRuntimeReady(runtime) ? "green" : "blocked",
runtime: durable,
blocker: isDurableRuntimeReady(runtime) ? null : durable.blocker
};
const [audit, evidence] = await Promise.all([
safeRuntimeRead(() => runtimeStore?.queryAuditEvents?.({ projectId: M3_IO_CHAIN.projectId, limit: 8 })),
safeRuntimeRead(() => runtimeStore?.queryEvidenceRecords?.({ projectId: M3_IO_CHAIN.projectId, limit: 8 }))
]);
const latestAudit = latestM3AuditEvent(audit.value?.events ?? []);
const latestEvidence = latestM3EvidenceRecord(evidence.value?.records ?? []);
const operationId = latestEvidence?.operationId ?? latestAudit?.operationId ?? null;
const traceId = latestEvidence?.traceId ?? latestEvidence?.metadata?.traceId ?? latestAudit?.traceId ?? null;
return {
...base,
operationId,
traceId,
auditId: latestAudit?.auditId ?? null,
evidenceId: latestEvidence?.evidenceId ?? null,
durableStatus: base.durableStatus,
blocker: base.blocker ?? audit.error?.code ?? evidence.error?.code ?? null,
readStatus: {
audit: audit.ok ? "read" : "blocked",
evidence: evidence.ok ? "read" : "blocked",
auditError: audit.error?.message ?? null,
evidenceError: evidence.error?.message ?? null
}
};
}
async function safeRuntimeRead(read) {
if (typeof read !== "function") {
return {
ok: false,
value: null,
error: {
code: "runtime_read_unavailable",
message: "runtime read method is unavailable"
}
};
}
try {
const value = await read();
if (value === undefined || value === null) {
return {
ok: false,
value: null,
error: {
code: "runtime_read_unavailable",
message: "runtime read returned no value"
}
};
}
return {
ok: true,
value,
error: null
};
} catch (error) {
return {
ok: false,
value: null,
error: {
code: error?.code ?? "runtime_read_blocked",
message: error instanceof Error ? redactBlockerMessage(error.message) : String(error)
}
};
}
}
function latestM3AuditEvent(events) {
return [...events].reverse().find((event) =>
event?.operationId &&
event?.traceId &&
(event.action === "m3.io.do.write" || event.action === "m3.io.di.read" || String(event.metadata?.route ?? "").includes("res_boxsimu_1:DO1"))
) ?? null;
}
function latestM3EvidenceRecord(records) {
return [...records].reverse().find((record) =>
record?.operationId &&
(record.serviceId === CLOUD_API_SERVICE_ID || String(record.metadata?.route ?? "").includes("res_boxsimu_1:DO1"))
) ?? null;
}
function defaultM3Gateways({ sourceKind, observedAt }) {
return [
{
id: M3_IO_CHAIN.sourceGatewayId,
role: "source",
online: false,
observable: false,
sessionId: M3_IO_CHAIN.sourceGatewaySessionId,
lastSeenAt: observedAt,
sourceKind,
lastError: "未验证"
},
{
id: M3_IO_CHAIN.targetGatewayId,
role: "target",
online: false,
observable: false,
sessionId: M3_IO_CHAIN.targetGatewaySessionId,
lastSeenAt: observedAt,
sourceKind,
lastError: "未验证"
}
];
}
function defaultM3Boxes({ sourceKind, observedAt }) {
return [
{
id: M3_IO_CHAIN.sourceBoxId,
resourceId: M3_IO_CHAIN.sourceResourceId,
gatewayId: M3_IO_CHAIN.sourceGatewayId,
gatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
online: false,
observable: false,
sourceKind,
observedAt,
ports: {
[M3_IO_CHAIN.sourcePort]: {
value: null,
direction: "output",
source: "未验证",
sourceKind,
observedAt
}
}
},
{
id: M3_IO_CHAIN.targetBoxId,
resourceId: M3_IO_CHAIN.targetResourceId,
gatewayId: M3_IO_CHAIN.targetGatewayId,
gatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
online: false,
observable: false,
sourceKind,
observedAt,
ports: {
[M3_IO_CHAIN.targetPort]: {
value: null,
direction: "input",
source: "未验证",
sourceKind,
observedAt
}
}
}
];
}
function defaultM3PatchPanel({ observedAt }) {
return {
serviceId: M3_IO_CHAIN.patchPanelServiceId,
observable: false,
connectionActive: false,
connectionVersion: null,
wiringConfigId: null,
lastSyncAt: observedAt,
lastError: "未验证",
sourceKind: "UNVERIFIED"
};
}
async function readM3StatusPort({ gateway, command, requestJson, timeoutMs, observedAt, port, direction }) {
const capability = capabilityFor({ ...command, action: "di.read", port }, {
boxId: command.boxId,
resourceId: command.resourceId
});
const dispatch = await invokeGateway(gateway.url, {
operation: "hardware.port.read",
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId: gateway.gatewaySessionId,
resourceId: command.resourceId,
boxId: command.boxId,
capabilityId: capability.capabilityId,
port,
operationId: `op_m3_status_read_${port.toLowerCase()}_${randomUUID()}`,
traceId: `trc_m3_status_read_${port.toLowerCase()}_${randomUUID()}`,
actorType: "service",
actorId: `svc_${CLOUD_API_SERVICE_ID}`,
source: "cloud-api-m3-status"
}, { requestJson, timeoutMs });
if (!dispatch.ok || dispatch.body?.accepted !== true) {
return unverifiedPortRead({
port,
direction,
source: "gateway-simu",
reason: `gateway-simu ${port} 读取失败:${dispatch.error ?? dispatch.body?.error?.message ?? "unknown"}`,
observedAt
});
}
return {
port: {
value: readValueFromGatewayResult(dispatch.body),
direction,
source: port === M3_IO_CHAIN.targetPort ? "gateway-simu/patch-panel" : "gateway-simu",
sourceKind: "DEV-LIVE",
observedAt: dispatch.body?.result?.state?.updatedAt ?? dispatch.body?.state?.updatedAt ?? observedAt,
auditId: dispatch.body?.auditId ?? dispatch.body?.result?.auditId ?? null,
evidenceId: dispatch.body?.evidenceId ?? dispatch.body?.result?.evidenceId ?? null,
state: dispatch.body?.result?.state ?? dispatch.body?.state ?? null
},
error: null
};
}
function unverifiedPortRead({ port, direction, source, reason, observedAt }) {
return {
port: {
value: null,
direction,
source,
sourceKind: "UNVERIFIED",
observedAt,
lastError: reason
},
error: reason
};
}
function gatewayStatusEntry(gateway, { id, sessionId, role, observedAt }) {
return {
id: gateway.gatewayId ?? id,
role,
online: gateway.available === true,
observable: gateway.available === true,
sessionId: gateway.gatewaySessionId ?? sessionId,
lastSeenAt: gateway.status?.lastHeartbeatAt ?? gateway.status?.updatedAt ?? gateway.status?.session?.lastSeenAt ?? observedAt,
sourceKind: gateway.available === true ? "DEV-LIVE" : "UNVERIFIED",
lastError: gateway.available === true ? null : gateway.reason,
blocker: gateway.available === true ? null : gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable
};
}
function boxStatusEntry({ gateway, box, boxId, resourceId, gatewayId, gatewaySessionId, observedAt, ports }) {
return {
id: box?.boxId ?? boxId,
resourceId: box?.resourceId ?? resourceId,
gatewayId: gateway.gatewayId ?? gatewayId,
gatewaySessionId: gateway.gatewaySessionId ?? gatewaySessionId,
online: gateway.available === true && Boolean(box),
observable: gateway.available === true && Boolean(box),
sourceKind: gateway.available === true && box ? "DEV-LIVE" : "UNVERIFIED",
observedAt,
ports
};
}
function patchPanelStatusEntry({ statusResponse, wiringResponse, observedAt }) {
const status = statusResponse.body ?? {};
const wiring = wiringResponse.body ?? {};
const connectionActive = statusResponse.ok && wiringResponse.ok && hasExpectedPatchPanelWiring(status) && hasExpectedPatchPanelWiring(wiring);
const lastError = !statusResponse.ok
? `hwlab-patch-panel status 不可读:${statusResponse.error ?? `HTTP ${statusResponse.status}`}`
: !wiringResponse.ok
? `hwlab-patch-panel wiring 不可读:${wiringResponse.error ?? `HTTP ${wiringResponse.status}`}`
: connectionActive
? status.metadata?.evidenceSummary?.diagnostics?.errorCount > 0
? "hwlab-patch-panel diagnostics reports errors"
: null
: `未确认 active ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`;
return {
serviceId: status.serviceId ?? M3_IO_CHAIN.patchPanelServiceId,
observable: statusResponse.ok,
connectionActive,
connectionVersion: status.metadata?.evidenceSummary?.tickCount ?? status.metadata?.evidenceSummary?.routeCount ?? null,
wiringConfigId: status.wiringConfigId ?? wiring.wiringConfigId ?? null,
lastSyncAt: status.metadata?.evidenceSummary?.lastSyncAt ?? status.observedAt ?? wiring.updatedAt ?? observedAt,
lastError,
sourceKind: connectionActive ? "DEV-LIVE" : statusResponse.ok ? "UNVERIFIED" : "BLOCKED",
activeConnections: status.activeConnections ?? [],
diagnostics: status.metadata?.diagnostics ?? status.metadata?.evidenceSummary?.diagnostics ?? null
};
}
function readinessCheckFromGateway(id, gateway) {
return {
id,
status: gateway.available ? "pass" : "blocked",
layer: gateway.role,
sourceKind: gateway.available ? "DEV-LIVE" : "BLOCKED",
summary: gateway.available
? `${gateway.role} gateway ${gateway.gatewayId}/${gateway.gatewaySessionId} 只读状态可用。`
: gateway.reason,
gatewayId: gateway.gatewayId ?? null,
gatewaySessionId: gateway.gatewaySessionId ?? null,
url: gateway.url ? redactUrl(gateway.url) : null,
blocker: gateway.available ? null : gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable
};
}
function readinessCheckFromBox(id, box, command) {
return {
id,
status: box ? "pass" : "blocked",
sourceKind: box ? "DEV-LIVE" : "BLOCKED",
summary: box
? `${command.resourceId}/${command.port} 已在 gateway registry 中可见。`
: `${command.resourceId}/${command.port} 未在 gateway registry 中可见。`,
resourceId: box?.resourceId ?? command.resourceId,
boxId: box?.boxId ?? command.boxId,
port: command.port,
blocker: box ? null : M3_IO_BLOCKER_CODES.boxUnavailable
};
}
function readinessCheckFromPatchPanelStatus(response) {
const expected = response.ok && hasExpectedPatchPanelWiring(response.body);
return {
id: "patch-panel-status",
status: expected ? "pass" : "blocked",
sourceKind: expected ? "DEV-LIVE" : "BLOCKED",
summary: expected
? "hwlab-patch-panel status 包含目标 active 接线。"
: response.ok
? "hwlab-patch-panel status 未包含目标 active 接线。"
: `hwlab-patch-panel status 不可读:${response.error ?? `HTTP ${response.status}`}`,
serviceId: response.body?.serviceId ?? null,
state: response.body?.state ?? null,
blocker: expected ? null : M3_IO_BLOCKER_CODES.wiringMissing
};
}
function readinessCheckFromPatchPanelWiring(response) {
const expected = response.ok && hasExpectedPatchPanelWiring(response.body);
return {
id: "patch-panel-wiring",
status: expected ? "pass" : "blocked",
sourceKind: expected ? "DEV-LIVE" : "BLOCKED",
summary: expected
? "hwlab-patch-panel /wiring 包含目标 active 接线。"
: response.ok
? "hwlab-patch-panel /wiring 未包含目标 active 接线。"
: `hwlab-patch-panel /wiring 不可读:${response.error ?? `HTTP ${response.status}`}`,
wiringConfigId: response.body?.wiringConfigId ?? null,
wiringStatus: response.body?.status ?? null,
blocker: expected ? null : M3_IO_BLOCKER_CODES.wiringMissing
};
}
async function writeDo({
config,
command,
capability,
gateway,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
requestJson,
now
}) {
const dispatch = await invokeGateway(gateway.url, {
operation: "hardware.port.write",
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId: gateway.gatewaySessionId,
resourceId: command.resourceId,
boxId: command.boxId,
capabilityId: capability.capabilityId,
port: command.port,
value: command.value,
operationId: operation.operationId,
traceId: meta.traceId,
requestId: meta.requestId,
actorType: "service",
actorId: `svc_${CLOUD_API_SERVICE_ID}`,
requestedBy: meta.actorId,
source: "cloud-api-m3-io-control"
}, { requestJson, timeoutMs: config.timeoutMs });
if (!dispatch.ok || dispatch.body?.accepted !== true) {
return blockedControlResult({
action: command.action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
code: M3_IO_BLOCKER_CODES.dispatchFailed,
reason: `gateway-simu DO 写入失败:${dispatch.error ?? dispatch.body?.error?.message ?? "unknown"}`,
gateway,
dispatch,
now
});
}
const patchPanel = await tickPatchPanel(config.patchPanelUrl, {
signals: [
{
fromResourceId: M3_IO_CHAIN.sourceResourceId,
fromPort: M3_IO_CHAIN.sourcePort,
value: command.value,
operationId: operation.operationId,
traceId: meta.traceId
}
]
}, { requestJson, timeoutMs: config.timeoutMs });
const patchIssue = classifyPatchPanelResult(patchPanel);
if (patchIssue) {
return blockedControlResult({
action: command.action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
code: patchIssue.code,
reason: patchIssue.reason,
gateway,
dispatch,
patchPanel,
now
});
}
const targetReadback = await readTargetDiAfterWrite({
config,
operation,
meta,
requestJson
});
if (targetReadback.status !== "succeeded" || targetReadback.value !== command.value) {
return blockedControlResult({
action: command.action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
code: targetReadback.code ?? M3_IO_BLOCKER_CODES.wiringMissing,
reason: targetReadback.reason ?? `DI1 回读值 ${String(targetReadback.value)} 与 DO1 写入值 ${String(command.value)} 不一致;不能证明 M3 DO->DI 闭环。`,
gateway,
dispatch,
patchPanel,
targetReadback,
now
});
}
const tracePayload = {
action: command.action,
command,
gateway: gatewayResultSummary(gateway),
dispatch: dispatch.body,
patchPanel: patchPanel.body,
targetReadback
};
return successControlResult({
action: command.action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
gateway,
dispatch,
patchPanel,
targetReadback,
tracePayload,
now
});
}
async function readDi({
config,
command,
capability,
gateway,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
requestJson,
now
}) {
const dispatch = await invokeGateway(gateway.url, {
operation: "hardware.port.read",
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId: gateway.gatewaySessionId,
resourceId: command.resourceId,
boxId: command.boxId,
capabilityId: capability.capabilityId,
port: command.port,
operationId: operation.operationId,
traceId: meta.traceId,
requestId: meta.requestId,
actorType: "service",
actorId: `svc_${CLOUD_API_SERVICE_ID}`,
requestedBy: meta.actorId,
source: "cloud-api-m3-io-control"
}, { requestJson, timeoutMs: config.timeoutMs });
if (!dispatch.ok || dispatch.body?.accepted !== true) {
return blockedControlResult({
action: command.action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
code: M3_IO_BLOCKER_CODES.dispatchFailed,
reason: `gateway-simu DI 读取失败:${dispatch.error ?? dispatch.body?.error?.message ?? "unknown"}`,
gateway,
dispatch,
now
});
}
const tracePayload = {
action: command.action,
command,
gateway: gatewayResultSummary(gateway),
dispatch: dispatch.body,
value: readValueFromGatewayResult(dispatch.body)
};
return successControlResult({
action: command.action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
gateway,
dispatch,
tracePayload,
now
});
}
async function readTargetDiAfterWrite({ config, operation, meta, requestJson }) {
const gateway = await readGatewaySession(config.gateway2Url, {
gatewayRole: "target-readback",
expectedGatewayId: M3_IO_CHAIN.targetGatewayId,
expectedGatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
requestJson,
timeoutMs: config.timeoutMs
});
if (!gateway.available) {
return {
status: "blocked",
code: gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
reason: gateway.reason
};
}
const command = normalizeCommand("di.read", {});
const box = findGatewayBox(gateway.status, command);
if (!box) {
return {
status: "blocked",
code: M3_IO_BLOCKER_CODES.boxUnavailable,
reason: `target gateway 已响应,但 ${M3_IO_CHAIN.targetResourceId} 未注册/不可用`
};
}
const capability = capabilityFor(command, box);
const dispatch = await invokeGateway(gateway.url, {
operation: "hardware.port.read",
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId: gateway.gatewaySessionId,
resourceId: command.resourceId,
boxId: command.boxId,
capabilityId: capability.capabilityId,
port: command.port,
operationId: operation.operationId,
traceId: meta.traceId,
requestId: meta.requestId,
actorType: "service",
actorId: `svc_${CLOUD_API_SERVICE_ID}`,
requestedBy: meta.actorId,
source: "cloud-api-m3-io-control"
}, { requestJson, timeoutMs: config.timeoutMs });
return dispatch.ok && dispatch.body?.accepted === true
? {
status: "succeeded",
value: readValueFromGatewayResult(dispatch.body),
gatewaySessionId: gateway.gatewaySessionId,
resourceId: command.resourceId,
port: command.port,
result: dispatch.body
}
: {
status: "blocked",
code: M3_IO_BLOCKER_CODES.dispatchFailed,
reason: dispatch.error ?? dispatch.body?.error?.message ?? "target DI readback failed"
};
}
function normalizeAction(action) {
return String(action ?? "").trim().toLowerCase();
}
function normalizeCommand(action, params = {}) {
if (action === "do.write") {
return {
action,
gatewayId: params.gatewayId ?? params.sourceGatewayId ?? M3_IO_CHAIN.sourceGatewayId,
gatewaySessionId: params.gatewaySessionId ?? M3_IO_CHAIN.sourceGatewaySessionId,
resourceId: params.resourceId ?? M3_IO_CHAIN.sourceResourceId,
boxId: params.boxId ?? M3_IO_CHAIN.sourceBoxId,
port: normalizePort(params.port ?? M3_IO_CHAIN.sourcePort),
value: normalizeBoolean(params.value)
};
}
return {
action,
gatewayId: params.gatewayId ?? params.targetGatewayId ?? M3_IO_CHAIN.targetGatewayId,
gatewaySessionId: params.gatewaySessionId ?? M3_IO_CHAIN.targetGatewaySessionId,
resourceId: params.resourceId ?? M3_IO_CHAIN.targetResourceId,
boxId: params.boxId ?? M3_IO_CHAIN.targetBoxId,
port: normalizePort(params.port ?? M3_IO_CHAIN.targetPort)
};
}
function validateM3Command(command) {
if (command.action === "do.write") {
if (command.gatewayId !== M3_IO_CHAIN.sourceGatewayId || command.resourceId !== M3_IO_CHAIN.sourceResourceId || command.port !== M3_IO_CHAIN.sourcePort) {
return `M3 只允许 ${M3_IO_CHAIN.sourceGatewayId}/${M3_IO_CHAIN.sourceResourceId}/${M3_IO_CHAIN.sourcePort} 执行 DO write;当前请求会造成端口方向或资源越界。`;
}
if (typeof command.value !== "boolean") {
return "M3 DO1 写入值必须是 boolean。";
}
return null;
}
if (command.action === "di.read") {
if (command.gatewayId !== M3_IO_CHAIN.targetGatewayId || command.resourceId !== M3_IO_CHAIN.targetResourceId || command.port !== M3_IO_CHAIN.targetPort) {
return `M3 只允许 ${M3_IO_CHAIN.targetGatewayId}/${M3_IO_CHAIN.targetResourceId}/${M3_IO_CHAIN.targetPort} 执行 DI read;当前请求会造成端口方向或资源越界。`;
}
}
return null;
}
function normalizePort(port) {
return String(port ?? "").trim().toUpperCase();
}
function normalizeBoolean(value) {
if (typeof value === "boolean") return value;
if (value === "true") return true;
if (value === "false") return false;
return value;
}
function buildRequestMeta(params = {}, context = {}) {
return {
traceId: params.traceId || context.traceId || `trc_${randomUUID()}`,
requestId: params.requestId || context.requestId || `req_${randomUUID()}`,
actorType: "user",
actorId: params.actorId || context.actorId || "usr_hwlab_cloud_web",
serviceId: CLOUD_API_SERVICE_ID,
environment: ENVIRONMENT_DEV
};
}
function createOperationRecord({ action, command, capability, gatewaySessionId, meta, now }) {
const operationId = command.operationId || `op_m3_${action.replace(".", "_")}_${randomUUID()}`;
const operation = {
operationId,
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId,
resourceId: command.resourceId,
capabilityId: capability.capabilityId,
requestedBy: meta.actorId,
input: {
action,
port: command.port,
...(Object.hasOwn(command, "value") ? { value: command.value } : {}),
route: {
from: `${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort}`,
via: M3_IO_CHAIN.patchPanelServiceId,
to: `${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`
},
frontendBypass: false
},
status: "accepted",
environment: ENVIRONMENT_DEV,
requestedAt: now,
updatedAt: now
};
assertProtocolRecord("hardwareOperation", operation);
return operation;
}
function capabilityFor(command, box = {}) {
const suffix = command.action === "do.write" ? "write" : "read";
const portPart = command.port.toLowerCase();
const boxId = box.boxId ?? command.boxId;
return {
capabilityId: `cap_${boxId}_${portPart}_${suffix}`,
resourceId: command.resourceId,
projectId: M3_IO_CHAIN.projectId,
name: command.action,
description: `M3 ${command.action} ${command.resourceId}:${command.port}`,
direction: command.action === "do.write" ? "output" : "input",
valueType: "boolean",
constraints: {
port: command.port,
portFamily: command.action === "do.write" ? "DO" : "DI",
patchPanelOnlyPropagation: true,
frontendBypassAllowed: false
},
mutatesState: command.action === "do.write",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
}
async function readGatewaySession(url, { gatewayRole, expectedGatewayId, expectedGatewaySessionId, requestJson, timeoutMs }) {
if (!url) {
return {
available: false,
role: gatewayRole,
code: M3_IO_BLOCKER_CODES.gatewayUnavailable,
reason: "gateway 未注册/不可用:cloud-api 未配置 gateway-simu endpoint"
};
}
const response = await requestRuntimeJson(new URL("/status", ensureTrailingSlash(url)).toString(), {
method: "GET",
requestJson,
timeoutMs
});
if (!response.ok) {
return {
available: false,
role: gatewayRole,
url: redactUrl(url),
code: M3_IO_BLOCKER_CODES.gatewayUnavailable,
reason: `gateway 未注册/不可用:${response.error ?? `HTTP ${response.status}`}`
};
}
const status = response.body ?? {};
const gatewaySessionId = status.session?.gatewaySessionId ?? status.gatewaySessionId ?? status.registry?.gatewaySessionId;
const gatewayId = status.gatewayId ?? status.session?.gatewayId ?? status.registry?.gatewayId;
if (!gatewaySessionId || !gatewayId) {
return {
available: false,
role: gatewayRole,
url: redactUrl(url),
status,
code: M3_IO_BLOCKER_CODES.gatewayUnavailable,
reason: "gateway 未注册/不可用:/status 未返回 gatewaySessionId/gatewayId"
};
}
if (expectedGatewayId && gatewayId !== expectedGatewayId) {
return {
available: false,
role: gatewayRole,
url: redactUrl(url),
status,
code: M3_IO_BLOCKER_CODES.gatewayIdentityMismatch,
reason: `gateway-simu 身份不匹配:期望 gatewayId=${expectedGatewayId},实际=${gatewayId};控制面板保持阻塞。`
};
}
if (expectedGatewaySessionId && gatewaySessionId !== expectedGatewaySessionId) {
return {
available: false,
role: gatewayRole,
url: redactUrl(url),
status,
code: M3_IO_BLOCKER_CODES.gatewayIdentityMismatch,
reason: `gateway-simu 会话不匹配:期望 gatewaySessionId=${expectedGatewaySessionId},实际=${gatewaySessionId};控制面板保持阻塞。`
};
}
return {
available: true,
role: gatewayRole,
url,
gatewayId,
gatewaySessionId,
status
};
}
function findGatewayBox(gatewayStatus, command) {
const boxes = [
...(Array.isArray(gatewayStatus?.registry?.boxes) ? gatewayStatus.registry.boxes : []),
...(Array.isArray(gatewayStatus?.boxes)
? gatewayStatus.boxes.map((item) => typeof item === "string" ? { resourceId: item } : item)
: [])
];
return boxes.find((box) => box?.resourceId === command.resourceId || box?.boxId === command.boxId) ?? null;
}
async function invokeGateway(gatewayUrl, body, options) {
return requestRuntimeJson(new URL("/invoke", ensureTrailingSlash(gatewayUrl)).toString(), {
method: "POST",
body,
requestJson: options.requestJson,
timeoutMs: options.timeoutMs
});
}
async function tickPatchPanel(patchPanelUrl, body, options) {
if (!patchPanelUrl) {
return {
ok: false,
status: 0,
error: "patch-panel endpoint is not configured"
};
}
return requestRuntimeJson(new URL("/sync/tick", ensureTrailingSlash(patchPanelUrl)).toString(), {
method: "POST",
body,
requestJson: options.requestJson,
timeoutMs: options.timeoutMs
});
}
function classifyPatchPanelResult(patchPanel) {
if (!patchPanel.ok) {
return {
code: M3_IO_BLOCKER_CODES.patchPanelUnavailable,
reason: `hwlab-patch-panel 不可用:${patchPanel.error ?? `HTTP ${patchPanel.status}`}`
};
}
if (patchPanel.body?.accepted !== true) {
const diagnostic = firstDiagnostic(patchPanel.body);
return {
code: diagnostic?.code === "target_endpoint_missing" ? M3_IO_BLOCKER_CODES.boxUnavailable : M3_IO_BLOCKER_CODES.patchPanelUnavailable,
reason: diagnostic?.message ?? "hwlab-patch-panel 拒绝同步;请检查 wiring 和 endpointMap。"
};
}
if (!hasExpectedPatchDelivery(patchPanel.body)) {
return {
code: M3_IO_BLOCKER_CODES.wiringMissing,
reason: `hwlab-patch-panel 未路由 ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}wiring 不存在或不是 active。`
};
}
return null;
}
function hasExpectedPatchDelivery(body) {
const routes = Array.isArray(body?.routes) ? body.routes : [];
return routes.some((route) =>
(route.deliveries ?? []).some((delivery) =>
delivery.sourceResourceId === M3_IO_CHAIN.sourceResourceId &&
delivery.sourcePort === M3_IO_CHAIN.sourcePort &&
delivery.resourceId === M3_IO_CHAIN.targetResourceId &&
delivery.port === M3_IO_CHAIN.targetPort &&
delivery.deliveryStatus !== "failed"
)
);
}
function hasExpectedPatchPanelWiring(body) {
const connections = [
...(Array.isArray(body?.activeConnections) ? body.activeConnections : []),
...(Array.isArray(body?.connections) ? body.connections : []),
...(Array.isArray(body?.wiringConfig?.connections) ? body.wiringConfig.connections : [])
];
const active = body?.state === undefined || !["faulted", "blocked", "disabled"].includes(String(body.state).toLowerCase());
const wiringActive = body?.status === undefined || String(body.status).toLowerCase() === "active";
return active && wiringActive && connections.some((connection) => {
const from = connection.from ?? connection;
const to = connection.to ?? connection;
return String(from.resourceId ?? connection.fromResourceId ?? "") === M3_IO_CHAIN.sourceResourceId &&
normalizePort(from.port ?? connection.fromPort) === M3_IO_CHAIN.sourcePort &&
String(to.resourceId ?? connection.toResourceId ?? "") === M3_IO_CHAIN.targetResourceId &&
normalizePort(to.port ?? connection.toPort) === M3_IO_CHAIN.targetPort;
});
}
function firstDiagnostic(body) {
return [
...(Array.isArray(body?.diagnostics) ? body.diagnostics : []),
...(Array.isArray(body?.routes) ? body.routes.flatMap((route) => route.diagnostics ?? []) : [])
][0] ?? null;
}
async function requestRuntimeJson(url, { method = "GET", body, requestJson, timeoutMs = DEFAULT_TIMEOUT_MS }) {
if (requestJson) {
try {
const result = await requestJson(url, { method, body, timeoutMs });
return normalizeRuntimeJsonResponse(result);
} catch (error) {
return {
ok: false,
status: 0,
error: error instanceof Error ? error.message : String(error)
};
}
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
method,
headers: body === undefined
? { accept: "application/json" }
: {
accept: "application/json",
"content-type": "application/json"
},
body: body === undefined ? undefined : JSON.stringify(body),
signal: controller.signal
});
const text = await response.text();
let parsed = null;
try {
parsed = text ? JSON.parse(text) : null;
} catch {
parsed = null;
}
return {
ok: response.ok,
status: response.status,
body: parsed,
error: response.ok ? null : parsed?.error?.message ?? parsed?.message ?? response.statusText
};
} catch (error) {
return {
ok: false,
status: 0,
error: error.name === "AbortError" ? `request timed out after ${timeoutMs}ms` : error.message
};
} finally {
clearTimeout(timer);
}
}
function normalizeRuntimeJsonResponse(result = {}) {
if (Object.hasOwn(result, "ok") || Object.hasOwn(result, "body")) {
return {
ok: result.ok ?? (Number(result.status ?? 200) >= 200 && Number(result.status ?? 200) < 300),
status: result.status ?? 200,
body: result.body ?? result.json ?? null,
error: result.error ?? result.body?.error?.message ?? null
};
}
if (Object.hasOwn(result, "status") || Object.hasOwn(result, "json")) {
const status = result.status ?? 200;
return {
ok: status >= 200 && status < 300,
status,
body: result.json ?? null,
error: result.error ?? null
};
}
return {
ok: true,
status: 200,
body: result
};
}
async function persistRuntimeRegistrations({ runtimeStore, gatewayStatus, box, capability, command, meta }) {
const gatewaySessionId = gatewayStatus.session?.gatewaySessionId ?? gatewayStatus.gatewaySessionId ?? gatewayStatus.registry?.gatewaySessionId ?? command.gatewaySessionId;
const gatewayId = gatewayStatus.gatewayId ?? gatewayStatus.session?.gatewayId ?? gatewayStatus.registry?.gatewayId ?? command.gatewayId;
const gatewaySession = {
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId,
gatewayId,
serviceId: "hwlab-gateway-simu",
endpoint: gatewayStatus.registry?.endpoint ?? gatewayStatus.session?.endpoint,
status: "connected",
labels: {
m3ControlSurface: "true"
}
};
const resource = {
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId,
resourceId: command.resourceId,
boxId: box.boxId ?? command.boxId,
resourceType: "simulator_endpoint",
state: "available",
metadata: {
serviceId: "hwlab-box-simu",
patchPanelOnlyPropagation: true,
frontendBypassAllowed: false
}
};
const writes = [];
writes.push(await tryRuntimeWrite("gateway.session.register", () => runtimeStore?.registerGatewaySession?.(gatewaySession, meta)));
writes.push(await tryRuntimeWrite("box.resource.register", () => runtimeStore?.registerBoxResource?.(resource, meta)));
writes.push(await tryRuntimeWrite("box.capability.report", () => runtimeStore?.reportBoxCapabilities?.({ capability }, meta)));
return writes;
}
async function tryRuntimeWrite(label, write) {
if (typeof write !== "function") {
return {
label,
written: false,
skipped: true,
reason: "runtimeStore method unavailable"
};
}
try {
const result = await write();
if (result === undefined) {
return {
label,
written: false,
skipped: true,
reason: "runtimeStore method unavailable"
};
}
return {
label,
written: true,
result
};
} catch (error) {
return {
label,
written: false,
reason: error instanceof Error ? error.message : String(error),
code: error?.code ?? null,
data: error?.data ?? null
};
}
}
async function successControlResult(input) {
const records = await writeOutcomeRecords({
...input,
outcome: "succeeded",
reason: null
});
const target = controlTarget(input.command, input.operation.gatewaySessionId);
return {
serviceId: CLOUD_API_SERVICE_ID,
contractVersion: M3_IO_CONTROL_CONTRACT_VERSION,
status: "succeeded",
accepted: true,
capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.ready,
controlReady: true,
action: input.action,
chain: M3_IO_CHAIN,
command: input.command,
target,
gatewayId: target.gatewayId,
gatewaySessionId: target.gatewaySessionId,
resourceId: target.resourceId,
boxId: target.boxId,
port: target.port,
value: target.value,
operationId: input.operation.operationId,
traceId: input.meta.traceId,
auditId: records.auditEvent.auditId,
evidenceId: records.evidenceRecord.evidenceId,
operationState: {
status: "succeeded",
reachable: true
},
auditState: auditState(records.auditWrite, records.runtimeAfter),
operation: {
...input.operation,
status: "succeeded",
completedAt: input.now,
updatedAt: input.now
},
result: {
value: input.command.action === "di.read"
? readValueFromGatewayResult(input.dispatch.body)
: input.command.value,
dispatch: gatewayResultSummary(input.gateway),
patchPanel: input.patchPanel?.body ?? null,
targetReadback: input.targetReadback ?? null
},
controlPath: {
status: "succeeded",
cloudApi: true,
gatewaySimu: true,
boxSimu: true,
patchPanel: input.action === "do.write",
frontendBypass: false
},
auditEvent: records.auditEvent,
evidenceRecord: records.evidenceRecord,
evidenceState: evidenceState(records.runtimeAfter, records),
durableStatus: durableStatus(records.runtimeAfter),
trustBlocker: trustBlocker(records.runtimeAfter),
blockerClassification: redactedBlockerClassification({
code: records.runtimeAfter?.blocker,
reason: records.runtimeAfter?.reason,
runtime: records.runtimeAfter
}),
persistence: persistenceSummary(records.runtimeAfter, input.registration, input.operationWrite, records),
observedAt: input.now
};
}
async function blockedControlResult(input) {
const records = await writeOutcomeRecords({
...input,
outcome: "failed",
reason: input.reason
});
const target = controlTarget(input.command, input.operation.gatewaySessionId);
return {
serviceId: CLOUD_API_SERVICE_ID,
contractVersion: M3_IO_CONTROL_CONTRACT_VERSION,
status: "blocked",
accepted: false,
capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
controlReady: false,
action: input.action,
chain: M3_IO_CHAIN,
command: input.command,
target,
gatewayId: target.gatewayId,
gatewaySessionId: target.gatewaySessionId,
resourceId: target.resourceId,
boxId: target.boxId,
port: target.port,
value: target.value,
operationId: input.operation.operationId,
traceId: input.meta.traceId,
auditId: records.auditEvent.auditId,
evidenceId: records.evidenceRecord.evidenceId,
blocker: {
code: input.code,
message: input.reason,
zh: input.reason
},
blockerClassification: redactedBlockerClassification({
code: input.code,
reason: input.reason,
runtime: records.runtimeAfter
}),
operationState: {
status: "blocked",
reachable: false,
blocker: input.code
},
auditState: auditState(records.auditWrite, records.runtimeAfter),
operation: {
...input.operation,
status: "failed",
completedAt: input.now,
updatedAt: input.now,
error: {
code: input.code,
message: input.reason
}
},
result: {
dispatch: input.gateway ? gatewayResultSummary(input.gateway) : null,
patchPanel: input.patchPanel?.body ?? null,
targetReadback: input.targetReadback ?? null
},
controlPath: {
status: "blocked",
cloudApi: true,
gatewaySimu: Boolean(input.gateway),
boxSimu: input.code !== M3_IO_BLOCKER_CODES.gatewayUnavailable,
patchPanel: Boolean(input.patchPanel?.ok),
frontendBypass: false
},
auditEvent: records.auditEvent,
evidenceRecord: records.evidenceRecord,
evidenceState: evidenceState(records.runtimeAfter, records),
durableStatus: durableStatus(records.runtimeAfter),
trustBlocker: trustBlocker(records.runtimeAfter),
persistence: persistenceSummary(records.runtimeAfter, input.registration, input.operationWrite, records),
observedAt: input.now
};
}
function blockedResult({ action, command = null, meta, runtime, code, reason, httpStatus, now, details = {} }) {
const target = controlTarget(command, command?.gatewaySessionId);
return {
serviceId: CLOUD_API_SERVICE_ID,
contractVersion: M3_IO_CONTROL_CONTRACT_VERSION,
status: "blocked",
accepted: false,
capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
controlReady: false,
action: action || "unknown",
chain: M3_IO_CHAIN,
command,
target,
gatewayId: target.gatewayId,
gatewaySessionId: target.gatewaySessionId,
resourceId: target.resourceId,
boxId: target.boxId,
port: target.port,
value: target.value,
operationId: null,
traceId: meta.traceId,
auditId: null,
evidenceId: null,
blocker: {
code,
message: reason,
zh: reason
},
blockerClassification: redactedBlockerClassification({
code,
reason,
runtime
}),
operationState: {
status: "blocked",
reachable: false,
blocker: code
},
auditState: {
status: "not_written",
durableStatus: durableStatus(runtime),
reason: "operation was blocked before audit write"
},
controlPath: {
status: "blocked",
cloudApi: true,
gatewaySimu: false,
boxSimu: false,
patchPanel: false,
frontendBypass: false
},
evidenceState: evidenceState(runtime, {
auditWrite: { written: false, reason: "operation was blocked before audit write" },
evidenceWrite: { written: false, reason: "operation was blocked before evidence write" }
}),
durableStatus: durableStatus(runtime),
trustBlocker: trustBlocker(runtime),
persistence: {
runtime,
writes: []
},
observedAt: now,
httpStatus,
...details
};
}
async function writeOutcomeRecords({
action,
command,
operation,
meta,
runtimeStore,
runtimeBefore,
registration,
operationWrite,
tracePayload,
outcome,
reason,
now
}) {
const auditEvent = createProtocolAuditEvent({
auditId: `aud_${operation.operationId.slice(3)}_${outcome}`,
traceId: meta.traceId,
actorType: "user",
actorId: meta.actorId,
action: action === "do.write" ? "m3.io.do.write" : "m3.io.di.read",
targetType: "hardware_operation",
targetId: operation.operationId,
projectId: M3_IO_CHAIN.projectId,
gatewaySessionId: operation.gatewaySessionId,
operationId: operation.operationId,
serviceId: CLOUD_API_SERVICE_ID,
outcome,
reason: reason ?? undefined,
metadata: {
route: `${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort}->${M3_IO_CHAIN.patchPanelServiceId}->${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`,
command,
frontendBypass: false,
runtimeDurableBefore: runtimeBefore?.status ?? "unknown"
},
occurredAt: now
});
const evidenceRecord = createEvidenceRecord({
evidenceId: `evd_${operation.operationId.slice(3)}_${outcome}`,
projectId: M3_IO_CHAIN.projectId,
operationId: operation.operationId,
traceId: meta.traceId,
createdAt: now,
payload: {
outcome,
reason,
tracePayload,
command,
frontendBypass: false,
patchPanelOnlyPropagation: true
}
});
const auditWrite = await tryRuntimeWrite("audit.event.write", () => runtimeStore?.writeAuditEvent?.({ event: auditEvent }, meta));
const evidenceWrite = await tryRuntimeWrite("evidence.record.write", () => runtimeStore?.writeEvidenceRecord?.({ record: evidenceRecord }, meta));
const runtimeAfter = await safeRuntimeSummary(runtimeStore);
return {
auditEvent,
evidenceRecord,
auditWrite,
evidenceWrite,
runtimeAfter,
registration,
operationWrite
};
}
function createEvidenceRecord({ evidenceId, projectId, operationId, traceId, payload, createdAt }) {
const metadata = {
traceId,
serviceId: CLOUD_API_SERVICE_ID,
route: `${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort}->${M3_IO_CHAIN.patchPanelServiceId}->${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`,
frontendBypass: false,
patchPanelOnlyPropagation: true
};
const serialized = JSON.stringify({
serviceId: CLOUD_API_SERVICE_ID,
projectId,
operationId,
traceId,
payload,
metadata,
createdAt
});
const record = {
evidenceId,
projectId,
operationId,
kind: "trace",
uri: `memory://hwlab-cloud-api/${operationId}/m3-io-control.json`,
mimeType: "application/json",
sha256: createHash("sha256").update(serialized).digest("hex"),
sizeBytes: Buffer.byteLength(serialized),
serviceId: CLOUD_API_SERVICE_ID,
environment: ENVIRONMENT_DEV,
metadata,
createdAt
};
assertProtocolRecord("evidenceRecord", record);
return record;
}
function evidenceState(runtime, records) {
const durableReady = isDurableRuntimeReady(runtime);
const recordsWritten = records.auditWrite?.written === true && records.evidenceWrite?.written === true;
const evidenceWriteState = recordWriteState(records.evidenceWrite, runtime);
if (durableReady && recordsWritten) {
return {
status: "green",
sourceKind: "DEV-LIVE",
durable: true,
writeStatus: evidenceWriteState.status,
durableStatus: durableStatus(runtime),
reason: "operation/audit/evidence 已通过 durable runtime 写入;仍需 DEV 浏览器 E2E 才能宣称验收通过。"
};
}
const blocker = runtime?.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked;
return {
status: "blocked",
sourceKind: "BLOCKED",
durable: runtime?.durable === true,
blocker,
writeStatus: evidenceWriteState.status,
durableStatus: durableStatus(runtime),
reason: `操作链路可达性与可信持久化分离:runtime durable 未 greenstatus=${runtime?.status ?? "unknown"}blocker=${blocker}),不能宣称可信闭环完全 green。`
};
}
function auditState(auditWrite, runtime) {
return {
...recordWriteState(auditWrite, runtime),
durableStatus: durableStatus(runtime)
};
}
function recordWriteState(write, runtime) {
if (write?.written === true && isDurableRuntimeReady(runtime)) {
return {
status: "persisted",
durable: true,
sourceKind: "DEV-LIVE"
};
}
if (write?.written === true) {
return {
status: "written_non_durable",
durable: false,
sourceKind: "BLOCKED",
blocker: runtime?.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked
};
}
return {
status: "not_written",
durable: false,
sourceKind: "BLOCKED",
blocker: runtime?.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked,
reason: write?.reason ?? "runtime write did not complete"
};
}
function durableStatus(runtime) {
const ready = isDurableRuntimeReady(runtime);
return {
status: runtime?.status ?? "unknown",
durable: runtime?.durable === true,
ready: runtime?.ready === true,
liveRuntimeEvidence: runtime?.liveRuntimeEvidence === true,
blocker: ready ? null : runtime?.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked,
redacted: true
};
}
function trustBlocker(runtime) {
if (isDurableRuntimeReady(runtime)) return null;
const durable = durableStatus(runtime);
return {
code: durable.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked,
layer: "runtime-durable",
zh: `runtime durable 未 green${durable.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked}`,
category: "runtime_durable",
durableStatus: durable
};
}
function redactedBlockerClassification({ code, reason, runtime } = {}) {
const blockerCode = code ?? runtime?.blocker ?? null;
if (!blockerCode) {
return null;
}
return {
code: blockerCode,
category: blockerCategory(blockerCode),
runtimeStatus: runtime?.status ?? "unknown",
redacted: true,
message: reason ? redactBlockerMessage(reason) : null
};
}
function blockerCategory(code) {
if (String(code).startsWith("runtime_durable")) return "runtime_durable";
if (String(code).includes("gateway")) return "gateway_session";
if (String(code).includes("patch_panel")) return "patch_panel";
if (String(code).includes("wiring")) return "patch_panel_wiring";
if (String(code).includes("box")) return "box_resource";
if (String(code).includes("port")) return "port_direction";
return "m3_io_control";
}
function redactBlockerMessage(message) {
return String(message)
.replace(/https?:\/\/[^\s,;]+/giu, "[redacted-url]")
.replace(/postgres(?:ql)?:\/\/[^\s,;]+/giu, "[redacted-db-url]");
}
function controlTarget(command, gatewaySessionId) {
return {
gatewayId: command?.gatewayId ?? null,
gatewaySessionId: gatewaySessionId ?? command?.gatewaySessionId ?? null,
resourceId: command?.resourceId ?? null,
boxId: command?.boxId ?? null,
port: command?.port ?? null,
value: Object.hasOwn(command ?? {}, "value") ? command.value : null
};
}
function persistenceSummary(runtime, registration, operationWrite, records) {
return {
runtime,
writes: [
...(registration ?? []),
operationWrite,
records.auditWrite,
records.evidenceWrite
].filter(Boolean)
};
}
async function safeRuntimeSummary(runtimeStore) {
try {
if (typeof runtimeStore?.readiness === "function") {
return await runtimeStore.readiness();
}
if (typeof runtimeStore?.summary === "function") {
return runtimeStore.summary();
}
} catch (error) {
return {
adapter: "unknown",
durable: false,
ready: false,
status: "blocked",
blocker: "runtime_summary_unavailable",
reason: error instanceof Error ? error.message : String(error),
liveRuntimeEvidence: false
};
}
return {
adapter: "none",
durable: false,
ready: false,
status: "blocked",
blocker: "runtime_store_missing",
reason: "runtime store is not available",
liveRuntimeEvidence: false
};
}
function isDurableRuntimeReady(runtime) {
if (!runtime || typeof runtime !== "object") return false;
if (runtime.durable !== true) return false;
if (runtime.ready === false) return false;
if (runtime.liveRuntimeEvidence !== true) return false;
return !["blocked", "degraded", "failed"].includes(String(runtime.status ?? "").toLowerCase());
}
function readValueFromGatewayResult(result) {
return result?.result?.value ?? result?.value ?? null;
}
function gatewayResultSummary(gateway) {
return {
gatewayId: gateway.gatewayId,
gatewaySessionId: gateway.gatewaySessionId,
url: redactUrl(gateway.url),
role: gateway.role
};
}
function publicGatewayStatus(status) {
return {
gatewayId: status?.gatewayId ?? status?.session?.gatewayId ?? null,
gatewaySessionId: status?.session?.gatewaySessionId ?? status?.gatewaySessionId ?? null,
boxCount: Array.isArray(status?.registry?.boxes) ? status.registry.boxes.length : null
};
}
function finalizeControlResult(result) {
return result;
}
function ensureTrailingSlash(url) {
return url.endsWith("/") ? url : `${url}/`;
}
function trimUrl(value) {
const text = String(value ?? "").trim();
return text ? text : null;
}
function parseTimeout(value) {
const parsed = Number.parseInt(value ?? "", 10);
return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS;
}
function redactUrl(url) {
try {
const parsed = new URL(url);
parsed.username = parsed.username ? "***" : "";
parsed.password = parsed.password ? "***" : "";
return parsed.toString();
} catch {
return "invalid-url";
}
}