feat(cli): migrate child json recovery callers
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-27-p11-monitor-web-observability-dashboard.
|
||||
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-07-01-p15-cadence-otel.
|
||||
// Responsibility: P5 web-probe sentinel service validation, maintenance, report and dashboard commands.
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import type { CommandResult } from "./command";
|
||||
import { runCommand } from "./command";
|
||||
import { resolveCliChildJsonCommandResult } from "./cli-child-json-recovery";
|
||||
import { repoRoot } from "./config";
|
||||
import { startJob } from "./jobs";
|
||||
import type { RenderedCliResult } from "./output";
|
||||
@@ -513,8 +513,13 @@ function readSentinelReportArtifactSummary(state: SentinelCicdState, body: Recor
|
||||
"NODE",
|
||||
].join("\n");
|
||||
const result = runCommand(["trans", `${state.spec.nodeId}:${state.spec.workspace}`, "sh"], repoRoot, { input: script, timeoutMs: Math.max(5, Math.min(timeoutSeconds, 55)) * 1000 });
|
||||
const parsed = parseJsonObject(result.stdout);
|
||||
return { ok: result.exitCode === 0 && parsed?.ok === true, ...record(parsed), result: compactCommand(result), valuesRedacted: true };
|
||||
const parsedResolution = resolveCliChildJsonCommandResult({
|
||||
result,
|
||||
requestedStdoutType: "web-probe sentinel report summary JSON",
|
||||
acceptParsed: (value) => value.ok === true || value.ok === false || typeof value.reason === "string" || typeof value.reportJsonPath === "string" || Object.keys(record(value.counts)).length > 0,
|
||||
});
|
||||
const parsed = parsedResolution.parsed;
|
||||
return { ok: result.exitCode === 0 && parsed?.ok === true, ...record(parsed), result: compactCommand(result), stdoutRecovery: parsedResolution.diagnostics, valuesRedacted: true };
|
||||
}
|
||||
|
||||
function isSafeSentinelReportStateDir(value: string): boolean {
|
||||
@@ -1468,10 +1473,13 @@ function callSentinelService(state: SentinelCicdState, method: "GET" | "POST", p
|
||||
let parsed: Record<string, unknown> | null = null;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
result = runCommand(["trans", stringAt(state.controlPlaneNode, "kubeRoute"), "sh", "--", script], repoRoot, { timeoutMs: attemptTimeoutSeconds * 1000 });
|
||||
parsed = parseJsonObject(result.stdout);
|
||||
const recovered = parsed === null ? recoverTruncatedSshStdoutJson(result) : null;
|
||||
if (recovered !== null) parsed = recovered.parsed;
|
||||
attempts.push({ attempt, ...compactCommand(result), parsedOk: parsed !== null, parsedFromDump: recovered !== null, dumpPath: recovered?.dumpPath ?? null, valuesRedacted: true });
|
||||
const parsedResolution = resolveCliChildJsonCommandResult({
|
||||
result,
|
||||
requestedStdoutType: "web-probe sentinel service proxy response JSON",
|
||||
acceptParsed: isSentinelServiceResponseContract,
|
||||
});
|
||||
parsed = parsedResolution.parsed;
|
||||
attempts.push({ attempt, ...compactCommand(result), parsedOk: parsed !== null, stdoutRecovery: parsedResolution.diagnostics, valuesRedacted: true });
|
||||
if (result.exitCode === 0) break;
|
||||
}
|
||||
const compactBodyJson = compactSentinelServiceBodyJson(parsed);
|
||||
@@ -1492,27 +1500,15 @@ function callSentinelService(state: SentinelCicdState, method: "GET" | "POST", p
|
||||
};
|
||||
}
|
||||
|
||||
function recoverTruncatedSshStdoutJson(result: CommandResult): { parsed: Record<string, unknown>; dumpPath: string } | null {
|
||||
const dumpPath = sshStdoutDumpPathFromStderr(result.stderr);
|
||||
if (dumpPath === null || !existsSync(dumpPath)) return null;
|
||||
const parsed = parseJsonObject(readFileSync(dumpPath, "utf8"));
|
||||
return parsed === null ? null : { parsed, dumpPath };
|
||||
}
|
||||
|
||||
function sshStdoutDumpPathFromStderr(value: string): string | null {
|
||||
for (const rawLine of value.split(/\r?\n/u)) {
|
||||
const line = rawLine.trim();
|
||||
const prefix = "UNIDESK_SSH_STDOUT_TRUNCATED ";
|
||||
if (!line.startsWith(prefix)) continue;
|
||||
try {
|
||||
const payload = JSON.parse(line.slice(prefix.length)) as unknown;
|
||||
const dumpPath = stringAtNullable(record(payload), "dumpPath");
|
||||
if (dumpPath !== null) return dumpPath;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
function isSentinelServiceResponseContract(value: Record<string, unknown>): boolean {
|
||||
return value.ok === false
|
||||
|| typeof value.view === "string"
|
||||
|| typeof value.renderedText === "string"
|
||||
|| typeof value.error === "string"
|
||||
|| Array.isArray(value.availableViews)
|
||||
|| Object.keys(record(value.run)).length > 0
|
||||
|| Object.keys(record(value.summary)).length > 0
|
||||
|| Array.isArray(value.findings);
|
||||
}
|
||||
|
||||
function callSentinelServiceDirect(method: "GET" | "POST", pathWithQuery: string, body: Record<string, unknown> | null, timeoutSeconds: number, url: string): Record<string, unknown> {
|
||||
|
||||
Reference in New Issue
Block a user