@asterxsk/kiln 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +170 -170
- package/agent/AGENTS.md +67 -67
- package/agent/README.md +5 -5
- package/agent/extensions/AGENTS.md +68 -68
- package/agent/extensions/ask-user/index.ts +418 -418
- package/agent/extensions/ask-user/package-lock.json +769 -769
- package/agent/extensions/ask-user/package.json +19 -19
- package/agent/extensions/ask-user/prompt.ts +45 -45
- package/agent/extensions/ask-user/tsconfig.json +7 -7
- package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -942
- package/agent/extensions/background-terminals/index.ts +627 -627
- package/agent/extensions/background-terminals/manager.test.ts +735 -735
- package/agent/extensions/background-terminals/output.test.ts +109 -109
- package/agent/extensions/background-terminals/package-lock.json +769 -769
- package/agent/extensions/background-terminals/package.json +17 -17
- package/agent/extensions/background-terminals/prompt.test.ts +125 -125
- package/agent/extensions/background-terminals/ps.test.ts +82 -82
- package/agent/extensions/background-terminals/result-delivery.test.ts +44 -44
- package/agent/extensions/background-terminals/src/domain.ts +87 -87
- package/agent/extensions/background-terminals/src/manager.ts +907 -907
- package/agent/extensions/background-terminals/src/output.ts +84 -84
- package/agent/extensions/background-terminals/src/prompt.ts +142 -142
- package/agent/extensions/background-terminals/src/result-delivery.ts +27 -27
- package/agent/extensions/background-terminals/src/runtime.ts +36 -36
- package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -79
- package/agent/extensions/background-terminals/src/ui/ps.ts +621 -621
- package/agent/extensions/background-terminals/tsconfig.json +7 -7
- package/agent/extensions/file-search/index.spec.ts +443 -443
- package/agent/extensions/file-search/index.ts +459 -459
- package/agent/extensions/file-search/package-lock.json +2253 -2253
- package/agent/extensions/file-search/package.json +23 -23
- package/agent/extensions/file-search/src/args.ts +122 -122
- package/agent/extensions/file-search/src/binaries.ts +422 -422
- package/agent/extensions/file-search/src/output.ts +126 -126
- package/agent/extensions/file-search/src/process.ts +146 -146
- package/agent/extensions/file-search/src/prompt.ts +52 -52
- package/agent/extensions/file-search/tsconfig.json +7 -7
- package/agent/extensions/modelconf/PLAN.md +915 -915
- package/agent/extensions/modelconf/index.ts +296 -296
- package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -1101
- package/agent/extensions/pi-web-access/CHANGELOG.md +690 -690
- package/agent/extensions/pi-web-access/LICENSE +21 -21
- package/agent/extensions/pi-web-access/README.md +470 -470
- package/agent/extensions/pi-web-access/SECURITY.md +5 -5
- package/agent/extensions/pi-web-access/activity.ts +101 -101
- package/agent/extensions/pi-web-access/auth-fetch.ts +148 -148
- package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -272
- package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -669
- package/agent/extensions/pi-web-access/content-find.ts +139 -139
- package/agent/extensions/pi-web-access/credential-source.ts +191 -191
- package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -406
- package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -568
- package/agent/extensions/pi-web-access/declared-web-links.ts +173 -173
- package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -496
- package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -140
- package/agent/extensions/pi-web-access/exa.ts +526 -526
- package/agent/extensions/pi-web-access/extract.ts +1196 -1196
- package/agent/extensions/pi-web-access/feature-config.ts +29 -29
- package/agent/extensions/pi-web-access/fetch-params.ts +111 -111
- package/agent/extensions/pi-web-access/gemini-adc.ts +298 -298
- package/agent/extensions/pi-web-access/gemini-api.ts +353 -353
- package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -108
- package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -128
- package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -101
- package/agent/extensions/pi-web-access/gemini-web.ts +487 -487
- package/agent/extensions/pi-web-access/github-api.ts +197 -197
- package/agent/extensions/pi-web-access/github-extract.ts +746 -746
- package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -700
- package/agent/extensions/pi-web-access/index.ts +1737 -1737
- package/agent/extensions/pi-web-access/package-lock.json +5808 -5808
- package/agent/extensions/pi-web-access/package.json +64 -64
- package/agent/extensions/pi-web-access/page-query.ts +96 -96
- package/agent/extensions/pi-web-access/pdf-extract.ts +409 -409
- package/agent/extensions/pi-web-access/promise-try.d.ts +7 -7
- package/agent/extensions/pi-web-access/query-rewrite.ts +51 -51
- package/agent/extensions/pi-web-access/render-search-error.ts +170 -170
- package/agent/extensions/pi-web-access/rsc-extract.ts +338 -338
- package/agent/extensions/pi-web-access/source-check.ts +282 -282
- package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -526
- package/agent/extensions/pi-web-access/storage.ts +521 -521
- package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -125
- package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -208
- package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -840
- package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -441
- package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -283
- package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -25
- package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -210
- package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -552
- package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -212
- package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -40
- package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -334
- package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -95
- package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -53
- package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -92
- package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -86
- package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -34
- package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -173
- package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -257
- package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -170
- package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -133
- package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -178
- package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -148
- package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -223
- package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -378
- package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -565
- package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -99
- package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -50
- package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -51
- package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -140
- package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -500
- package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -286
- package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -102
- package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -152
- package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -274
- package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -179
- package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -205
- package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -456
- package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -182
- package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -64
- package/agent/extensions/pi-web-access/tsconfig.json +11 -11
- package/agent/extensions/pi-web-access/utils.ts +451 -451
- package/agent/extensions/pi-web-access/video-extract.ts +392 -392
- package/agent/extensions/pi-web-access/youtube-extract.ts +328 -328
- package/agent/extensions/shared/activity-status.ts +31 -31
- package/agent/extensions/shared/child-session.test.ts +270 -270
- package/agent/extensions/shared/child-session.ts +148 -148
- package/agent/extensions/shared/context-utilization.test.ts +48 -48
- package/agent/extensions/shared/context-utilization.ts +47 -47
- package/agent/extensions/shared/dashboard-state.ts +99 -99
- package/agent/extensions/shared/tool-call-timeout.test.ts +117 -117
- package/agent/extensions/shared/tool-call-timeout.ts +104 -104
- package/agent/extensions/subagents/by-the-way.test.ts +29 -29
- package/agent/extensions/subagents/claude.test.ts +119 -119
- package/agent/extensions/subagents/codex.test.ts +102 -102
- package/agent/extensions/subagents/context-usage.test.ts +107 -107
- package/agent/extensions/subagents/docs/design-plan.md +568 -568
- package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -354
- package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -571
- package/agent/extensions/subagents/index.ts +779 -779
- package/agent/extensions/subagents/manager.test.ts +276 -276
- package/agent/extensions/subagents/package-lock.json +2244 -2244
- package/agent/extensions/subagents/package.json +19 -19
- package/agent/extensions/subagents/result-delivery.test.ts +27 -27
- package/agent/extensions/subagents/src/backend.ts +73 -73
- package/agent/extensions/subagents/src/backends/claude.ts +701 -701
- package/agent/extensions/subagents/src/backends/codex.ts +1060 -1060
- package/agent/extensions/subagents/src/backends/pi.ts +575 -575
- package/agent/extensions/subagents/src/backends/stub.ts +300 -300
- package/agent/extensions/subagents/src/by-the-way.ts +21 -21
- package/agent/extensions/subagents/src/domain.ts +253 -253
- package/agent/extensions/subagents/src/format.ts +74 -74
- package/agent/extensions/subagents/src/manager.ts +736 -736
- package/agent/extensions/subagents/src/prompt.ts +92 -92
- package/agent/extensions/subagents/src/result-delivery.ts +20 -20
- package/agent/extensions/subagents/src/runtime.ts +53 -53
- package/agent/extensions/subagents/src/ui/takeover.ts +583 -583
- package/agent/extensions/subagents/src/ui/transcript.ts +201 -201
- package/agent/extensions/subagents/takeover.test.ts +29 -29
- package/agent/extensions/subagents/tsconfig.json +7 -7
- package/agent/extensions/todo/AGENTS.md +38 -38
- package/agent/extensions/todo/LICENSE +21 -21
- package/agent/extensions/todo/config.ts +55 -55
- package/agent/extensions/todo/index.ts +151 -151
- package/agent/extensions/todo/locales/de.json +17 -17
- package/agent/extensions/todo/locales/en.json +15 -15
- package/agent/extensions/todo/locales/es.json +17 -17
- package/agent/extensions/todo/locales/fr.json +17 -17
- package/agent/extensions/todo/locales/pt-BR.json +17 -17
- package/agent/extensions/todo/locales/pt.json +17 -17
- package/agent/extensions/todo/locales/ru.json +17 -17
- package/agent/extensions/todo/locales/uk.json +17 -17
- package/agent/extensions/todo/locales/zh.json +17 -17
- package/agent/extensions/todo/package-lock.json +3358 -3358
- package/agent/extensions/todo/package.json +67 -67
- package/agent/extensions/todo/state/i18n-bridge.ts +64 -64
- package/agent/extensions/todo/state/invariants.ts +20 -20
- package/agent/extensions/todo/state/replay.ts +38 -38
- package/agent/extensions/todo/state/selectors.ts +107 -107
- package/agent/extensions/todo/state/state-reducer.ts +326 -326
- package/agent/extensions/todo/state/state.ts +18 -18
- package/agent/extensions/todo/state/store.ts +82 -82
- package/agent/extensions/todo/state/task-graph.ts +57 -57
- package/agent/extensions/todo/todo-overlay.ts +200 -200
- package/agent/extensions/todo/todo.ts +155 -155
- package/agent/extensions/todo/tool/response-envelope.ts +109 -109
- package/agent/extensions/todo/tool/types.ts +206 -206
- package/agent/extensions/todo/view/format.ts +177 -177
- package/agent/install.ps1 +637 -527
- package/agent/install.sh +620 -511
- package/agent/keybindings.json +7 -7
- package/bin/kiln.js +124 -11
- package/package.json +8 -2
- package/agent/extensions/taste/index.ts +0 -443
- package/agent/extensions/taste/install.ps1 +0 -23
- package/agent/extensions/taste/install.sh +0 -21
- /package/agent/extensions/{status line → statusline}/index.ts +0 -0
- /package/agent/extensions/{status line → statusline}/install.ps1 +0 -0
- /package/agent/extensions/{status line → statusline}/install.sh +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "background-terminals",
|
|
3
|
-
"private": true,
|
|
4
|
-
"type": "module",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"check": "tsc --noEmit -p .",
|
|
7
|
-
"prepare": "effect-tsgo patch",
|
|
8
|
-
"test": "node --test --experimental-strip-types manager.test.ts output.test.ts prompt.test.ts result-delivery.test.ts ps.test.ts"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"effect": "^4.0.0-beta.99"
|
|
12
|
-
},
|
|
13
|
-
"devDependencies": {
|
|
14
|
-
"@effect/tsgo": "^0.24.2",
|
|
15
|
-
"typescript": "^7.0.2"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "background-terminals",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"check": "tsc --noEmit -p .",
|
|
7
|
+
"prepare": "effect-tsgo patch",
|
|
8
|
+
"test": "node --test --experimental-strip-types manager.test.ts output.test.ts prompt.test.ts result-delivery.test.ts ps.test.ts"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"effect": "^4.0.0-beta.99"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@effect/tsgo": "^0.24.2",
|
|
15
|
+
"typescript": "^7.0.2"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import type { OutputView, TerminalSnapshot } from "./src/domain.ts";
|
|
4
|
-
import {
|
|
5
|
-
BG_START_PARAMETER_DESCRIPTIONS,
|
|
6
|
-
BG_START_TOOL_DESCRIPTION,
|
|
7
|
-
buildKillReport,
|
|
8
|
-
buildStatusResult,
|
|
9
|
-
buildTerminalResultMessage,
|
|
10
|
-
} from "./src/prompt.ts";
|
|
11
|
-
|
|
12
|
-
test("start descriptions identify the platform-specific shell contract", () => {
|
|
13
|
-
assert.match(BG_START_TOOL_DESCRIPTION, /sh -c on POSIX/);
|
|
14
|
-
assert.match(BG_START_TOOL_DESCRIPTION, /cmd\.exe \/d \/s \/c on Windows/);
|
|
15
|
-
assert.match(BG_START_PARAMETER_DESCRIPTIONS.command, /sh -c on POSIX/);
|
|
16
|
-
assert.match(
|
|
17
|
-
BG_START_PARAMETER_DESCRIPTIONS.command,
|
|
18
|
-
/cmd\.exe \/d \/s \/c on Windows/,
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function view(overrides: Partial<OutputView> = {}): OutputView {
|
|
23
|
-
return { text: "", totalBytes: 0, truncatedBytes: 0, ...overrides };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function snap(overrides: Partial<TerminalSnapshot> = {}): TerminalSnapshot {
|
|
27
|
-
return {
|
|
28
|
-
id: "bt-1",
|
|
29
|
-
command: "sleep 999",
|
|
30
|
-
title: "test",
|
|
31
|
-
cwd: "/tmp",
|
|
32
|
-
pid: 123,
|
|
33
|
-
status: "done",
|
|
34
|
-
createdAt: Date.now() - 5_000,
|
|
35
|
-
settledAt: Date.now(),
|
|
36
|
-
exitCode: 0,
|
|
37
|
-
stdout: view(),
|
|
38
|
-
stderr: view(),
|
|
39
|
-
...overrides,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
test("kill report distinguishes killed / raced natural exit / already settled", () => {
|
|
44
|
-
const report = buildKillReport([
|
|
45
|
-
{
|
|
46
|
-
id: "bt-1",
|
|
47
|
-
title: "a",
|
|
48
|
-
status: "killed",
|
|
49
|
-
wasRunning: true,
|
|
50
|
-
killed: true,
|
|
51
|
-
exit: "SIGTERM",
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: "bt-2",
|
|
55
|
-
title: "b",
|
|
56
|
-
status: "done",
|
|
57
|
-
wasRunning: true,
|
|
58
|
-
killed: false,
|
|
59
|
-
exit: "exit 0",
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: "bt-3",
|
|
63
|
-
title: "c",
|
|
64
|
-
status: "failed",
|
|
65
|
-
wasRunning: false,
|
|
66
|
-
killed: false,
|
|
67
|
-
exit: "exit 1",
|
|
68
|
-
},
|
|
69
|
-
]);
|
|
70
|
-
const lines = report.split("\n");
|
|
71
|
-
assert.equal(lines[0], 'Killed bt-1 "a" (SIGTERM).');
|
|
72
|
-
assert.match(lines[1], /exited on its own before the kill landed \(exit 0\)/);
|
|
73
|
-
assert.match(lines[2], /was already failed \(exit 1\)/);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("status result marks head-truncated output with a pointer at the full log", () => {
|
|
77
|
-
const text = buildStatusResult(
|
|
78
|
-
snap({
|
|
79
|
-
stdout: view({
|
|
80
|
-
text: "tail of the log\n",
|
|
81
|
-
totalBytes: 5 * 1024 * 1024,
|
|
82
|
-
truncatedBytes: 5 * 1024 * 1024 - 16,
|
|
83
|
-
spillPath: "/tmp/bt-1.stdout.log",
|
|
84
|
-
}),
|
|
85
|
-
}),
|
|
86
|
-
);
|
|
87
|
-
assert.match(text, /stdout truncated: showing last /);
|
|
88
|
-
assert.match(text, /Full log: \/tmp\/bt-1\.stdout\.log/);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("completion message reports kill vs exit and omits empty stderr", () => {
|
|
92
|
-
const killed = buildTerminalResultMessage(
|
|
93
|
-
snap({ status: "killed", exitCode: undefined, signal: "SIGTERM" }),
|
|
94
|
-
);
|
|
95
|
-
assert.match(killed, /was killed after/);
|
|
96
|
-
assert.ok(!killed.includes("stderr"), "empty stderr section omitted");
|
|
97
|
-
|
|
98
|
-
const failed = buildTerminalResultMessage(
|
|
99
|
-
snap({
|
|
100
|
-
status: "failed",
|
|
101
|
-
exitCode: 3,
|
|
102
|
-
stderr: view({ text: "boom\n", totalBytes: 5 }),
|
|
103
|
-
}),
|
|
104
|
-
);
|
|
105
|
-
assert.match(failed, /exited \(exit 3\)/);
|
|
106
|
-
assert.match(failed, /stderr:\nboom/);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("completion output is a shorter tail than the detailed status view", () => {
|
|
110
|
-
const output = Array.from(
|
|
111
|
-
{ length: 100 },
|
|
112
|
-
(_, index) => `line-${index + 1}`,
|
|
113
|
-
).join("\n");
|
|
114
|
-
const terminal = snap({
|
|
115
|
-
stdout: view({ text: output, totalBytes: Buffer.byteLength(output) }),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const completion = buildTerminalResultMessage(terminal);
|
|
119
|
-
const status = buildStatusResult(terminal);
|
|
120
|
-
|
|
121
|
-
assert.ok(!completion.includes("line-1\n"));
|
|
122
|
-
assert.match(completion, /line-100/);
|
|
123
|
-
assert.match(completion, /stdout truncated/);
|
|
124
|
-
assert.match(status, /line-1\n/);
|
|
125
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import type { OutputView, TerminalSnapshot } from "./src/domain.ts";
|
|
4
|
+
import {
|
|
5
|
+
BG_START_PARAMETER_DESCRIPTIONS,
|
|
6
|
+
BG_START_TOOL_DESCRIPTION,
|
|
7
|
+
buildKillReport,
|
|
8
|
+
buildStatusResult,
|
|
9
|
+
buildTerminalResultMessage,
|
|
10
|
+
} from "./src/prompt.ts";
|
|
11
|
+
|
|
12
|
+
test("start descriptions identify the platform-specific shell contract", () => {
|
|
13
|
+
assert.match(BG_START_TOOL_DESCRIPTION, /sh -c on POSIX/);
|
|
14
|
+
assert.match(BG_START_TOOL_DESCRIPTION, /cmd\.exe \/d \/s \/c on Windows/);
|
|
15
|
+
assert.match(BG_START_PARAMETER_DESCRIPTIONS.command, /sh -c on POSIX/);
|
|
16
|
+
assert.match(
|
|
17
|
+
BG_START_PARAMETER_DESCRIPTIONS.command,
|
|
18
|
+
/cmd\.exe \/d \/s \/c on Windows/,
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function view(overrides: Partial<OutputView> = {}): OutputView {
|
|
23
|
+
return { text: "", totalBytes: 0, truncatedBytes: 0, ...overrides };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function snap(overrides: Partial<TerminalSnapshot> = {}): TerminalSnapshot {
|
|
27
|
+
return {
|
|
28
|
+
id: "bt-1",
|
|
29
|
+
command: "sleep 999",
|
|
30
|
+
title: "test",
|
|
31
|
+
cwd: "/tmp",
|
|
32
|
+
pid: 123,
|
|
33
|
+
status: "done",
|
|
34
|
+
createdAt: Date.now() - 5_000,
|
|
35
|
+
settledAt: Date.now(),
|
|
36
|
+
exitCode: 0,
|
|
37
|
+
stdout: view(),
|
|
38
|
+
stderr: view(),
|
|
39
|
+
...overrides,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
test("kill report distinguishes killed / raced natural exit / already settled", () => {
|
|
44
|
+
const report = buildKillReport([
|
|
45
|
+
{
|
|
46
|
+
id: "bt-1",
|
|
47
|
+
title: "a",
|
|
48
|
+
status: "killed",
|
|
49
|
+
wasRunning: true,
|
|
50
|
+
killed: true,
|
|
51
|
+
exit: "SIGTERM",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "bt-2",
|
|
55
|
+
title: "b",
|
|
56
|
+
status: "done",
|
|
57
|
+
wasRunning: true,
|
|
58
|
+
killed: false,
|
|
59
|
+
exit: "exit 0",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "bt-3",
|
|
63
|
+
title: "c",
|
|
64
|
+
status: "failed",
|
|
65
|
+
wasRunning: false,
|
|
66
|
+
killed: false,
|
|
67
|
+
exit: "exit 1",
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
const lines = report.split("\n");
|
|
71
|
+
assert.equal(lines[0], 'Killed bt-1 "a" (SIGTERM).');
|
|
72
|
+
assert.match(lines[1], /exited on its own before the kill landed \(exit 0\)/);
|
|
73
|
+
assert.match(lines[2], /was already failed \(exit 1\)/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("status result marks head-truncated output with a pointer at the full log", () => {
|
|
77
|
+
const text = buildStatusResult(
|
|
78
|
+
snap({
|
|
79
|
+
stdout: view({
|
|
80
|
+
text: "tail of the log\n",
|
|
81
|
+
totalBytes: 5 * 1024 * 1024,
|
|
82
|
+
truncatedBytes: 5 * 1024 * 1024 - 16,
|
|
83
|
+
spillPath: "/tmp/bt-1.stdout.log",
|
|
84
|
+
}),
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
assert.match(text, /stdout truncated: showing last /);
|
|
88
|
+
assert.match(text, /Full log: \/tmp\/bt-1\.stdout\.log/);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("completion message reports kill vs exit and omits empty stderr", () => {
|
|
92
|
+
const killed = buildTerminalResultMessage(
|
|
93
|
+
snap({ status: "killed", exitCode: undefined, signal: "SIGTERM" }),
|
|
94
|
+
);
|
|
95
|
+
assert.match(killed, /was killed after/);
|
|
96
|
+
assert.ok(!killed.includes("stderr"), "empty stderr section omitted");
|
|
97
|
+
|
|
98
|
+
const failed = buildTerminalResultMessage(
|
|
99
|
+
snap({
|
|
100
|
+
status: "failed",
|
|
101
|
+
exitCode: 3,
|
|
102
|
+
stderr: view({ text: "boom\n", totalBytes: 5 }),
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
assert.match(failed, /exited \(exit 3\)/);
|
|
106
|
+
assert.match(failed, /stderr:\nboom/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("completion output is a shorter tail than the detailed status view", () => {
|
|
110
|
+
const output = Array.from(
|
|
111
|
+
{ length: 100 },
|
|
112
|
+
(_, index) => `line-${index + 1}`,
|
|
113
|
+
).join("\n");
|
|
114
|
+
const terminal = snap({
|
|
115
|
+
stdout: view({ text: output, totalBytes: Buffer.byteLength(output) }),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const completion = buildTerminalResultMessage(terminal);
|
|
119
|
+
const status = buildStatusResult(terminal);
|
|
120
|
+
|
|
121
|
+
assert.ok(!completion.includes("line-1\n"));
|
|
122
|
+
assert.match(completion, /line-100/);
|
|
123
|
+
assert.match(completion, /stdout truncated/);
|
|
124
|
+
assert.match(status, /line-1\n/);
|
|
125
|
+
});
|
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
reconcileDashboardSelection,
|
|
5
|
-
type DashboardSelection,
|
|
6
|
-
} from "./src/ui/ps.ts";
|
|
7
|
-
import {
|
|
8
|
-
buildOutputLines,
|
|
9
|
-
createOutputLineCache,
|
|
10
|
-
sanitizeText,
|
|
11
|
-
} from "./src/ui/output-view.ts";
|
|
12
|
-
|
|
13
|
-
test("dashboard selection follows its terminal id and falls back by row", () => {
|
|
14
|
-
const selection: DashboardSelection = { id: "bt-7", index: 6 };
|
|
15
|
-
|
|
16
|
-
reconcileDashboardSelection(selection, [
|
|
17
|
-
{ id: "bt-new" },
|
|
18
|
-
...Array.from({ length: 8 }, (_, index) => ({ id: `bt-${index + 1}` })),
|
|
19
|
-
]);
|
|
20
|
-
assert.deepEqual(selection, { id: "bt-7", index: 7 });
|
|
21
|
-
|
|
22
|
-
reconcileDashboardSelection(selection, [
|
|
23
|
-
...Array.from({ length: 6 }, (_, index) => ({ id: `bt-${index + 1}` })),
|
|
24
|
-
{ id: "bt-8" },
|
|
25
|
-
{ id: "bt-9" },
|
|
26
|
-
]);
|
|
27
|
-
assert.deepEqual(selection, { id: "bt-9", index: 7 });
|
|
28
|
-
|
|
29
|
-
reconcileDashboardSelection(selection, [{ id: "bt-1" }, { id: "bt-2" }]);
|
|
30
|
-
assert.deepEqual(selection, { id: "bt-2", index: 1 });
|
|
31
|
-
|
|
32
|
-
reconcileDashboardSelection(selection, []);
|
|
33
|
-
assert.deepEqual(selection, { id: undefined, index: 0 });
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("sanitizeText strips ANSI, tabs, and control characters", () => {
|
|
37
|
-
assert.equal(sanitizeText("\u001b[31mred\u001b[0m"), "red");
|
|
38
|
-
assert.equal(sanitizeText("\u001b[12345Cshifted"), "shifted");
|
|
39
|
-
assert.equal(sanitizeText("\u001b]0;window title\u0007output"), "output");
|
|
40
|
-
assert.equal(
|
|
41
|
-
sanitizeText("\u001b]8;;https://example.com\u001b\\link\u001b]8;;\u001b\\"),
|
|
42
|
-
"link",
|
|
43
|
-
);
|
|
44
|
-
assert.equal(sanitizeText("\u001b]0;title\u009coutput"), "output");
|
|
45
|
-
assert.equal(sanitizeText("\u009d0;title\u0007output"), "output");
|
|
46
|
-
assert.equal(sanitizeText("a\u0085b"), "ab");
|
|
47
|
-
assert.equal(sanitizeText("a\tb"), "a b");
|
|
48
|
-
assert.equal(sanitizeText("a\u0007b\u0000c"), "abc");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("output line cache reuses a version/width key and invalidates either dimension", () => {
|
|
52
|
-
const cache = createOutputLineCache();
|
|
53
|
-
const first = cache.get("first", 1, 80);
|
|
54
|
-
const sameKey = cache.get("different text is intentionally ignored", 1, 80);
|
|
55
|
-
assert.equal(sameKey, first);
|
|
56
|
-
assert.deepEqual(sameKey, ["first"]);
|
|
57
|
-
|
|
58
|
-
const newVersion = cache.get("second", 2, 80);
|
|
59
|
-
assert.notEqual(newVersion, first);
|
|
60
|
-
assert.deepEqual(newVersion, ["second"]);
|
|
61
|
-
|
|
62
|
-
const newWidth = cache.get("x".repeat(25), 2, 10);
|
|
63
|
-
assert.notEqual(newWidth, newVersion);
|
|
64
|
-
assert.ok(newWidth.length > 1);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("buildOutputLines wraps long lines and keeps only the final CR segment", () => {
|
|
68
|
-
const lines = buildOutputLines("progress 1\rprogress 2\rdone\nnext", 80);
|
|
69
|
-
assert.deepEqual(lines, ["done", "next"]);
|
|
70
|
-
assert.deepEqual(buildOutputLines("progress 1\rprogress 2\r", 80), [
|
|
71
|
-
"progress 2",
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
const wrapped = buildOutputLines("x".repeat(25), 10);
|
|
75
|
-
assert.ok(wrapped.length > 1);
|
|
76
|
-
assert.equal(wrapped.join(""), "x".repeat(25));
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("buildOutputLines drops one trailing empty line from a trailing newline", () => {
|
|
80
|
-
assert.deepEqual(buildOutputLines("a\nb\n", 80), ["a", "b"]);
|
|
81
|
-
assert.deepEqual(buildOutputLines("a\n\n", 80), ["a", ""]);
|
|
82
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
reconcileDashboardSelection,
|
|
5
|
+
type DashboardSelection,
|
|
6
|
+
} from "./src/ui/ps.ts";
|
|
7
|
+
import {
|
|
8
|
+
buildOutputLines,
|
|
9
|
+
createOutputLineCache,
|
|
10
|
+
sanitizeText,
|
|
11
|
+
} from "./src/ui/output-view.ts";
|
|
12
|
+
|
|
13
|
+
test("dashboard selection follows its terminal id and falls back by row", () => {
|
|
14
|
+
const selection: DashboardSelection = { id: "bt-7", index: 6 };
|
|
15
|
+
|
|
16
|
+
reconcileDashboardSelection(selection, [
|
|
17
|
+
{ id: "bt-new" },
|
|
18
|
+
...Array.from({ length: 8 }, (_, index) => ({ id: `bt-${index + 1}` })),
|
|
19
|
+
]);
|
|
20
|
+
assert.deepEqual(selection, { id: "bt-7", index: 7 });
|
|
21
|
+
|
|
22
|
+
reconcileDashboardSelection(selection, [
|
|
23
|
+
...Array.from({ length: 6 }, (_, index) => ({ id: `bt-${index + 1}` })),
|
|
24
|
+
{ id: "bt-8" },
|
|
25
|
+
{ id: "bt-9" },
|
|
26
|
+
]);
|
|
27
|
+
assert.deepEqual(selection, { id: "bt-9", index: 7 });
|
|
28
|
+
|
|
29
|
+
reconcileDashboardSelection(selection, [{ id: "bt-1" }, { id: "bt-2" }]);
|
|
30
|
+
assert.deepEqual(selection, { id: "bt-2", index: 1 });
|
|
31
|
+
|
|
32
|
+
reconcileDashboardSelection(selection, []);
|
|
33
|
+
assert.deepEqual(selection, { id: undefined, index: 0 });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("sanitizeText strips ANSI, tabs, and control characters", () => {
|
|
37
|
+
assert.equal(sanitizeText("\u001b[31mred\u001b[0m"), "red");
|
|
38
|
+
assert.equal(sanitizeText("\u001b[12345Cshifted"), "shifted");
|
|
39
|
+
assert.equal(sanitizeText("\u001b]0;window title\u0007output"), "output");
|
|
40
|
+
assert.equal(
|
|
41
|
+
sanitizeText("\u001b]8;;https://example.com\u001b\\link\u001b]8;;\u001b\\"),
|
|
42
|
+
"link",
|
|
43
|
+
);
|
|
44
|
+
assert.equal(sanitizeText("\u001b]0;title\u009coutput"), "output");
|
|
45
|
+
assert.equal(sanitizeText("\u009d0;title\u0007output"), "output");
|
|
46
|
+
assert.equal(sanitizeText("a\u0085b"), "ab");
|
|
47
|
+
assert.equal(sanitizeText("a\tb"), "a b");
|
|
48
|
+
assert.equal(sanitizeText("a\u0007b\u0000c"), "abc");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("output line cache reuses a version/width key and invalidates either dimension", () => {
|
|
52
|
+
const cache = createOutputLineCache();
|
|
53
|
+
const first = cache.get("first", 1, 80);
|
|
54
|
+
const sameKey = cache.get("different text is intentionally ignored", 1, 80);
|
|
55
|
+
assert.equal(sameKey, first);
|
|
56
|
+
assert.deepEqual(sameKey, ["first"]);
|
|
57
|
+
|
|
58
|
+
const newVersion = cache.get("second", 2, 80);
|
|
59
|
+
assert.notEqual(newVersion, first);
|
|
60
|
+
assert.deepEqual(newVersion, ["second"]);
|
|
61
|
+
|
|
62
|
+
const newWidth = cache.get("x".repeat(25), 2, 10);
|
|
63
|
+
assert.notEqual(newWidth, newVersion);
|
|
64
|
+
assert.ok(newWidth.length > 1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("buildOutputLines wraps long lines and keeps only the final CR segment", () => {
|
|
68
|
+
const lines = buildOutputLines("progress 1\rprogress 2\rdone\nnext", 80);
|
|
69
|
+
assert.deepEqual(lines, ["done", "next"]);
|
|
70
|
+
assert.deepEqual(buildOutputLines("progress 1\rprogress 2\r", 80), [
|
|
71
|
+
"progress 2",
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const wrapped = buildOutputLines("x".repeat(25), 10);
|
|
75
|
+
assert.ok(wrapped.length > 1);
|
|
76
|
+
assert.equal(wrapped.join(""), "x".repeat(25));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("buildOutputLines drops one trailing empty line from a trailing newline", () => {
|
|
80
|
+
assert.deepEqual(buildOutputLines("a\nb\n", 80), ["a", "b"]);
|
|
81
|
+
assert.deepEqual(buildOutputLines("a\n\n", 80), ["a", ""]);
|
|
82
|
+
});
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { createDeferredResultDelivery } from "./src/result-delivery.ts";
|
|
4
|
-
|
|
5
|
-
test("a result consumed by a kill/status is not delivered", () => {
|
|
6
|
-
const delivery = createDeferredResultDelivery<{
|
|
7
|
-
id: string;
|
|
8
|
-
output: string;
|
|
9
|
-
}>();
|
|
10
|
-
|
|
11
|
-
delivery.defer({ id: "bt-1", output: "done" });
|
|
12
|
-
delivery.consume(["bt-1"]);
|
|
13
|
-
|
|
14
|
-
assert.deepEqual(delivery.drain(), []);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("unconsumed results are delivered once in settlement order", () => {
|
|
18
|
-
const delivery = createDeferredResultDelivery<{ id: string }>();
|
|
19
|
-
const first = { id: "bt-1" };
|
|
20
|
-
const second = { id: "bt-2" };
|
|
21
|
-
|
|
22
|
-
delivery.defer(first);
|
|
23
|
-
delivery.defer(second);
|
|
24
|
-
|
|
25
|
-
assert.deepEqual(delivery.drain(), [first, second]);
|
|
26
|
-
assert.deepEqual(delivery.drain(), []);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("re-deferring the same id replaces rather than duplicates", () => {
|
|
30
|
-
const delivery = createDeferredResultDelivery<{ id: string; n: number }>();
|
|
31
|
-
delivery.defer({ id: "bt-1", n: 1 });
|
|
32
|
-
delivery.defer({ id: "bt-1", n: 2 });
|
|
33
|
-
assert.deepEqual(delivery.drain(), [{ id: "bt-1", n: 2 }]);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("a drained result can be retained for retry after delivery fails", () => {
|
|
37
|
-
const delivery = createDeferredResultDelivery<{ id: string }>();
|
|
38
|
-
const result = { id: "bt-1" };
|
|
39
|
-
delivery.defer(result);
|
|
40
|
-
|
|
41
|
-
for (const drained of delivery.drain()) delivery.defer(drained);
|
|
42
|
-
|
|
43
|
-
assert.deepEqual(delivery.drain(), [result]);
|
|
44
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createDeferredResultDelivery } from "./src/result-delivery.ts";
|
|
4
|
+
|
|
5
|
+
test("a result consumed by a kill/status is not delivered", () => {
|
|
6
|
+
const delivery = createDeferredResultDelivery<{
|
|
7
|
+
id: string;
|
|
8
|
+
output: string;
|
|
9
|
+
}>();
|
|
10
|
+
|
|
11
|
+
delivery.defer({ id: "bt-1", output: "done" });
|
|
12
|
+
delivery.consume(["bt-1"]);
|
|
13
|
+
|
|
14
|
+
assert.deepEqual(delivery.drain(), []);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("unconsumed results are delivered once in settlement order", () => {
|
|
18
|
+
const delivery = createDeferredResultDelivery<{ id: string }>();
|
|
19
|
+
const first = { id: "bt-1" };
|
|
20
|
+
const second = { id: "bt-2" };
|
|
21
|
+
|
|
22
|
+
delivery.defer(first);
|
|
23
|
+
delivery.defer(second);
|
|
24
|
+
|
|
25
|
+
assert.deepEqual(delivery.drain(), [first, second]);
|
|
26
|
+
assert.deepEqual(delivery.drain(), []);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("re-deferring the same id replaces rather than duplicates", () => {
|
|
30
|
+
const delivery = createDeferredResultDelivery<{ id: string; n: number }>();
|
|
31
|
+
delivery.defer({ id: "bt-1", n: 1 });
|
|
32
|
+
delivery.defer({ id: "bt-1", n: 2 });
|
|
33
|
+
assert.deepEqual(delivery.drain(), [{ id: "bt-1", n: 2 }]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("a drained result can be retained for retry after delivery fails", () => {
|
|
37
|
+
const delivery = createDeferredResultDelivery<{ id: string }>();
|
|
38
|
+
const result = { id: "bt-1" };
|
|
39
|
+
delivery.defer(result);
|
|
40
|
+
|
|
41
|
+
for (const drained of delivery.drain()) delivery.defer(drained);
|
|
42
|
+
|
|
43
|
+
assert.deepEqual(delivery.drain(), [result]);
|
|
44
|
+
});
|