fix(cli): recover child json from dumps and artifacts

This commit is contained in:
Codex
2026-07-02 18:27:49 +00:00
parent fa4e1d236d
commit e2500ba418
3 changed files with 429 additions and 11 deletions
+108
View File
@@ -0,0 +1,108 @@
import assert from "node:assert/strict";
import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "bun:test";
import { resolveCliChildJsonObject } from "./cli-child-json-recovery";
test("child JSON recovery reads full JSON from CLI stdout dump wrapper", async () => {
const dir = await mkdtemp(join(tmpdir(), "unidesk-cli-child-json-dump-"));
const dumpPath = join(dir, "dump.json");
await writeFile(dumpPath, JSON.stringify({ ok: true, data: { contract: "full-json" } }) + "\n");
const wrapper = {
ok: true,
command: "fixture",
data: {
outputTruncated: true,
reason: "stdout-json-bytes-exceeded-threshold",
dump: { path: dumpPath },
},
};
const resolved = resolveCliChildJsonObject({
stdout: JSON.stringify(wrapper),
requestedStdoutType: "fixture JSON",
});
assert.equal(resolved.source, "dump");
assert.equal(resolved.parsed?.ok, true);
assert.deepEqual(resolved.parsed?.data, { contract: "full-json" });
assert.equal(resolved.diagnostics.stdoutKind, "dump-wrapper");
assert.equal(resolved.diagnostics.dumpPath, dumpPath);
assert.equal(resolved.diagnostics.dumpReadOk, true);
assert.equal(resolved.diagnostics.dumpJsonOk, true);
});
test("child JSON recovery falls back to artifact after trans truncation summary", () => {
const summary = {
code: "ssh-truncation-summary",
exitCode: 0,
timedOut: false,
stdout: {
stream: "stdout",
thresholdBytes: 10240,
observedBytesAtTruncation: 165000,
forwardedBytes: 10240,
dumpPath: null,
dumpError: null,
},
};
const resolved = resolveCliChildJsonObject({
stdout: `UNIDESK_SSH_TRUNCATION_SUMMARY ${JSON.stringify(summary)}\n`,
requestedStdoutType: "web-probe observe analyze compact JSON",
artifactFallback: {
path: "analysis/report.json",
nextCommand: "bun scripts/cli.ts web-probe observe collect webobs-fixture --view files --file analysis/report.json",
read: () => ({ ok: true, value: { ok: true, reportJsonPath: "analysis/report.json" }, reason: "analysis-artifact-contract" }),
},
});
assert.equal(resolved.source, "artifact");
assert.equal(resolved.parsed?.ok, true);
assert.equal(resolved.diagnostics.stdoutKind, "ssh-truncation-summary");
assert.equal(resolved.diagnostics.fallbackReason, "stdout-trans-truncated");
const artifact = resolved.diagnostics.artifact as Record<string, unknown>;
assert.equal(artifact.ok, true);
assert.equal(artifact.nextCommand, "bun scripts/cli.ts web-probe observe collect webobs-fixture --view files --file analysis/report.json");
});
test("child JSON recovery falls back to artifact when stdout is not JSON", () => {
const resolved = resolveCliChildJsonObject({
stdout: "remote helper wrote a bounded text summary instead of JSON",
requestedStdoutType: "web-probe observe analyze compact JSON",
artifactFallback: {
path: "analysis/report.json",
nextCommand: "collect report",
read: () => ({ ok: true, value: { ok: true, counts: { network: 9 }, reportJsonPath: "analysis/report.json" } }),
},
});
assert.equal(resolved.source, "artifact");
assert.equal(resolved.parsed?.ok, true);
assert.deepEqual(resolved.parsed?.counts, { network: 9 });
assert.equal(resolved.diagnostics.stdoutKind, "non-json");
assert.equal(resolved.diagnostics.fallbackReason, "stdout-not-json");
});
test("child JSON recovery reports artifact fallback failure when stdout is unusable and artifact is missing", () => {
const resolved = resolveCliChildJsonObject({
stdout: "not json",
requestedStdoutType: "web-probe observe analyze compact JSON",
artifactFallback: {
path: "analysis/report.json",
nextCommand: "collect report",
read: () => ({ ok: false, reason: "artifact-missing", path: "analysis/report.json" }),
},
});
assert.equal(resolved.parsed, null);
assert.equal(resolved.source, null);
assert.equal(resolved.diagnostics.stdoutKind, "non-json");
assert.equal(resolved.diagnostics.fallbackReason, "stdout-not-json");
const artifact = resolved.diagnostics.artifact as Record<string, unknown>;
assert.equal(artifact.ok, false);
assert.equal(artifact.reason, "artifact-missing");
assert.equal(artifact.path, "analysis/report.json");
assert.equal(artifact.nextCommand, "collect report");
});