import { assertEquals } from "@std/assert"; import { TERMINAL_PALETTE } from "@berry/util/palette.ts"; import type { ToolCall } from "@berry/api/web.ts"; import { withDom } from "@berry/util/dom.ts"; import { bashRenderer, bashView } from "./bash-view.tsx"; Deno.test("bash colors live raw output before completion and keeps older streams working", async () => { await withDom(() => { const view = bashView(); const call: ToolCall = { name: "bash", args: { command: "printf colors" }, status: "running", output: null, details: null, result: null, error: null, startedAt: null, durationMs: null, }; // the raw stream carries colour that the sanitized output has lost view.update({ ...call, output: "[31mred", details: { rawOutput: "\x1b[31mred" } }); const output = view.element.querySelector("pre:not(.code)")!; assertEquals(output.textContent, "red"); const red = output.firstElementChild as HTMLElement; const color = document.createElement("span"); color.style.color = TERMINAL_PALETTE[1]; assertEquals(red.style.color, color.style.color); const rawOutput = "\x1b[31mredder\x1b[0m plain"; view.update({ ...call, status: "ok", result: `${rawOutput}\n\n[exit code 0]` }); assertEquals(output.firstElementChild, red); assertEquals(output.textContent, "redder plain\n\n[exit code 0]"); const legacy = bashView(); legacy.update({ ...call, output: "\x1b[31mred" }); const legacyOutput = legacy.element.querySelector("pre:not(.code)")!; assertEquals(legacyOutput.textContent, "red"); assertEquals((legacyOutput.firstElementChild as HTMLElement).style.color, color.style.color); legacy.update({ ...call, status: "ok", result: { output: "final", exitCode: 3 } }); assertEquals(legacyOutput.textContent, "final"); assertEquals(legacy.element.querySelector(".exit")!.textContent, "exit 3"); legacy.update({ ...call, output: "", details: { rawOutput: "" } }); assertEquals((legacyOutput as HTMLElement).hidden, true); }); }); Deno.test("bash keeps timeout metadata out of the command summary", () => { const args = { command: "echo one\necho two", timeout: 60 }; assertEquals(bashRenderer.summary?.(args), "echo one"); assertEquals(bashRenderer.timeout?.(args), 60); assertEquals(bashRenderer.timeout?.({ command: "sleep 5", timeout: 0.2 }), 0.2); assertEquals(bashRenderer.summary?.({ command: "pwd" }), "pwd"); assertEquals(bashRenderer.timeout?.({ command: "pwd" }), null); assertEquals(bashRenderer.summary?.(null), null); assertEquals(bashRenderer.timeout?.(null), null); }); Deno.test("a nonzero exit is a bash call's outcome, from the text a model saw or a script's result", () => { const call = (result: ToolCall["result"]): ToolCall => ({ name: "bash", args: { command: "false" }, status: "ok", output: null, details: null, result, error: null, startedAt: null, durationMs: null, }); assertEquals(bashRenderer.outcome?.(call("failed\n\n[exit code 3]")), "exit 3"); assertEquals( bashRenderer.outcome?.(call("long\n\n[exit code 2; full output: /tmp/out]")), "exit 2", ); assertEquals(bashRenderer.outcome?.(call({ output: "", exitCode: 1 })), "exit 1"); assertEquals(bashRenderer.outcome?.(call("fine\n\n[exit code 0]")), null); assertEquals(bashRenderer.outcome?.(call({ output: "", exitCode: 0 })), null); assertEquals(bashRenderer.outcome?.(call("mentions [exit code 9] in passing\nthen more")), null); });