From f3390d576f7d8af194bebb33d3780c5cbe5ace16 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 25 Jul 2026 19:17:40 +0000 Subject: [PATCH 1/4] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/link-assistant/agent-commander/issues/46 --- .gitkeep | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitkeep b/.gitkeep index 13d525b..71f29e8 100644 --- a/.gitkeep +++ b/.gitkeep @@ -1 +1,2 @@ -# .gitkeep file auto-generated at 2026-06-17T15:20:23.237Z for PR creation at branch issue-39-15508d68b466 for issue https://github.com/link-assistant/agent-commander/issues/39 \ No newline at end of file +# .gitkeep file auto-generated at 2026-06-17T15:20:23.237Z for PR creation at branch issue-39-15508d68b466 for issue https://github.com/link-assistant/agent-commander/issues/39 +# Updated: 2026-07-25T19:17:39.829Z \ No newline at end of file From d44d5e661c77ec4e336ba120180a1917a6da57c9 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 25 Jul 2026 19:26:18 +0000 Subject: [PATCH 2/4] feat(js): publish configurable TUI replay bundles --- js/.changeset/friendly-tuis-render.md | 6 + js/README.md | 21 ++- js/package-lock.json | 243 +++++++++++++++++++++++++- js/package.json | 2 +- js/src/tui.mjs | 45 ++++- js/test/tui.test.mjs | 81 ++++++++- package.json | 2 +- 7 files changed, 388 insertions(+), 12 deletions(-) create mode 100644 js/.changeset/friendly-tuis-render.md diff --git a/js/.changeset/friendly-tuis-render.md b/js/.changeset/friendly-tuis-render.md new file mode 100644 index 0000000..264b94f --- /dev/null +++ b/js/.changeset/friendly-tuis-render.md @@ -0,0 +1,6 @@ +--- +'agent-commander': minor +--- + +Expose terminal artifact rendering and format options, default agent captures +to publishable 4:3 geometry, and include GIF replay fallbacks. diff --git a/js/README.md b/js/README.md index f18c1fd..81d3c25 100644 --- a/js/README.md +++ b/js/README.md @@ -101,8 +101,8 @@ const capture = await captureAgentTui({ startupInteractions: [ { after: 'Do you trust the contents of this directory?', key: 'ENTER' }, ], - cols: 100, - rows: 30, + // Defaults to an 80×30 terminal whose rendered content is 4:3. + aspectRatio: 4 / 3, interactions: [ { after: 'Choose an option', key: 'DOWN' }, { after: 'Second option', key: 'ENTER' }, @@ -110,6 +110,16 @@ const capture = await captureAgentTui({ ], stopMarker: 'Finished', artifactDirectory: 'artifacts/codex', + artifactOptions: { + borderRadius: 0, + cellWidth: 9, + cellHeight: 18, + fontSize: 14, + padding: 12, + background: '#111827', + foreground: '#e5e7eb', + }, + artifacts: ['svg', 'gif', 'cast', 'frames', 'transcript'], }); console.log(capture.transcript); @@ -119,8 +129,11 @@ console.log(capture.events); // normalized message and tool_call events The result includes command-stream's unrolled transcript, settled frames, raw asciicast event stream, and normalized semantic events. The artifact directory contains `transcript.txt`, `frames.json`, `session.cast`, `snapshot.svg`, and -the self-contained animated `recording.svg`. Partial artifacts are also written -when the terminal command times out. +the self-contained animated `recording.svg`, plus `recording.gif` for consumers +that cannot animate SVG. Use `artifacts` to select bundle formats and +`artifactOptions` to customize renderer geometry and theme. Explicit `cols` and +`rows` override the default geometry. Partial artifacts are also written when +the terminal command times out. For large generated prompts, pass `promptFile` or let the controller create a temporary prompt file automatically for `claude`, `codex`, `opencode`, `agent`, `qwen`, and `gemini`: diff --git a/js/package-lock.json b/js/package-lock.json index 498cb64..d6a1cd9 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -9,7 +9,7 @@ "version": "0.9.1", "license": "Unlicense", "dependencies": { - "command-stream": "^0.15.0" + "command-stream": "^0.17.0" }, "bin": { "start-agent": "bin/start-agent.mjs", @@ -938,6 +938,233 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@resvg/resvg-js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", + "license": "MPL-2.0", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@resvg/resvg-js-android-arm-eabi": "2.6.2", + "@resvg/resvg-js-android-arm64": "2.6.2", + "@resvg/resvg-js-darwin-arm64": "2.6.2", + "@resvg/resvg-js-darwin-x64": "2.6.2", + "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", + "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", + "@resvg/resvg-js-linux-arm64-musl": "2.6.2", + "@resvg/resvg-js-linux-x64-gnu": "2.6.2", + "@resvg/resvg-js-linux-x64-musl": "2.6.2", + "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", + "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", + "@resvg/resvg-js-win32-x64-msvc": "2.6.2" + } + }, + "node_modules/@resvg/resvg-js-android-arm-eabi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-android-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-x64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-arm64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-ia32-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", + "cpu": [ + "ia32" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-x64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1396,12 +1623,14 @@ } }, "node_modules/command-stream": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/command-stream/-/command-stream-0.15.0.tgz", - "integrity": "sha512-Lphe8u3lZocvxdLspqPesAl+EBAFe2VAbL3Ag6wU4XcHCVWJDHQE/KXdU6J2XevipXn0ZzoIqDCYCHaHsXhWtg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/command-stream/-/command-stream-0.17.0.tgz", + "integrity": "sha512-rRAj8UdtMfCAyetyFfoOfYVOuWgATgKfFjRMMdhiLy9wwYbI72Se5U0OIWByxjC/VZ+RwlA0wugqQ1cqWUoR2g==", "license": "Unlicense", "dependencies": { + "@resvg/resvg-js": "^2.6.2", "@xterm/headless": "^6.0.0", + "gifenc": "^1.0.3", "node-pty": "^1.2.0-beta.14" }, "engines": { @@ -2090,6 +2319,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gifenc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gifenc/-/gifenc-1.0.3.tgz", + "integrity": "sha512-xdr6AdrfGBcfzncONUOlXMBuc5wJDtOueE3c5rdG0oNgtINLD+f2iFZltrBRZYzACRbKr+mSVU/x98zv2u3jmw==", + "license": "MIT" + }, "node_modules/gitignore-to-glob": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", diff --git a/js/package.json b/js/package.json index fd1a1e0..7396d0e 100644 --- a/js/package.json +++ b/js/package.json @@ -49,7 +49,7 @@ "node": ">=20.0.0" }, "dependencies": { - "command-stream": "^0.15.0" + "command-stream": "^0.17.0" }, "devDependencies": { "@changesets/cli": "^2.29.7", diff --git a/js/src/tui.mjs b/js/src/tui.mjs index d2bb8d2..ef2ff39 100644 --- a/js/src/tui.mjs +++ b/js/src/tui.mjs @@ -1,4 +1,6 @@ import { captureTerminal } from 'command-stream'; +import { rm } from 'node:fs/promises'; +import { join } from 'node:path'; import { getTool, isToolSupported } from './tools/index.mjs'; import { normalizeExtraArgs, normalizeExtraEnv } from './tools/shell.mjs'; @@ -132,6 +134,41 @@ const ARG_BUILDERS = { qwen: qwenArgs, }; +const DEFAULT_CAPTURE_GEOMETRY = Object.fromEntries( + Object.keys(ARG_BUILDERS).map((tool) => [ + tool, + { cols: 80, aspectRatio: 4 / 3 }, + ]) +); + +const ARTIFACT_FILES = { + svg: ['snapshot.svg', 'recording.svg'], + gif: ['recording.gif'], + cast: ['session.cast'], + frames: ['frames.json'], + transcript: ['transcript.txt'], +}; + +const selectArtifacts = async (directory, requested) => { + if (!directory || requested === undefined) { + return; + } + const selected = new Set(requested); + const unknown = [...selected].filter((name) => !ARTIFACT_FILES[name]); + if (unknown.length > 0) { + throw new TypeError( + `Unknown terminal artifact type: ${unknown.join(', ')}` + ); + } + await Promise.all( + Object.entries(ARTIFACT_FILES) + .filter(([name]) => !selected.has(name)) + .flatMap(([, files]) => + files.map((file) => rm(join(directory, file), { force: true })) + ) + ); +}; + const launchEnvironment = ({ extraEnv, tool, readOnly }) => { const entries = normalizeExtraEnv(extraEnv); if (tool === 'opencode' && readOnly) { @@ -235,13 +272,16 @@ export const captureAgentTui = async (options) => { promptKey = 'ENTER', startupInteractions = [], interactions = [], - cols, + cols = DEFAULT_CAPTURE_GEOMETRY[tool]?.cols ?? 80, rows, + aspectRatio = DEFAULT_CAPTURE_GEOMETRY[tool]?.aspectRatio ?? 4 / 3, settleMilliseconds, stopMarker, stopMarkerGraceMilliseconds, timeoutMilliseconds, artifactDirectory, + artifactOptions, + artifacts, onTrace, } = options; const launch = buildAgentTuiLaunch(options); @@ -256,6 +296,7 @@ export const captureAgentTui = async (options) => { ...launch, cols, rows, + aspectRatio, settleMilliseconds, interactions: [ ...startupInteractions, @@ -266,8 +307,10 @@ export const captureAgentTui = async (options) => { stopMarkerGraceMilliseconds, timeoutMilliseconds, artifactDirectory, + artifactOptions, onTrace, }); + await selectArtifacts(artifactDirectory, artifacts); return { ...capture, tool, diff --git a/js/test/tui.test.mjs b/js/test/tui.test.mjs index a8e4946..96d72f8 100644 --- a/js/test/tui.test.mjs +++ b/js/test/tui.test.mjs @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { mkdtemp, readFile, rm } from 'node:fs/promises'; +import { access, mkdtemp, readFile, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { test } from 'node:test'; @@ -151,10 +151,89 @@ test( (await readFile(join(artifactDirectory, 'recording.svg'))).length > 0, tool ); + assert.ok( + (await readFile(join(artifactDirectory, 'recording.gif'))).length > 0, + tool + ); + assert.strictEqual(capture.asciicast.header.width, 24, tool); + assert.strictEqual(capture.asciicast.header.height, 6, tool); } } ); +test('captureAgentTui defaults every agent to publishable 4:3 geometry', async () => { + const directory = dirname(fileURLToPath(import.meta.url)); + + for (const tool of tools) { + const capture = await captureAgentTui({ + tool, + workingDirectory: process.cwd(), + executable: process.execPath, + prefixArgs: [join(directory, 'fixtures/fake-agent-tui.mjs'), tool], + prompt: 'hello', + promptAfter: `ready:${tool}`, + startupInteractions: [ + { after: `trust:${tool}`, text: 'y', key: 'ENTER' }, + ], + interactions: [ + { + after: 'waiting-resize', + resize: { cols: 81, rows: 31 }, + }, + ], + }); + + assert.strictEqual(capture.asciicast.header.width, 80, tool); + assert.strictEqual(capture.asciicast.header.height, 30, tool); + } +}); + +test('captureAgentTui forwards renderer options and selects artifacts', async (t) => { + const directory = dirname(fileURLToPath(import.meta.url)); + const artifactDirectory = await mkdtemp(join(tmpdir(), 'agent-tui-options-')); + t.after(() => rm(artifactDirectory, { recursive: true, force: true })); + + await captureAgentTui({ + tool: 'opencode', + workingDirectory: process.cwd(), + executable: process.execPath, + prefixArgs: [join(directory, 'fixtures/fake-agent-tui.mjs'), 'opencode'], + prompt: 'hello', + promptAfter: 'ready:opencode', + startupInteractions: [{ after: 'trust:opencode', text: 'y', key: 'ENTER' }], + interactions: [ + { + after: 'waiting-resize', + resize: { cols: 81, rows: 31 }, + }, + ], + aspectRatio: 16 / 9, + artifactDirectory, + artifactOptions: { + borderRadius: 0, + cellWidth: 10, + cellHeight: 20, + fontSize: 16, + padding: 8, + background: '#010203', + foreground: '#fefdfc', + }, + artifacts: ['svg', 'gif'], + }); + + const recording = await readFile( + join(artifactDirectory, 'recording.svg'), + 'utf8' + ); + assert.match(recording, /rx="0"/u); + assert.match(recording, /fill="#010203"/u); + assert.match(recording, /font-size="16"/u); + await access(join(artifactDirectory, 'recording.gif')); + await assert.rejects(access(join(artifactDirectory, 'session.cast'))); + await assert.rejects(access(join(artifactDirectory, 'frames.json'))); + await assert.rejects(access(join(artifactDirectory, 'transcript.txt'))); +}); + test('normalizeTuiTranscript keeps repeated semantic events in order', () => { assert.deepStrictEqual( normalizeTuiTranscript( diff --git a/package.json b/package.json index e14e456..532da80 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "node": ">=20.0.0" }, "dependencies": { - "command-stream": "^0.15.0" + "command-stream": "^0.17.0" }, "repository": { "type": "git", From 108094339add3e527043a8b5ef0bb20c583d2265 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 25 Jul 2026 19:29:21 +0000 Subject: [PATCH 3/4] test(js): skip native PTY coverage under Deno --- js/test/tui.test.mjs | 126 +++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/js/test/tui.test.mjs b/js/test/tui.test.mjs index 96d72f8..a5faeed 100644 --- a/js/test/tui.test.mjs +++ b/js/test/tui.test.mjs @@ -161,19 +161,56 @@ test( } ); -test('captureAgentTui defaults every agent to publishable 4:3 geometry', async () => { - const directory = dirname(fileURLToPath(import.meta.url)); +test( + 'captureAgentTui defaults every agent to publishable 4:3 geometry', + { skip: isDeno, timeout: 30_000 }, + async () => { + const directory = dirname(fileURLToPath(import.meta.url)); - for (const tool of tools) { - const capture = await captureAgentTui({ - tool, + for (const tool of tools) { + const capture = await captureAgentTui({ + tool, + workingDirectory: process.cwd(), + executable: process.execPath, + prefixArgs: [join(directory, 'fixtures/fake-agent-tui.mjs'), tool], + prompt: 'hello', + promptAfter: `ready:${tool}`, + startupInteractions: [ + { after: `trust:${tool}`, text: 'y', key: 'ENTER' }, + ], + interactions: [ + { + after: 'waiting-resize', + resize: { cols: 81, rows: 31 }, + }, + ], + }); + + assert.strictEqual(capture.asciicast.header.width, 80, tool); + assert.strictEqual(capture.asciicast.header.height, 30, tool); + } + } +); + +test( + 'captureAgentTui forwards renderer options and selects artifacts', + { skip: isDeno }, + async (t) => { + const directory = dirname(fileURLToPath(import.meta.url)); + const artifactDirectory = await mkdtemp( + join(tmpdir(), 'agent-tui-options-') + ); + t.after(() => rm(artifactDirectory, { recursive: true, force: true })); + + await captureAgentTui({ + tool: 'opencode', workingDirectory: process.cwd(), executable: process.execPath, - prefixArgs: [join(directory, 'fixtures/fake-agent-tui.mjs'), tool], + prefixArgs: [join(directory, 'fixtures/fake-agent-tui.mjs'), 'opencode'], prompt: 'hello', - promptAfter: `ready:${tool}`, + promptAfter: 'ready:opencode', startupInteractions: [ - { after: `trust:${tool}`, text: 'y', key: 'ENTER' }, + { after: 'trust:opencode', text: 'y', key: 'ENTER' }, ], interactions: [ { @@ -181,58 +218,33 @@ test('captureAgentTui defaults every agent to publishable 4:3 geometry', async ( resize: { cols: 81, rows: 31 }, }, ], + aspectRatio: 16 / 9, + artifactDirectory, + artifactOptions: { + borderRadius: 0, + cellWidth: 10, + cellHeight: 20, + fontSize: 16, + padding: 8, + background: '#010203', + foreground: '#fefdfc', + }, + artifacts: ['svg', 'gif'], }); - assert.strictEqual(capture.asciicast.header.width, 80, tool); - assert.strictEqual(capture.asciicast.header.height, 30, tool); + const recording = await readFile( + join(artifactDirectory, 'recording.svg'), + 'utf8' + ); + assert.match(recording, /rx="0"/u); + assert.match(recording, /fill="#010203"/u); + assert.match(recording, /font-size="16"/u); + await access(join(artifactDirectory, 'recording.gif')); + await assert.rejects(access(join(artifactDirectory, 'session.cast'))); + await assert.rejects(access(join(artifactDirectory, 'frames.json'))); + await assert.rejects(access(join(artifactDirectory, 'transcript.txt'))); } -}); - -test('captureAgentTui forwards renderer options and selects artifacts', async (t) => { - const directory = dirname(fileURLToPath(import.meta.url)); - const artifactDirectory = await mkdtemp(join(tmpdir(), 'agent-tui-options-')); - t.after(() => rm(artifactDirectory, { recursive: true, force: true })); - - await captureAgentTui({ - tool: 'opencode', - workingDirectory: process.cwd(), - executable: process.execPath, - prefixArgs: [join(directory, 'fixtures/fake-agent-tui.mjs'), 'opencode'], - prompt: 'hello', - promptAfter: 'ready:opencode', - startupInteractions: [{ after: 'trust:opencode', text: 'y', key: 'ENTER' }], - interactions: [ - { - after: 'waiting-resize', - resize: { cols: 81, rows: 31 }, - }, - ], - aspectRatio: 16 / 9, - artifactDirectory, - artifactOptions: { - borderRadius: 0, - cellWidth: 10, - cellHeight: 20, - fontSize: 16, - padding: 8, - background: '#010203', - foreground: '#fefdfc', - }, - artifacts: ['svg', 'gif'], - }); - - const recording = await readFile( - join(artifactDirectory, 'recording.svg'), - 'utf8' - ); - assert.match(recording, /rx="0"/u); - assert.match(recording, /fill="#010203"/u); - assert.match(recording, /font-size="16"/u); - await access(join(artifactDirectory, 'recording.gif')); - await assert.rejects(access(join(artifactDirectory, 'session.cast'))); - await assert.rejects(access(join(artifactDirectory, 'frames.json'))); - await assert.rejects(access(join(artifactDirectory, 'transcript.txt'))); -}); +); test('normalizeTuiTranscript keeps repeated semantic events in order', () => { assert.deepStrictEqual( From c36c5c3c1c3c680f48c244642b04210257809bbd Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 25 Jul 2026 19:38:32 +0000 Subject: [PATCH 4/4] fix(js): patch Node GIF encoder interop --- .gitattributes | 1 + js/package-lock.json | 303 ++++++++++++++++++++++--- js/package.json | 5 +- js/patches/command-stream+0.17.0.patch | 20 ++ package.json | 7 +- 5 files changed, 298 insertions(+), 38 deletions(-) create mode 100644 .gitattributes create mode 100644 js/patches/command-stream+0.17.0.patch diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a0848b6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.patch whitespace=-blank-at-eol diff --git a/js/package-lock.json b/js/package-lock.json index d6a1cd9..8093031 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -9,7 +9,8 @@ "version": "0.9.1", "license": "Unlicense", "dependencies": { - "command-stream": "^0.17.0" + "command-stream": "^0.17.0", + "patch-package": "^8.0.1" }, "bin": { "start-agent": "bin/start-agent.mjs", @@ -1195,6 +1196,12 @@ "addons/*" ] }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "license": "BSD-2-Clause" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1275,7 +1282,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1380,7 +1386,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1399,11 +1404,28 @@ "node": ">= 0.8" } }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -1417,7 +1439,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -1444,7 +1465,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1478,7 +1498,6 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, "funding": [ { "type": "github", @@ -1589,7 +1608,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1602,7 +1620,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/colorette": { @@ -1670,7 +1687,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1706,6 +1722,23 @@ "dev": true, "license": "MIT" }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -1740,7 +1773,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -1799,7 +1831,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1809,7 +1840,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -1819,7 +1849,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2179,7 +2208,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2205,6 +2233,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -2245,7 +2282,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2268,7 +2304,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2293,7 +2328,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -2386,7 +2420,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2399,24 +2432,33 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2445,7 +2487,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2560,6 +2601,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-expression": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", @@ -2627,7 +2683,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -2695,11 +2750,28 @@ "node": ">=0.10.0" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/js-stringify": { @@ -2845,6 +2917,25 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", + "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -2862,6 +2953,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -2883,6 +2983,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3047,7 +3156,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3074,7 +3182,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -3120,6 +3227,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -3248,6 +3364,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3274,6 +3399,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3387,6 +3528,79 @@ "node": ">=6" } }, + "node_modules/patch-package": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^10.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.2.4", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/patch-package/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/patch-package/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-package/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3401,7 +3615,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3435,7 +3648,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -3898,7 +4110,6 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3907,11 +4118,27 @@ "node": ">=10" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -3924,7 +4151,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4100,7 +4326,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -4151,11 +4376,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -4218,7 +4451,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -4352,7 +4584,6 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/js/package.json b/js/package.json index 7396d0e..e2ee19e 100644 --- a/js/package.json +++ b/js/package.json @@ -14,6 +14,7 @@ "files": [ "src", "bin", + "patches", "README.md", "CHANGELOG.md" ], @@ -28,6 +29,7 @@ "format:check": "prettier --check .", "check:duplication": "jscpd .", "check": "npm run lint && npm run format:check && npm run check:duplication", + "postinstall": "patch-package", "prepare": "cd .. && husky js/.husky || true", "changeset": "changeset", "changeset:version": "node ../scripts/js/changeset-version.mjs", @@ -49,7 +51,8 @@ "node": ">=20.0.0" }, "dependencies": { - "command-stream": "^0.17.0" + "command-stream": "^0.17.0", + "patch-package": "^8.0.1" }, "devDependencies": { "@changesets/cli": "^2.29.7", diff --git a/js/patches/command-stream+0.17.0.patch b/js/patches/command-stream+0.17.0.patch new file mode 100644 index 0000000..58ddc92 --- /dev/null +++ b/js/patches/command-stream+0.17.0.patch @@ -0,0 +1,20 @@ +diff --git a/node_modules/command-stream/src/terminal-artifacts.mjs b/node_modules/command-stream/src/terminal-artifacts.mjs +index 6d2624b..aa9c495 100644 +--- a/node_modules/command-stream/src/terminal-artifacts.mjs ++++ b/node_modules/command-stream/src/terminal-artifacts.mjs +@@ -407,9 +407,12 @@ const renderRecordingSvg = ({ frames, options, font }) => { + }; + + const renderGif = async ({ frames, options, font }) => { +- const [{ Resvg }, { GIFEncoder, applyPalette, quantize }] = await Promise.all( +- [import('@resvg/resvg-js'), import('gifenc')] +- ); ++ const [{ Resvg }, gifencModule] = await Promise.all([ ++ import('@resvg/resvg-js'), ++ import('gifenc'), ++ ]); ++ const { GIFEncoder, applyPalette, quantize } = ++ gifencModule.GIFEncoder ? gifencModule : gifencModule.default; + const { width, height } = dimensions(frames, options); + const { times } = frameTimes(frames, options.idleTimeLimit); + const sheet = svgShell({ diff --git a/package.json b/package.json index 532da80..58913b7 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,13 @@ "files": [ "js/src", "js/bin", + "js/patches", "README.md", "LICENSE" ], + "scripts": { + "postinstall": "patch-package --patch-dir js/patches" + }, "keywords": [ "agent", "cli", @@ -31,7 +35,8 @@ "node": ">=20.0.0" }, "dependencies": { - "command-stream": "^0.17.0" + "command-stream": "^0.17.0", + "patch-package": "^8.0.1" }, "repository": { "type": "git",