2026-10-10 06:22:20 +08:00
|
|
|
|
import { constants } from "node:buffer";
|
|
|
|
|
|
import { createHash } from "node:crypto";
|
2026-10-08 03:44:51 +08:00
|
|
|
|
import { Writable } from "node:stream";
|
|
|
|
|
|
|
2026-10-13 03:33:27 +08:00
|
|
|
|
import { Cli, Formatter } from "incur";
|
|
|
|
|
|
import { estimateTokenCount } from "tokenx";
|
|
|
|
|
|
import { parse as parseYaml } from "yaml";
|
2026-10-08 03:44:51 +08:00
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
|
|
|
|
|
|
|
|
import { writeJsonOutput } from "../../../src/cli/jsonOutput.js";
|
2026-10-10 06:22:20 +08:00
|
|
|
|
import {
|
2026-10-13 03:33:27 +08:00
|
|
|
|
createCliResultOutput,
|
2026-10-10 06:22:20 +08:00
|
|
|
|
streamCliCommandResults,
|
2026-10-13 03:33:27 +08:00
|
|
|
|
} from "../../../src/cli/cliResultOutput.js";
|
|
|
|
|
|
import { logCliCommand } from "../../../src/cliLogging.js";
|
2026-10-10 06:22:20 +08:00
|
|
|
|
import { registerManagedCommands } from "../../../src/cli/managedCommands.js";
|
2026-10-13 03:33:27 +08:00
|
|
|
|
import { analysisErrorProjectionSchema } from "../../../src/contracts/errorSchemas.js";
|
2026-10-10 06:22:20 +08:00
|
|
|
|
import { silentLogger } from "../../../src/logger.js";
|
2026-10-08 03:44:51 +08:00
|
|
|
|
|
|
|
|
|
|
const collect = async (
|
|
|
|
|
|
value: unknown,
|
|
|
|
|
|
format: "json" | "jsonl" = "json",
|
2026-10-09 20:29:48 -07:00
|
|
|
|
terminal = true,
|
2026-10-08 03:44:51 +08:00
|
|
|
|
): Promise<string> => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: unknown, _encoding, callback) {
|
|
|
|
|
|
if (!Buffer.isBuffer(chunk)) {
|
|
|
|
|
|
callback(new TypeError("Expected a UTF-8 output buffer"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
2026-10-09 20:29:48 -07:00
|
|
|
|
if (terminal) Object.defineProperty(destination, "isTTY", { value: true });
|
2026-10-08 03:44:51 +08:00
|
|
|
|
await writeJsonOutput(value, destination, format);
|
|
|
|
|
|
return Buffer.concat(chunks).toString("utf8");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
describe("incremental CLI JSON", () => {
|
|
|
|
|
|
it.each([
|
|
|
|
|
|
null,
|
|
|
|
|
|
true,
|
|
|
|
|
|
-0,
|
|
|
|
|
|
1e-7,
|
|
|
|
|
|
"",
|
|
|
|
|
|
{ empty: [], object: {}, nested: [{ value: null }] },
|
|
|
|
|
|
{ 'quoted"key': '\u0000\b\t\n\f\r\\"', unicode: "雪𝟠\ud800\udfff" },
|
|
|
|
|
|
{ text: `${"x".repeat(8191)}𝟠${"y".repeat(8191)}\ud800` },
|
|
|
|
|
|
])("matches native JSON encoding for %j", async (value) => {
|
|
|
|
|
|
expect(await collect(value)).toBe(`${JSON.stringify(value, null, 2)}\n`);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it("preserves shared objects without interpreting them as cycles", async () => {
|
|
|
|
|
|
const shared = { value: "shared" };
|
|
|
|
|
|
const value = { copies: [shared, shared] };
|
|
|
|
|
|
expect(await collect(value)).toBe(`${JSON.stringify(value, null, 2)}\n`);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-09 20:29:48 -07:00
|
|
|
|
it("indents JSON only for a terminal and writes piped JSON compactly", async () => {
|
|
|
|
|
|
const value = { nested: [{ value: null }], text: "雪𝟠" };
|
|
|
|
|
|
expect(await collect(value, "json", true)).toBe(
|
|
|
|
|
|
`${JSON.stringify(value, null, 2)}\n`,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(await collect(value, "json", false)).toBe(
|
|
|
|
|
|
`${JSON.stringify(value)}\n`,
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-08 03:44:51 +08:00
|
|
|
|
it("writes compact JSONL records and preserves an empty record set", async () => {
|
|
|
|
|
|
const value = [{ text: "first\nrecord" }, { text: "雪𝟠" }];
|
|
|
|
|
|
expect(await collect(value, "jsonl")).toBe(
|
|
|
|
|
|
`${value.map((item) => JSON.stringify(item)).join("\n")}\n`,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(await collect([], "jsonl")).toBe("");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it("bounds escaped leaf writes and waits for a slow destination", async () => {
|
|
|
|
|
|
const leaf = `\u0000雪𝟠`.repeat(32 * 1024);
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
let largestChunk = 0;
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
highWaterMark: 1,
|
|
|
|
|
|
write(chunk: unknown, _encoding, callback) {
|
|
|
|
|
|
if (!Buffer.isBuffer(chunk)) {
|
|
|
|
|
|
callback(new TypeError("Expected UTF-8 bytes"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
largestChunk = Math.max(largestChunk, chunk.length);
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
}, 1);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
2026-10-09 20:29:48 -07:00
|
|
|
|
Object.defineProperty(destination, "isTTY", { value: true });
|
2026-10-08 03:44:51 +08:00
|
|
|
|
await writeJsonOutput({ leaf }, destination);
|
|
|
|
|
|
expect(largestChunk).toBeLessThan(Buffer.byteLength(leaf));
|
|
|
|
|
|
const actual = Buffer.concat(chunks).toString("utf8");
|
|
|
|
|
|
const expected = `${JSON.stringify({ leaf }, null, 2)}\n`;
|
|
|
|
|
|
expect(actual.length).toBe(expected.length);
|
|
|
|
|
|
expect(actual === expected).toBe(true);
|
|
|
|
|
|
expect(destination.writableEnded).toBe(false);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it("waits for the final write even when it fits the destination buffer", async () => {
|
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(_chunk, _encoding, callback) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
completed = true;
|
|
|
|
|
|
callback();
|
|
|
|
|
|
}, 5);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
await writeJsonOutput({ result: "small" }, destination);
|
|
|
|
|
|
expect(completed).toBe(true);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it.each([false, true])(
|
|
|
|
|
|
"propagates a failed destination (deferred: %s)",
|
|
|
|
|
|
async (deferred) => {
|
|
|
|
|
|
const cause = new Error("Fixture destination failed");
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(_chunk, _encoding, callback) {
|
|
|
|
|
|
if (deferred) setTimeout(() => callback(cause), 5);
|
|
|
|
|
|
else callback(cause);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
writeJsonOutput({ result: "small" }, destination),
|
|
|
|
|
|
).rejects.toBe(cause);
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
it("leaves a reusable destination open without adding error listeners", async () => {
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(_chunk, _encoding, callback) {
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const listeners = destination.listenerCount("error");
|
|
|
|
|
|
await writeJsonOutput({ result: "first" }, destination);
|
|
|
|
|
|
await writeJsonOutput({ result: "second" }, destination);
|
|
|
|
|
|
expect(destination.listenerCount("error")).toBe(listeners);
|
|
|
|
|
|
expect(destination.writableEnded).toBe(false);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it("rejects a cycle at the serialization boundary", async () => {
|
|
|
|
|
|
const cycle: Record<string, unknown> = {};
|
|
|
|
|
|
cycle.self = cycle;
|
|
|
|
|
|
await expect(collect(cycle)).rejects.toThrow("circular reference");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it.each([undefined, Number.NaN, Number.POSITIVE_INFINITY, 1n, new Date(0)])(
|
|
|
|
|
|
"rejects non-JSON input %s",
|
|
|
|
|
|
async (value) => {
|
|
|
|
|
|
await expect(collect(value)).rejects.toThrow("ordinary JSON");
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-22 07:37:21 +08:00
|
|
|
|
describe("CLI command output wrapping", () => {
|
|
|
|
|
|
it("wraps a mounted group root reached through its alias", async () => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const output = createCliResultOutput(
|
|
|
|
|
|
["--format", "json", "--full-output"],
|
|
|
|
|
|
destination,
|
|
|
|
|
|
);
|
|
|
|
|
|
if (output === undefined)
|
|
|
|
|
|
throw new Error("Missing structured output surface");
|
|
|
|
|
|
|
|
|
|
|
|
const group = Cli.create("nested", {
|
|
|
|
|
|
aliases: ["nested-alias"],
|
|
|
|
|
|
run: () => ({ kind: "group-root" }),
|
|
|
|
|
|
}).command("child", { run: () => ({ kind: "child" }) });
|
|
|
|
|
|
const cli = Cli.create("rea-output-group-test", { sync: false }).command(
|
|
|
|
|
|
group,
|
|
|
|
|
|
);
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
|
|
|
|
|
|
let fallback = "";
|
|
|
|
|
|
await cli.serve(["nested-alias", "--format", "json"], {
|
|
|
|
|
|
stdout: (text) => {
|
|
|
|
|
|
if (!output.handled) fallback += text;
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
expect(output.handled).toBe(true);
|
|
|
|
|
|
expect(fallback).toBe("");
|
|
|
|
|
|
expect(JSON.parse(Buffer.concat(chunks).toString("utf8"))).toMatchObject({
|
|
|
|
|
|
ok: true,
|
|
|
|
|
|
data: { kind: "group-root" },
|
|
|
|
|
|
meta: { command: "nested" },
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-13 03:33:27 +08:00
|
|
|
|
describe("CLI result output surface", () => {
|
|
|
|
|
|
it.each(["json", "yaml", "toon", "md"] as const)(
|
|
|
|
|
|
"places typed analysis failures in the full-output error envelope (%s)",
|
|
|
|
|
|
async (format) => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const failure = {
|
|
|
|
|
|
error: "Analysis failed",
|
|
|
|
|
|
code: "target_unavailable",
|
|
|
|
|
|
category: "unavailable",
|
|
|
|
|
|
message: "The selected artifact is missing.",
|
|
|
|
|
|
retryable: false,
|
|
|
|
|
|
remediation: { action: "Check that the path exists." },
|
|
|
|
|
|
details: { source: "artifact-open" },
|
|
|
|
|
|
input_path: "missing.dll",
|
|
|
|
|
|
input_reason: "read-failed",
|
|
|
|
|
|
};
|
|
|
|
|
|
const argv = [
|
|
|
|
|
|
"inspect-managed-artifact",
|
|
|
|
|
|
"missing.dll",
|
|
|
|
|
|
"--format",
|
|
|
|
|
|
format,
|
|
|
|
|
|
"--full-output",
|
|
|
|
|
|
"--filter-output",
|
|
|
|
|
|
"message",
|
|
|
|
|
|
];
|
|
|
|
|
|
const output = createCliResultOutput(argv, destination);
|
|
|
|
|
|
if (output === undefined)
|
|
|
|
|
|
throw new Error("Missing structured output surface");
|
|
|
|
|
|
const cli = Cli.create("rea-output-test", { sync: false });
|
|
|
|
|
|
registerManagedCommands(cli, silentLogger, async () => failure);
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
let fallback = "";
|
|
|
|
|
|
const previousExitCode = process.exitCode;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await cli.serve(argv, {
|
|
|
|
|
|
stdout: (text) => {
|
|
|
|
|
|
if (!output.handled) fallback += text;
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.exitCode = previousExitCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
|
|
|
|
expect(output.handled).toBe(true);
|
|
|
|
|
|
expect(fallback).toBe("");
|
|
|
|
|
|
if (format === "json" || format === "yaml") {
|
|
|
|
|
|
const document = format === "json" ? JSON.parse(text) : parseYaml(text);
|
|
|
|
|
|
expect(document).toMatchObject({
|
|
|
|
|
|
ok: false,
|
|
|
|
|
|
error: {
|
|
|
|
|
|
code: failure.code,
|
|
|
|
|
|
category: failure.category,
|
|
|
|
|
|
message: failure.message,
|
|
|
|
|
|
retryable: false,
|
|
|
|
|
|
remediation: failure.remediation,
|
|
|
|
|
|
details: {
|
|
|
|
|
|
...failure.details,
|
|
|
|
|
|
input_path: failure.input_path,
|
|
|
|
|
|
input_reason: failure.input_reason,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
meta: { command: "inspect-managed-artifact" },
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(() =>
|
|
|
|
|
|
analysisErrorProjectionSchema.parse(document.error),
|
|
|
|
|
|
).not.toThrow();
|
|
|
|
|
|
expect(document).not.toHaveProperty("data");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
expect(text).toContain("false");
|
|
|
|
|
|
expect(text).toContain(failure.code);
|
|
|
|
|
|
expect(text).not.toContain("true");
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
it.each([
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "custom command failure",
|
|
|
|
|
|
value: {
|
|
|
|
|
|
status: "failed",
|
|
|
|
|
|
message: "Update was not applied.",
|
|
|
|
|
|
remediation: "Check the package manager, then retry.",
|
|
|
|
|
|
details: { stage: "install" },
|
|
|
|
|
|
},
|
|
|
|
|
|
isFailure: () => true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "unhealthy doctor result",
|
|
|
|
|
|
value: { healthy: false, checks: [{ name: "provider", ok: false }] },
|
|
|
|
|
|
isFailure: undefined,
|
|
|
|
|
|
},
|
|
|
|
|
|
])("preserves $name as a full-output error payload", async (scenario) => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const argv = ["fixture", "--json", "--full-output"];
|
|
|
|
|
|
const output = createCliResultOutput(argv, destination);
|
|
|
|
|
|
if (output === undefined) throw new Error("Missing JSON output surface");
|
|
|
|
|
|
const cli = Cli.create("rea-output-test", { sync: false });
|
|
|
|
|
|
cli.command("fixture", {
|
|
|
|
|
|
run: () =>
|
|
|
|
|
|
logCliCommand(
|
|
|
|
|
|
silentLogger,
|
|
|
|
|
|
"fixture",
|
|
|
|
|
|
async () => scenario.value,
|
|
|
|
|
|
scenario.isFailure,
|
|
|
|
|
|
),
|
|
|
|
|
|
});
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
const previousExitCode = process.exitCode;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await cli.serve(argv, { stdout: () => undefined });
|
|
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.exitCode = previousExitCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
expect(JSON.parse(Buffer.concat(chunks).toString("utf8"))).toMatchObject({
|
|
|
|
|
|
ok: false,
|
|
|
|
|
|
error: scenario.value,
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("full-output token windows", () => {
|
|
|
|
|
|
it("preserves Incur token-window metadata when truncating a TOON failure", async () => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const argv = [
|
|
|
|
|
|
"inspect-managed-artifact",
|
|
|
|
|
|
"missing.dll",
|
|
|
|
|
|
"--format",
|
|
|
|
|
|
"toon",
|
|
|
|
|
|
"--full-output",
|
|
|
|
|
|
"--token-limit",
|
|
|
|
|
|
"3",
|
|
|
|
|
|
];
|
|
|
|
|
|
const output = createCliResultOutput(argv, destination);
|
|
|
|
|
|
if (output === undefined) throw new Error("Missing TOON output surface");
|
|
|
|
|
|
const cli = Cli.create("rea-output-test", { sync: false });
|
|
|
|
|
|
registerManagedCommands(cli, silentLogger, async () => ({
|
|
|
|
|
|
code: "target_unavailable",
|
|
|
|
|
|
category: "unavailable",
|
|
|
|
|
|
message: "The selected artifact is missing.",
|
|
|
|
|
|
retryable: false,
|
|
|
|
|
|
remediation: { action: "Check that the path exists." },
|
|
|
|
|
|
details: { source: "artifact-open" },
|
|
|
|
|
|
}));
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
const previousExitCode = process.exitCode;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await cli.serve(argv, { stdout: () => undefined });
|
|
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.exitCode = previousExitCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
|
|
|
|
expect(text).toContain("[truncated: showing tokens 0–3 of");
|
|
|
|
|
|
expect(text).toMatch(/nextOffset:?\s*3/);
|
|
|
|
|
|
expect(text).toContain("ok: false");
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("logger classification handoff", () => {
|
|
|
|
|
|
it("honors an explicit success for data containing error-shaped fields", async () => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const result = {
|
|
|
|
|
|
error: {
|
|
|
|
|
|
code: "target_unavailable",
|
|
|
|
|
|
category: "unavailable",
|
|
|
|
|
|
message: "Source data describes a missing target.",
|
|
|
|
|
|
retryable: false,
|
|
|
|
|
|
remediation: { action: "Use the value as source data." },
|
|
|
|
|
|
},
|
|
|
|
|
|
source: "captured-metadata",
|
|
|
|
|
|
};
|
|
|
|
|
|
const argv = ["fixture", "--json", "--full-output"];
|
|
|
|
|
|
const output = createCliResultOutput(argv, destination);
|
|
|
|
|
|
if (output === undefined) throw new Error("Missing JSON output surface");
|
|
|
|
|
|
const cli = Cli.create("rea-output-test", { sync: false });
|
|
|
|
|
|
cli.command("fixture", {
|
|
|
|
|
|
run: () =>
|
|
|
|
|
|
logCliCommand(
|
|
|
|
|
|
silentLogger,
|
|
|
|
|
|
"fixture",
|
|
|
|
|
|
async () => result,
|
|
|
|
|
|
() => false,
|
|
|
|
|
|
),
|
|
|
|
|
|
});
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
const previousExitCode = process.exitCode;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await cli.serve(argv, { stdout: () => undefined });
|
|
|
|
|
|
expect(process.exitCode).toBe(previousExitCode);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.exitCode = previousExitCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
expect(JSON.parse(Buffer.concat(chunks).toString("utf8"))).toMatchObject({
|
|
|
|
|
|
ok: true,
|
|
|
|
|
|
data: result,
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("full-output token counts", () => {
|
|
|
|
|
|
it("counts the full failure payload before applying output filters", async () => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const failure = {
|
|
|
|
|
|
code: "target_unavailable",
|
|
|
|
|
|
category: "unavailable",
|
|
|
|
|
|
message: "The selected artifact is missing.",
|
|
|
|
|
|
retryable: false,
|
|
|
|
|
|
remediation: { action: "Check that the path exists." },
|
|
|
|
|
|
details: { source: "artifact-open" },
|
|
|
|
|
|
};
|
|
|
|
|
|
const argv = [
|
|
|
|
|
|
"inspect-managed-artifact",
|
|
|
|
|
|
"missing.dll",
|
|
|
|
|
|
"--json",
|
|
|
|
|
|
"--full-output",
|
|
|
|
|
|
"--token-count",
|
|
|
|
|
|
"--filter-output",
|
|
|
|
|
|
"message",
|
|
|
|
|
|
];
|
|
|
|
|
|
const output = createCliResultOutput(argv, destination);
|
|
|
|
|
|
if (output === undefined) throw new Error("Missing JSON output surface");
|
|
|
|
|
|
const cli = Cli.create("rea-output-test", { sync: false });
|
|
|
|
|
|
registerManagedCommands(cli, silentLogger, async () => failure);
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
const previousExitCode = process.exitCode;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await cli.serve(argv, { stdout: () => undefined });
|
|
|
|
|
|
expect(process.exitCode).toBe(1);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
process.exitCode = previousExitCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
expect(output.handled).toBe(true);
|
|
|
|
|
|
expect(Buffer.concat(chunks).toString("utf8")).toBe(
|
|
|
|
|
|
`${estimateTokenCount(Formatter.format(failure, "json"))}\n`,
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe("CLI result output surface", () => {
|
2026-10-08 03:44:51 +08:00
|
|
|
|
it.each([
|
|
|
|
|
|
["--json", "--token-limit", "5"],
|
|
|
|
|
|
["--json", "--format", "invalid"],
|
|
|
|
|
|
])("delegates unsupported output controls %j", (...arguments_) => {
|
2026-10-13 03:33:27 +08:00
|
|
|
|
expect(createCliResultOutput(arguments_, new Writable())).toBe(undefined);
|
2026-10-08 03:44:51 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it("keeps ownership after a failed write so no second document is emitted", async () => {
|
|
|
|
|
|
const cause = new Error("Fixture destination failed");
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(_chunk, _encoding, callback) {
|
|
|
|
|
|
callback(cause);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
2026-10-13 03:33:27 +08:00
|
|
|
|
const output = createCliResultOutput(["--json"], destination);
|
2026-10-08 03:44:51 +08:00
|
|
|
|
if (output === undefined) throw new Error("Missing JSON output surface");
|
|
|
|
|
|
await expect(
|
|
|
|
|
|
output.write(
|
|
|
|
|
|
{ result: "small" },
|
|
|
|
|
|
{ command: "fixture", duration: "1ms", format: "json" },
|
|
|
|
|
|
),
|
|
|
|
|
|
).rejects.toBe(cause);
|
|
|
|
|
|
expect(output.handled).toBe(true);
|
|
|
|
|
|
expect(output.failed).toBe(true);
|
|
|
|
|
|
});
|
2026-10-10 06:22:20 +08:00
|
|
|
|
|
|
|
|
|
|
it("streams managed command output past the engine string limit and declines oversized token counting", async () => {
|
|
|
|
|
|
const leaf = "x".repeat(64 * 1024);
|
|
|
|
|
|
const count = Math.ceil(constants.MAX_STRING_LENGTH / leaf.length) + 1;
|
|
|
|
|
|
const value = { methods: Array.from({ length: count }, () => leaf) };
|
|
|
|
|
|
const expected = createHash("sha256");
|
|
|
|
|
|
const encodedLeaf = JSON.stringify(leaf);
|
|
|
|
|
|
const prefix = '{\n "methods": [\n ';
|
|
|
|
|
|
const separator = ",\n ";
|
|
|
|
|
|
const suffix = "\n ]\n}\n";
|
|
|
|
|
|
expected.update(prefix);
|
|
|
|
|
|
for (let index = 0; index < count; index += 1) {
|
|
|
|
|
|
if (index > 0) expected.update(separator);
|
|
|
|
|
|
expected.update(encodedLeaf);
|
|
|
|
|
|
}
|
|
|
|
|
|
expected.update(suffix);
|
|
|
|
|
|
const expectedCharacters =
|
|
|
|
|
|
prefix.length +
|
|
|
|
|
|
count * encodedLeaf.length +
|
|
|
|
|
|
(count - 1) * separator.length +
|
|
|
|
|
|
suffix.length;
|
|
|
|
|
|
expect(expectedCharacters).toBeGreaterThan(constants.MAX_STRING_LENGTH);
|
|
|
|
|
|
|
|
|
|
|
|
const actual = createHash("sha256");
|
|
|
|
|
|
let bytes = 0;
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
bytes += chunk.length;
|
|
|
|
|
|
actual.update(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const cli = Cli.create("rea-output-test", { sync: false });
|
2026-10-21 18:43:30 -06:00
|
|
|
|
registerManagedCommands(cli, silentLogger, async (...[path, operation]) => {
|
2026-10-10 06:22:20 +08:00
|
|
|
|
expect(path).toBe("fixture.dll");
|
|
|
|
|
|
expect(operation).toBe("inspect_managed_members");
|
|
|
|
|
|
return value;
|
|
|
|
|
|
});
|
2026-10-09 20:29:48 -07:00
|
|
|
|
// A terminal destination exercises the indented encoding past the limit.
|
|
|
|
|
|
Object.defineProperty(destination, "isTTY", { value: true });
|
2026-10-10 06:22:20 +08:00
|
|
|
|
const argv = ["inspect-managed-members", "fixture.dll", "--json"];
|
2026-10-13 03:33:27 +08:00
|
|
|
|
const output = createCliResultOutput(argv, destination);
|
2026-10-10 06:22:20 +08:00
|
|
|
|
if (output === undefined) throw new Error("Missing JSON output surface");
|
|
|
|
|
|
streamCliCommandResults(cli, output);
|
|
|
|
|
|
let exitCode = 0;
|
|
|
|
|
|
let fallbackBytes = 0;
|
|
|
|
|
|
await cli.serve(argv, {
|
|
|
|
|
|
stdout: (text) => {
|
|
|
|
|
|
if (!output.handled) fallbackBytes += text.length;
|
|
|
|
|
|
},
|
|
|
|
|
|
exit: (code) => {
|
|
|
|
|
|
exitCode = code;
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(exitCode).toBe(0);
|
|
|
|
|
|
expect(fallbackBytes).toBe(0);
|
|
|
|
|
|
expect(bytes).toBe(expectedCharacters);
|
|
|
|
|
|
expect(actual.digest("hex")).toBe(expected.digest("hex"));
|
|
|
|
|
|
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const tokenArgv = [...argv, "--token-count", "--full-output"];
|
2026-10-13 03:33:27 +08:00
|
|
|
|
const tokenOutput = createCliResultOutput(
|
2026-10-10 06:22:20 +08:00
|
|
|
|
tokenArgv,
|
|
|
|
|
|
new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
|
|
|
|
|
if (tokenOutput === undefined)
|
|
|
|
|
|
throw new Error("Missing token-count output surface");
|
|
|
|
|
|
const tokenCli = Cli.create("rea-output-test", { sync: false });
|
|
|
|
|
|
registerManagedCommands(tokenCli, silentLogger, async () => value);
|
|
|
|
|
|
streamCliCommandResults(tokenCli, tokenOutput);
|
|
|
|
|
|
await tokenCli.serve(tokenArgv, {
|
|
|
|
|
|
stdout: (text) => {
|
|
|
|
|
|
if (!tokenOutput.handled) fallbackBytes += text.length;
|
|
|
|
|
|
},
|
|
|
|
|
|
exit: (code) => {
|
|
|
|
|
|
exitCode = code;
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
expect(exitCode).toBe(1);
|
|
|
|
|
|
expect(fallbackBytes).toBe(0);
|
|
|
|
|
|
expect(JSON.parse(Buffer.concat(chunks).toString("utf8"))).toMatchObject({
|
|
|
|
|
|
ok: false,
|
|
|
|
|
|
error: {
|
|
|
|
|
|
code: "resource_constraint",
|
|
|
|
|
|
remediation: {
|
|
|
|
|
|
action: expect.stringContaining("Remove --token-count"),
|
|
|
|
|
|
},
|
|
|
|
|
|
details: {
|
|
|
|
|
|
reported_limits: {
|
|
|
|
|
|
formatted_characters: expectedCharacters - 1,
|
|
|
|
|
|
max_string_characters: constants.MAX_STRING_LENGTH,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
meta: { command: "inspect-managed-members" },
|
|
|
|
|
|
});
|
|
|
|
|
|
}, 60_000);
|
2026-10-08 03:44:51 +08:00
|
|
|
|
});
|
2026-10-13 05:43:20 -07:00
|
|
|
|
|
|
|
|
|
|
describe("streamed CLI filtered selections", () => {
|
|
|
|
|
|
it.each([
|
|
|
|
|
|
["raw_result", "json", "null\n"],
|
|
|
|
|
|
["raw_result", "jsonl", "null\n"],
|
|
|
|
|
|
["nested.value", "json", '{"nested":{"value":null}}\n'],
|
|
|
|
|
|
["missing", "json", "{}\n"],
|
|
|
|
|
|
["count", "json", "0\n"],
|
|
|
|
|
|
["enabled", "json", "false\n"],
|
|
|
|
|
|
] as const)(
|
|
|
|
|
|
"keeps the selected value of --filter-output %s in %s",
|
|
|
|
|
|
async (filter, format, expected) => {
|
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
|
const destination = new Writable({
|
|
|
|
|
|
write(chunk: Buffer, _encoding, callback) {
|
|
|
|
|
|
chunks.push(chunk);
|
|
|
|
|
|
callback();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
const output = createCliResultOutput(
|
|
|
|
|
|
["--format", format, "--filter-output", filter],
|
|
|
|
|
|
destination,
|
|
|
|
|
|
);
|
|
|
|
|
|
if (output === undefined) throw new Error("Missing JSON output surface");
|
|
|
|
|
|
await output.write(
|
|
|
|
|
|
{ raw_result: null, nested: { value: null }, count: 0, enabled: false },
|
|
|
|
|
|
{ command: "fixture", duration: "1ms", format },
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(Buffer.concat(chunks).toString("utf8")).toBe(expected);
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|