// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. git-mirror module for scripts/src/hwlab-node-impl.ts. // Moved mechanically from scripts/src/hwlab-node-impl.ts:2800-3280 for #903. // SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0. // SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-25-p0-web-probe-sentinel. // Responsibility: YAML-first node/lane operations, including Workbench observability control commands. import { createHash, randomBytes } from "node:crypto"; import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { repoRoot, rootPath, type Config } from "../config"; import { runCommand, type CommandResult } from "../command"; import { startJob } from "../jobs"; import { classifySshTcpPoolFailure } from "../ssh"; import { HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH, hwlabNodeControlPlaneInfraHelp, runHwlabNodeControlPlaneInfra } from "../hwlab-node-control-plane"; import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes"; import { nodeWebProbeScriptRunnerSource } from "../hwlab-node-web-probe-runner-source"; import { nodeWebObserveAnalyzerSource } from "../hwlab-node-web-observe-analyzer-source"; import { nodeWebObserveRunnerSource } from "../hwlab-node-web-observe-runner-source"; import { nodeWebObserveCollectViewNodeScript, parseNodeWebProbeObserveCollectView, type NodeWebProbeObserveCollectView } from "../hwlab-node-web-observe-collect"; import { withWebObserveCollectRendered, withWebObserveCommandRendered, withWebObserveStatusRendered } from "../hwlab-node-web-observe-render"; import { buildWebObserveWrapperForObserveOptions, webObserveWrapperStateDirFromStatus } from "../hwlab-node-web-observe-wrapper"; import { renderWebObserveWrapperContract } from "../hwlab-node-web-observe-wrapper-render"; import { runWebProbeSentinelCommand, type WebProbeSentinelOptions } from "../hwlab-node-web-sentinel-cicd"; import { hwlabNodeHelp, hwlabNodeObservabilityHelp, hwlabNodeWebProbeHelp } from "../hwlab-node-help"; import { compactWebProbeResult, compactWebProbeScriptResult } from "../hwlab-node-web-probe-summary"; import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRuleSummaries, nodeObservabilityWarningAlertExpression, nodeObservabilityWarningAlertSummaries } from "../hwlab-node-observability-promql"; import { runDelegatedHwlabNodeCommand, type DelegatedNodeDomain } from "../hwlab-node-transport"; import type { RenderedCliResult } from "../output"; import { formatElapsedMs, isCommandSuccess, nodeRuntimePipelineRunName, nodeRuntimeRerunPipelineRunName, resolveNodeRuntimeLaneHead, shortValue } from "./cleanup"; import { nodeRuntimeApply } from "./control-actions"; import { NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS, NODE_RUNTIME_TRIGGER_SEVERE_WARNING_MS } from "./entry"; import { parseNodeScopedDelegatedOptions } from "./plan"; import { compactNodeRuntimeTaskRunDiagnostic, nodeRuntimePipelineFailureSummary } from "./render"; import { compactRuntimeCommand } from "./runtime-common"; import { compactNodeRuntimeGitMirrorObservation, compactNodeRuntimeGitMirrorRun, nodeRuntimeEnsureGitMirrorFlushed, nodeRuntimeEnsureGitMirrorSourceCurrent, nodeRuntimeExternalPostgresSecretRows, nodeRuntimeGitMirrorRun, nodeRuntimeGitMirrorStatus, nodeRuntimeOpportunisticGitMirrorFlush, nodeRuntimeOpportunisticGitMirrorSync, nodeScopedFullOutput } from "./status"; import { record } from "./utils"; import { webObserveTable } from "./web-observe-render"; import { createNodeRuntimePipelineRun, getNodeRuntimePipelineRun, nodeRuntimePipelineRunManifest, printNodeRuntimeTriggerProgress, waitForNodeRuntimePipelineRunTerminal } from "./web-probe"; import { webObserveShort, webObserveText } from "./web-probe-observe"; export function nodeRuntimeTriggerCurrentOutput(scoped: ReturnType): Record | RenderedCliResult { const result = nodeRuntimeTriggerCurrent(scoped); if (nodeScopedFullOutput(scoped)) return result; return withNodeRuntimeTriggerRendered(result, scoped); } export function nodeRuntimeTriggerCurrent(scoped: ReturnType): Record { const spec = scoped.spec; const pipelineWaitSeconds = nodeRuntimeCicdWaitSeconds(scoped); const triggerStartedAt = Date.now(); const triggerElapsedMs = () => Date.now() - triggerStartedAt; const sourceSnapshotSync = scoped.dryRun ? null : nodeRuntimeGitMirrorRun({ ...scoped, domain: "git-mirror", action: "sync", confirm: true, dryRun: false, wait: true, discardStaleGitops: scoped.discardStaleGitops === true || scoped.rerun === true, }); if (sourceSnapshotSync !== null && sourceSnapshotSync.ok !== true) { return { ok: false, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, phase: "source-snapshot-sync", degradedReason: "node-runtime-source-snapshot-sync-failed", sourceSnapshotSync: nodeScopedFullOutput(scoped) ? sourceSnapshotSync : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), }; } printNodeRuntimeTriggerProgress(spec, { stage: "source-head", status: "started" }); const head = resolveNodeRuntimeLaneHead(spec); const sourceCommit = head.sourceCommit; if (sourceCommit === null) { printNodeRuntimeTriggerProgress(spec, { stage: "source-head", status: "failed" }); return { ok: false, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, phase: "source-head", degradedReason: "node-runtime-source-head-unresolved", headProbe: compactRuntimeCommand(head.result), sourceSnapshotSync: sourceSnapshotSync === null ? null : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), }; } const basePipelineRun = nodeRuntimePipelineRunName(spec, sourceCommit); const pipelineRun = scoped.rerun ? nodeRuntimeRerunPipelineRunName(spec, sourceCommit) : basePipelineRun; printNodeRuntimeTriggerProgress(spec, { stage: "source-head", status: "succeeded", sourceCommit, pipelineRun }); printNodeRuntimeTriggerProgress(spec, { stage: "probe-existing-pipelinerun", status: "started", sourceCommit, pipelineRun }); const before = getNodeRuntimePipelineRun(spec, pipelineRun); printNodeRuntimeTriggerProgress(spec, { stage: "probe-existing-pipelinerun", status: "succeeded", sourceCommit, pipelineRun, existingStatus: before.status ?? null }); if (scoped.dryRun) { const gitMirror = nodeRuntimeGitMirrorStatus(scoped); return { ok: true, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, mode: "dry-run", sourceCommit, pipelineRun, rerunOf: scoped.rerun ? basePipelineRun : null, rerun: scoped.rerun, before, gitMirror: nodeScopedFullOutput(scoped) ? gitMirror : compactNodeRuntimeGitMirrorObservation(gitMirror), sourceSnapshotSync: sourceSnapshotSync === null ? null : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), manifest: nodeRuntimePipelineRunManifest(spec, sourceCommit, pipelineRun), next: { triggerCurrent: `bun scripts/cli.ts hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane} --confirm` }, }; } if (!scoped.rerun && before.exists === true && (before.status === "True" || before.status === "Unknown")) { const pipelineWait = before.status === "Unknown" ? waitForNodeRuntimePipelineRunTerminal(spec, pipelineRun, pipelineWaitSeconds, { opportunisticPostSync: () => nodeRuntimeOpportunisticGitMirrorSync(scoped, sourceCommit, pipelineRun), opportunisticPostFlush: () => nodeRuntimeOpportunisticGitMirrorFlush(scoped, sourceCommit, pipelineRun), }) : { ok: true, status: "already-succeeded", pipelineRun: before, polls: 0, elapsedMs: 0 }; const waitedPipelineRun = record(pipelineWait.pipelineRun); const pipelineFailureSummary = nodeRuntimePipelineFailureSummary(pipelineWait); const postFlush = waitedPipelineRun.status === "True" ? nodeRuntimeEnsureGitMirrorFlushed(scoped, "post", sourceCommit, pipelineRun) : null; const pipelinePending = pipelineWait.status === "pending"; const ok = pipelineWait.ok === true && (postFlush === null || postFlush.ok === true); const elapsedMs = triggerElapsedMs(); const triggerWarning = pipelinePending ? null : nodeRuntimeTriggerElapsedWarning(spec, pipelineRun, elapsedMs); if (triggerWarning !== null) printNodeRuntimeTriggerProgress(spec, { stage: "trigger-current", status: "warning", ...triggerWarning }); return { ok, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, mode: "confirmed-trigger", completion: pipelinePending ? "pending" : ok ? "completed" : "failed", warning: pipelinePending ? pipelineWait.warning ?? nodeRuntimeCicdWaitWarning(spec, pipelineRun, pipelineWait) : triggerWarning ?? undefined, triggerElapsedMs: elapsedMs, sourceCommit, pipelineRun, before, pipelineWait, pipelineFailureSummary, postFlush, sourceSnapshotSync: sourceSnapshotSync === null ? null : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), skipped: true, reason: before.status === "True" ? "existing-pipelinerun-succeeded" : "existing-pipelinerun-running", skipPolicy: "source-commit-only", skipExplanation: "An existing PipelineRun for this HWLAB source commit was reused. If UniDesk node/lane YAML or other render inputs changed without a HWLAB source commit change, rerun the controlled publish with --rerun.", rerunAvailable: true, rerunCommand: `bun scripts/cli.ts hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane} --confirm --rerun`, degradedReason: ok ? undefined : postFlush !== null && postFlush.ok !== true ? "node-runtime-git-mirror-post-flush-failed" : "node-runtime-existing-pipelinerun-not-succeeded", next: { status: `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --pipeline-run ${pipelineRun}`, rerun: `bun scripts/cli.ts hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane} --confirm --rerun`, }, }; } printNodeRuntimeTriggerProgress(spec, { stage: "git-mirror-pre-sync", status: "started", sourceCommit, pipelineRun }); const gitMirror = nodeRuntimeEnsureGitMirrorSourceCurrent(scoped, sourceCommit, pipelineRun); if (gitMirror.ok !== true) { printNodeRuntimeTriggerProgress(spec, { stage: "git-mirror-pre-sync", status: "failed", sourceCommit, pipelineRun, reason: gitMirror.degradedReason ?? null }); return { ok: false, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, phase: "git-mirror-pre-sync", sourceCommit, pipelineRun, before, gitMirror, sourceSnapshotSync: sourceSnapshotSync === null ? null : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), degradedReason: "node-runtime-git-mirror-pre-sync-failed", }; } printNodeRuntimeTriggerProgress(spec, { stage: "git-mirror-pre-sync", status: "succeeded", sourceCommit, pipelineRun }); printNodeRuntimeTriggerProgress(spec, { stage: "control-plane-refresh", status: "started", sourceCommit, pipelineRun }); const refresh = nodeRuntimeApply({ ...scoped, action: "apply", dryRun: false }); if (refresh.ok !== true) { const diagnostics = record(refresh.diagnostics); printNodeRuntimeTriggerProgress(spec, { stage: "control-plane-refresh", status: "failed", sourceCommit, pipelineRun, ...(diagnostics.classification === "ssh-tcp-pool-transient" ? { reason: "ssh-tcp-pool-transient", failureKind: diagnostics.failureKind ?? null } : {}), }); return { ok: false, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, phase: "control-plane-apply", sourceCommit, pipelineRun, refresh, sourceSnapshotSync: sourceSnapshotSync === null ? null : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), diagnostics: Object.keys(diagnostics).length > 0 ? diagnostics : null, degradedReason: "node-runtime-control-plane-apply-before-trigger-failed", next: { retryTriggerCurrent: `bun scripts/cli.ts hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane} --confirm`, ...(diagnostics.classification === "ssh-tcp-pool-transient" ? { sshPoolStatus: `bun scripts/cli.ts debug ssh-pool ${scoped.node}` } : {}), }, }; } printNodeRuntimeTriggerProgress(spec, { stage: "control-plane-refresh", status: "succeeded", sourceCommit, pipelineRun }); printNodeRuntimeTriggerProgress(spec, { stage: "create-pipelinerun", status: "started", sourceCommit, pipelineRun, rerun: scoped.rerun }); const create = createNodeRuntimePipelineRun(spec, sourceCommit, pipelineRun, scoped.timeoutSeconds, scoped.rerun); const after = getNodeRuntimePipelineRun(spec, pipelineRun); const createObserved = after.exists === true && (after.status === "Unknown" || after.status === "True"); const createOk = isCommandSuccess(create) || createObserved; printNodeRuntimeTriggerProgress(spec, { stage: "create-pipelinerun", status: createOk ? "succeeded" : "failed", sourceCommit, pipelineRun, exitCode: create.exitCode, createObservedAfterTimeout: createObserved && !isCommandSuccess(create) ? true : undefined }); const pipelineWait = createOk ? waitForNodeRuntimePipelineRunTerminal(spec, pipelineRun, pipelineWaitSeconds, { opportunisticPostSync: () => nodeRuntimeOpportunisticGitMirrorSync(scoped, sourceCommit, pipelineRun), opportunisticPostFlush: () => nodeRuntimeOpportunisticGitMirrorFlush(scoped, sourceCommit, pipelineRun), }) : null; const waitedPipelineRun = record(pipelineWait?.pipelineRun); const pipelineFailureSummary = nodeRuntimePipelineFailureSummary(pipelineWait); const postFlush = waitedPipelineRun.status === "True" ? nodeRuntimeEnsureGitMirrorFlushed(scoped, "post", sourceCommit, pipelineRun) : null; const pipelineReady = pipelineWait !== null && pipelineWait.ok === true; const postFlushOk = postFlush === null || postFlush.ok === true; const pipelinePending = pipelineWait !== null && pipelineWait.status === "pending"; const ok = createOk && (pipelineReady || pipelinePending) && postFlushOk; const elapsedMs = triggerElapsedMs(); const triggerWarning = pipelinePending ? null : nodeRuntimeTriggerElapsedWarning(spec, pipelineRun, elapsedMs); if (triggerWarning !== null) printNodeRuntimeTriggerProgress(spec, { stage: "trigger-current", status: "warning", ...triggerWarning }); return { ok, command: `hwlab nodes control-plane trigger-current --node ${scoped.node} --lane ${scoped.lane}`, node: scoped.node, lane: scoped.lane, mode: "confirmed-trigger", completion: pipelinePending ? "pending" : ok ? "completed" : "failed", warning: pipelinePending ? pipelineWait?.warning ?? nodeRuntimeCicdWaitWarning(spec, pipelineRun, pipelineWait) : triggerWarning ?? undefined, triggerElapsedMs: elapsedMs, mutation: createOk, sourceCommit, pipelineRun, sourceSnapshotSync: sourceSnapshotSync === null ? null : compactNodeRuntimeGitMirrorRun(sourceSnapshotSync), rerunOf: scoped.rerun ? basePipelineRun : null, rerun: scoped.rerun, before, gitMirror, refresh, create: compactRuntimeCommand(create), after, pipelineWait, pipelineFailureSummary, postFlush, createObservedAfterTimeout: createObserved && !isCommandSuccess(create) ? true : undefined, degradedReason: ok ? undefined : !createOk ? "node-runtime-pipelinerun-create-failed" : !pipelineReady ? "node-runtime-pipelinerun-not-succeeded" : "node-runtime-git-mirror-post-flush-failed", next: { status: `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --pipeline-run ${pipelineRun}` }, }; } export function nodeRuntimeCicdWaitSeconds(scoped: ReturnType): number { return Math.min(scoped.timeoutSeconds, NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS); } export function nodeRuntimeCicdWaitWarning(spec: HwlabRuntimeLaneSpec, pipelineRun: string, pipelineWait: unknown): Record { const wait = record(pipelineWait); return { code: "node-runtime-cicd-wait-over-120s", message: `PipelineRun ${pipelineRun} is still non-terminal after ${NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS}s; this is a severe timeout and requires further investigation of Tekton TaskRuns/Pods instead of treating it as normal wait time.`, waitedSeconds: NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS, elapsedMs: wait.elapsedMs ?? null, polls: wait.polls ?? null, inspectEnvReuse: `bun scripts/cli.ts hwlab nodes control-plane status --node ${spec.nodeId} --lane ${spec.lane} --pipeline-run ${pipelineRun} --full`, inspectGitMirror: `bun scripts/cli.ts hwlab nodes git-mirror status --node ${spec.nodeId} --lane ${spec.lane}`, }; } export function nodeRuntimeTriggerElapsedWarning(spec: HwlabRuntimeLaneSpec, pipelineRun: string, elapsedMs: number): Record | null { if (elapsedMs < NODE_RUNTIME_TRIGGER_SEVERE_WARNING_MS) return null; return { code: "node-runtime-trigger-over-120s", message: `trigger-current total elapsed time exceeded ${NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS}s; this is a severe timeout even if the PipelineRun eventually succeeded and requires follow-up investigation of slow control-plane, Tekton and git-mirror stages.`, thresholdMs: NODE_RUNTIME_TRIGGER_SEVERE_WARNING_MS, elapsedMs, inspectEnvReuse: `bun scripts/cli.ts hwlab nodes control-plane status --node ${spec.nodeId} --lane ${spec.lane} --pipeline-run ${pipelineRun} --full`, inspectGitMirror: `bun scripts/cli.ts hwlab nodes git-mirror status --node ${spec.nodeId} --lane ${spec.lane}`, }; } export function withNodeRuntimeTriggerRendered(result: Record, scoped: ReturnType): RenderedCliResult { const pipelineWait = record(result.pipelineWait); const pipelineRunRecord = record(pipelineWait.pipelineRun ?? result.after ?? result.before); const warning = record(result.warning ?? pipelineWait.warning); const postFlush = record(result.postFlush); const gitMirror = record(result.gitMirror); const gitMirrorSummary = record(postFlush.afterSummary ?? postFlush.beforeSummary ?? gitMirror.statusSummary ?? gitMirror.afterSummary ?? gitMirror.beforeSummary ?? gitMirror.summary); const refresh = record(result.refresh); const create = record(result.create); const next = record(result.next); const pipelineStatus = pipelineRunRecord.status ?? pipelineWait.status ?? "-"; const completion = result.completion ?? (result.ok === true ? "completed" : "failed"); const renderedText = [ "hwlab nodes control-plane trigger-current", "", webObserveTable( ["NODE", "LANE", "STATUS", "COMPLETION", "SOURCE", "PIPELINERUN"], [[scoped.node, scoped.lane, result.ok === true ? "ok" : "failed", webObserveText(completion), shortValue(result.sourceCommit), webObserveText(result.pipelineRun)]], ), "", webObserveTable( ["STAGE", "STATUS", "DETAIL"], [ ["git-mirror", gitMirror.ok === true ? "ok" : gitMirror.ok === false ? "failed" : "-", webObserveText(gitMirror.mode ?? gitMirror.degradedReason)], ["refresh", refresh.ok === true ? "ok" : refresh.ok === false ? "failed" : "-", webObserveText(refresh.degradedReason)], ["create", create.exitCode === 0 || result.mutation === true ? "ok" : create.exitCode === undefined ? "-" : "failed", result.createObservedAfterTimeout === true ? "observed-after-timeout" : `exit=${webObserveText(create.exitCode)}`], ["pipeline-wait", webObserveText(pipelineWait.status), `pipeline=${webObserveText(pipelineStatus)} polls=${webObserveText(pipelineWait.polls)} elapsed=${formatElapsedMs(pipelineWait.elapsedMs)}`], ["post-flush", postFlush.ok === true ? "ok" : postFlush.ok === false ? "failed" : "-", webObserveText(postFlush.mode ?? postFlush.degradedReason)], ["total", result.triggerElapsedMs === undefined ? "-" : "elapsed", formatElapsedMs(result.triggerElapsedMs)], ], ), "", Object.keys(warning).length === 0 ? "WARNING\n-" : webObserveTable( ["CODE", "MESSAGE"], [[webObserveText(warning.code ?? "warning"), webObserveText(warning.message ?? "PipelineRun is still active after the default wait window.")]], ), "", Object.keys(warning).length === 0 ? "INSPECT\n-" : webObserveTable( ["CHECK", "COMMAND"], [ ["env-reuse", webObserveText(warning.inspectEnvReuse ?? `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --pipeline-run ${result.pipelineRun} --full`)], ["git-mirror", webObserveText(warning.inspectGitMirror ?? `bun scripts/cli.ts hwlab nodes git-mirror status --node ${scoped.node} --lane ${scoped.lane}`)], ], ), "", Object.keys(gitMirrorSummary).length === 0 ? "GIT_MIRROR\n-" : webObserveTable( ["LOCAL_SOURCE", "GITHUB_SOURCE", "LOCAL_GITOPS", "GITHUB_GITOPS", "PENDING", "IN_SYNC"], [[ shortValue(gitMirrorSummary.localSource), shortValue(gitMirrorSummary.githubSource), shortValue(gitMirrorSummary.localGitops), shortValue(gitMirrorSummary.githubGitops), webObserveText(gitMirrorSummary.pendingFlush), webObserveText(gitMirrorSummary.githubInSync), ]], ), "", "NEXT", ` ${next.status ?? `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --pipeline-run ${result.pipelineRun}`}`, "", "Disclosure:", " default view is a bounded CICD summary; use --full or --raw for the complete JSON payload.", ` ${NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS}s is the severe timeout warning threshold for both PipelineRun wait and total trigger elapsed time.`, ].join("\n"); return { ...result, renderedText, contentType: "text/plain" }; } export function withNodeRuntimeControlPlanePlanRendered(result: Record, scoped: ReturnType): RenderedCliResult { const checks = record(result.checks); const next = record(result.next); const renderedText = [ "hwlab nodes control-plane plan", "", webObserveTable( ["NODE", "LANE", "STATUS", "MODE", "NAMESPACE", "EXT_PG", "PUBLIC"], [[ scoped.node, scoped.lane, result.ok === true ? "ok" : "failed", webObserveText(result.mode), webObserveText(checks.runtimeNamespace), webObserveText(checks.externalPostgresActive), webObserveText(checks.publicExposureDeclared), ]], ), "", webObserveTable( ["CHECK", "VALUE"], [ ["node-scoped-target", webObserveText(checks.nodeScopedTargetConfigured)], ["external-postgres-declared", webObserveText(checks.externalPostgresDeclared)], ["external-postgres-active", webObserveText(checks.externalPostgresActive)], ["local-postgres-absent", webObserveText(checks.localPostgresExpectedAbsent)], ["secret-values-printed", webObserveText(checks.secretValuesPrinted)], ], ), "", "NEXT", ` ${next.status ?? `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane}`}`, "", "Disclosure:", " default view is a bounded control-plane plan summary; use --full or --raw for the complete JSON payload.", ].join("\n"); return { ...result, renderedText, contentType: "text/plain" }; } export function withNodeRuntimeControlPlaneStatusRendered(result: Record, scoped: ReturnType): RenderedCliResult { const pipelineRun = record(result.pipelineRun); const pipelineRunDiagnostics = record(pipelineRun.diagnostics); const argo = record(result.argo); const runtime = record(result.runtime); const publicProbe = record(result.publicProbe); const gitMirror = record(result.gitMirror); const next = record(result.next); const diagnostic = record(publicProbe.diagnostic); const notReadyWorkloads = Array.isArray(runtime.notReadyWorkloads) ? runtime.notReadyWorkloads.map((item) => webObserveText(item)).filter(Boolean) : []; const runtimeDetail = [ `workloads=${webObserveText(runtime.workloadReady)}`, `pg=${webObserveText(runtime.externalPostgresReady ?? runtime.localPostgresReady)}`, ...(notReadyWorkloads.length > 0 ? [`notReady=${webObserveShort(notReadyWorkloads.join(","), 80)}`] : []), ].join(" "); const failedTaskRuns = Array.isArray(pipelineRunDiagnostics.failedTaskRuns) ? pipelineRunDiagnostics.failedTaskRuns.map(compactNodeRuntimeTaskRunDiagnostic).filter(Boolean) : []; const pendingTaskRuns = Array.isArray(pipelineRunDiagnostics.pendingTaskRuns) ? pipelineRunDiagnostics.pendingTaskRuns.map(compactNodeRuntimeTaskRunDiagnostic).filter(Boolean) : []; const pipelineDetail = [ webObserveShort(webObserveText(pipelineRun.reason ?? pipelineRun.message), 50), ...(failedTaskRuns.length > 0 ? [`failed=${webObserveShort(failedTaskRuns.join(","), 80)}`] : []), ...(pendingTaskRuns.length > 0 ? [`pending=${webObserveShort(pendingTaskRuns.join(","), 80)}`] : []), ].filter(Boolean).join(" "); const status = result.ok === true ? "ok" : result.ok === false ? "failed" : "-"; const renderedText = [ "hwlab nodes control-plane status", "", webObserveTable( ["NODE", "LANE", "STATUS", "REASON", "SOURCE", "PIPELINERUN"], [[ scoped.node, scoped.lane, status, webObserveShort(webObserveText(result.degradedReason), 40), shortValue(result.sourceCommit), webObserveShort(webObserveText(pipelineRun.name), 36), ]], ), "", webObserveTable( ["STAGE", "STATUS", "DETAIL"], [ ["pipeline", pipelineRun.ready === true ? "ok" : pipelineRun.exists === true ? webObserveText(pipelineRun.status) : "missing", pipelineDetail], ["argo", argo.ready === true ? "ok" : "failed", `${webObserveText(argo.syncStatus)}/${webObserveText(argo.health)} rev=${shortValue(argo.syncRevision)}`], ["runtime", runtime.ready === true ? "ok" : "failed", runtimeDetail], ["public", publicProbe.ready === true ? "ok" : "failed", webObserveShort(webObserveText(diagnostic.kind ?? diagnostic.message), 80)], ["git-mirror", gitMirror.ready === true ? "ok" : "failed", `pending=${webObserveText(gitMirror.pendingFlush)} inSync=${webObserveText(gitMirror.githubInSync)}`], ], ), "", webObserveTable( ["LOCAL_SOURCE", "GITHUB_SOURCE", "LOCAL_GITOPS", "GITHUB_GITOPS"], [[ shortValue(gitMirror.localSource), shortValue(gitMirror.githubSource), shortValue(gitMirror.localGitops), shortValue(gitMirror.githubGitops), ]], ), "", "NEXT", ` ${result.nextAction ?? next.full ?? `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --full`}`, "", "Disclosure:", " default view is a bounded control-plane status summary; use --full or --raw for the complete JSON payload.", ].join("\n"); return { ...result, renderedText, contentType: "text/plain" }; } export function withNodeRuntimeControlPlaneStatusFullRendered(result: Record, scoped: ReturnType): RenderedCliResult { const summary = record(result.summary); const pipelineRun = record(summary.pipelineRun); const argo = record(summary.argo); const runtimeSummary = record(summary.runtime); const publicProbe = record(summary.publicProbe); const gitMirror = record(summary.gitMirror); const runtime = record(result.runtime); const bridge = record(runtime.externalPostgresBridge); const secrets = record(runtime.externalPostgresSecrets); const next = record(summary.next); const workloadReadiness = Array.isArray(runtime.workloadReadiness) ? runtime.workloadReadiness.map(record) : []; const workloadRows = workloadReadiness.length === 0 ? [["-", "-", "-", "-", "-"]] : workloadReadiness.slice(0, 18).map((item) => [ webObserveText(item.ref), item.ready === true ? "ok" : "not-ready", webObserveText(item.readyReplicas), webObserveText(item.currentReplicas), webObserveText(item.desiredReplicas), ]); const bridgeRows = bridge.required === false ? [["not-required", "-", "-", "-", "-"]] : [[ bridge.ready === true ? "ok" : "failed", webObserveText(bridge.serviceName), webObserveText(bridge.endpointAddress), webObserveText(bridge.port), `${webObserveText(bridge.expectedEndpointAddress)}:${webObserveText(bridge.expectedPort)}`, ]]; const secretRows = nodeRuntimeExternalPostgresSecretRows(secrets); const status = result.ok === true ? "ok" : result.ok === false ? "failed" : "-"; const renderedText = [ "hwlab nodes control-plane status --full", "", webObserveTable( ["NODE", "LANE", "STATUS", "REASON", "SOURCE", "PIPELINERUN"], [[ scoped.node, scoped.lane, status, webObserveShort(webObserveText(summary.degradedReason ?? result.degradedReason), 40), shortValue(summary.sourceCommit ?? result.sourceCommit), webObserveShort(webObserveText(pipelineRun.name ?? result.pipelineRun), 36), ]], ), "", webObserveTable( ["STAGE", "STATUS", "DETAIL"], [ ["pipeline", pipelineRun.ready === true ? "ok" : pipelineRun.exists === true ? webObserveText(pipelineRun.status) : "missing", webObserveShort(webObserveText(pipelineRun.reason ?? pipelineRun.message), 80)], ["argo", argo.ready === true ? "ok" : "failed", `${webObserveText(argo.syncStatus)}/${webObserveText(argo.health)} rev=${shortValue(argo.syncRevision)}`], ["runtime", runtimeSummary.ready === true ? "ok" : "failed", `namespace=${webObserveText(runtimeSummary.namespace)} workloads=${webObserveText(runtimeSummary.workloadReady)} pg=${webObserveText(runtimeSummary.externalPostgresReady ?? runtimeSummary.localPostgresReady)}`], ["public", publicProbe.ready === true ? "ok" : "failed", webObserveShort(webObserveText(record(publicProbe.diagnostic).kind ?? record(publicProbe.diagnostic).message), 80)], ["git-mirror", gitMirror.ready === true ? "ok" : "failed", `pending=${webObserveText(gitMirror.pendingFlush)} inSync=${webObserveText(gitMirror.githubInSync)}`], ], ), "", "RUNTIME_WORKLOADS", webObserveTable(["REF", "READY", "READY_REPLICAS", "CURRENT", "DESIRED"], workloadRows), ...(workloadReadiness.length > workloadRows.length ? [` ... ${workloadReadiness.length - workloadRows.length} more workloads omitted; use --raw for complete JSON.`] : []), "", "EXTERNAL_POSTGRES_BRIDGE", webObserveTable(["STATUS", "SERVICE", "ENDPOINT", "PORT", "EXPECTED"], bridgeRows), "", "EXTERNAL_POSTGRES_SECRETS", webObserveTable(["ROLE", "SECRET", "KEY", "PRESENT", "VALUES_PRINTED"], secretRows), "", "NEXT", ` ${summary.nextAction ?? next.full ?? `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --full`}`, ` bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --raw`, "", "Disclosure:", " --full status is a bounded runtime-workloads drill-down view; use --raw only when complete JSON is required.", " Secret evidence is limited to object metadata, key names, presence, and valuesPrinted=false.", ].join("\n"); return { ...result, renderedText, contentType: "text/plain" }; }