@code-yeongyu/senpi-agent-core 2026.9.12 → 2026.9.13
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/README.md +4 -0
- package/dist/harness/agent-harness.d.ts +639 -384
- package/dist/harness/agent-harness.d.ts.map +1 -1
- package/dist/harness/agent-harness.js +5 -250
- package/dist/harness/agent-harness.js.map +1 -1
- package/dist/harness/compaction/branch-summarization.d.ts +12 -6
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/harness/compaction/branch-summarization.js +33 -27
- package/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/dist/harness/compaction/compaction.d.ts +30 -8
- package/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/dist/harness/compaction/compaction.js +53 -64
- package/dist/harness/compaction/compaction.js.map +1 -1
- package/dist/harness/config.d.ts +9 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +35 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/context.d.ts +9 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +13 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/env/nodejs.d.ts +25 -28
- package/dist/harness/env/nodejs.d.ts.map +1 -1
- package/dist/harness/env/nodejs.js +368 -125
- package/dist/harness/env/nodejs.js.map +1 -1
- package/dist/harness/events.d.ts +20 -38
- package/dist/harness/events.d.ts.map +1 -1
- package/dist/harness/events.js +233 -55
- package/dist/harness/events.js.map +1 -1
- package/dist/harness/execution/assistant.d.ts +42 -0
- package/dist/harness/execution/assistant.d.ts.map +1 -0
- package/dist/harness/execution/assistant.js +82 -0
- package/dist/harness/execution/assistant.js.map +1 -0
- package/dist/harness/execution/effect-gate.d.ts +22 -0
- package/dist/harness/execution/effect-gate.d.ts.map +1 -0
- package/dist/harness/execution/effect-gate.js +48 -0
- package/dist/harness/execution/effect-gate.js.map +1 -0
- package/dist/harness/execution/tools.d.ts +67 -0
- package/dist/harness/execution/tools.d.ts.map +1 -0
- package/dist/harness/execution/tools.js +121 -0
- package/dist/harness/execution/tools.js.map +1 -0
- package/dist/harness/hooks.d.ts +38 -0
- package/dist/harness/hooks.d.ts.map +1 -0
- package/dist/harness/hooks.js +404 -0
- package/dist/harness/hooks.js.map +1 -0
- package/dist/harness/messages.d.ts +2 -2
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/prompt-templates.d.ts +3 -2
- package/dist/harness/prompt-templates.d.ts.map +1 -1
- package/dist/harness/prompt-templates.js +17 -17
- package/dist/harness/prompt-templates.js.map +1 -1
- package/dist/harness/result.d.ts +93 -0
- package/dist/harness/result.d.ts.map +1 -1
- package/dist/harness/result.js +39 -0
- package/dist/harness/result.js.map +1 -1
- package/dist/harness/runtime/drive/boundary.d.ts +28 -0
- package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
- package/dist/harness/runtime/drive/boundary.js +173 -0
- package/dist/harness/runtime/drive/boundary.js.map +1 -0
- package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
- package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
- package/dist/harness/runtime/drive/checkpoint.js +133 -0
- package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
- package/dist/harness/runtime/drive/deferred.d.ts +15 -0
- package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
- package/dist/harness/runtime/drive/deferred.js +179 -0
- package/dist/harness/runtime/drive/deferred.js.map +1 -0
- package/dist/harness/runtime/drive/generation.d.ts +8 -0
- package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
- package/dist/harness/runtime/drive/generation.js +205 -0
- package/dist/harness/runtime/drive/generation.js.map +1 -0
- package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
- package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
- package/dist/harness/runtime/drive/reconcile.js +140 -0
- package/dist/harness/runtime/drive/reconcile.js.map +1 -0
- package/dist/harness/runtime/drive/recovery.d.ts +8 -0
- package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
- package/dist/harness/runtime/drive/recovery.js +85 -0
- package/dist/harness/runtime/drive/recovery.js.map +1 -0
- package/dist/harness/runtime/drive/response.d.ts +23 -0
- package/dist/harness/runtime/drive/response.d.ts.map +1 -0
- package/dist/harness/runtime/drive/response.js +394 -0
- package/dist/harness/runtime/drive/response.js.map +1 -0
- package/dist/harness/runtime/drive/retry.d.ts +4 -0
- package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
- package/dist/harness/runtime/drive/retry.js +34 -0
- package/dist/harness/runtime/drive/retry.js.map +1 -0
- package/dist/harness/runtime/drive/structural.d.ts +32 -0
- package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
- package/dist/harness/runtime/drive/structural.js +929 -0
- package/dist/harness/runtime/drive/structural.js.map +1 -0
- package/dist/harness/runtime/drive/terminal.d.ts +7 -0
- package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
- package/dist/harness/runtime/drive/terminal.js +49 -0
- package/dist/harness/runtime/drive/terminal.js.map +1 -0
- package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
- package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
- package/dist/harness/runtime/drive/tool-placement.js +244 -0
- package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
- package/dist/harness/runtime/drive/tools.d.ts +6 -0
- package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
- package/dist/harness/runtime/drive/tools.js +449 -0
- package/dist/harness/runtime/drive/tools.js.map +1 -0
- package/dist/harness/runtime/drive.d.ts +6 -0
- package/dist/harness/runtime/drive.d.ts.map +1 -0
- package/dist/harness/runtime/drive.js +93 -0
- package/dist/harness/runtime/drive.js.map +1 -0
- package/dist/harness/runtime/harness.d.ts +59 -0
- package/dist/harness/runtime/harness.d.ts.map +1 -0
- package/dist/harness/runtime/harness.js +302 -0
- package/dist/harness/runtime/harness.js.map +1 -0
- package/dist/harness/runtime/index.d.ts +2 -0
- package/dist/harness/runtime/index.d.ts.map +1 -0
- package/dist/harness/runtime/index.js +2 -0
- package/dist/harness/runtime/index.js.map +1 -0
- package/dist/harness/runtime/lane.d.ts +118 -0
- package/dist/harness/runtime/lane.d.ts.map +1 -0
- package/dist/harness/runtime/lane.js +1559 -0
- package/dist/harness/runtime/lane.js.map +1 -0
- package/dist/harness/runtime/progress.d.ts +15 -0
- package/dist/harness/runtime/progress.d.ts.map +1 -0
- package/dist/harness/runtime/progress.js +66 -0
- package/dist/harness/runtime/progress.js.map +1 -0
- package/dist/harness/runtime/reducer.d.ts +5 -0
- package/dist/harness/runtime/reducer.d.ts.map +1 -0
- package/dist/harness/runtime/reducer.js +240 -0
- package/dist/harness/runtime/reducer.js.map +1 -0
- package/dist/harness/runtime/restore.d.ts +24 -0
- package/dist/harness/runtime/restore.d.ts.map +1 -0
- package/dist/harness/runtime/restore.js +117 -0
- package/dist/harness/runtime/restore.js.map +1 -0
- package/dist/harness/runtime/transcript.d.ts +21 -0
- package/dist/harness/runtime/transcript.d.ts.map +1 -0
- package/dist/harness/runtime/transcript.js +72 -0
- package/dist/harness/runtime/transcript.js.map +1 -0
- package/dist/harness/runtime/types.d.ts +105 -0
- package/dist/harness/runtime/types.d.ts.map +1 -0
- package/dist/harness/runtime/types.js +50 -0
- package/dist/harness/runtime/types.js.map +1 -0
- package/dist/harness/session/commit.d.ts +53 -0
- package/dist/harness/session/commit.d.ts.map +1 -0
- package/dist/harness/session/commit.js +57 -0
- package/dist/harness/session/commit.js.map +1 -0
- package/dist/harness/session/context.d.ts +6 -19
- package/dist/harness/session/context.d.ts.map +1 -1
- package/dist/harness/session/context.js +31 -53
- package/dist/harness/session/context.js.map +1 -1
- package/dist/harness/session/fork-policy.d.ts +21 -0
- package/dist/harness/session/fork-policy.d.ts.map +1 -0
- package/dist/harness/session/fork-policy.js +55 -0
- package/dist/harness/session/fork-policy.js.map +1 -0
- package/dist/harness/session/fork.d.ts +16 -0
- package/dist/harness/session/fork.d.ts.map +1 -0
- package/dist/harness/session/fork.js +79 -0
- package/dist/harness/session/fork.js.map +1 -0
- package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
- package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
- package/dist/harness/session/in-memory-storage-state.js +269 -0
- package/dist/harness/session/in-memory-storage-state.js.map +1 -0
- package/dist/harness/session/index.d.ts +9 -5
- package/dist/harness/session/index.d.ts.map +1 -1
- package/dist/harness/session/index.js +7 -4
- package/dist/harness/session/index.js.map +1 -1
- package/dist/harness/session/jsonl/codec.d.ts +19 -8
- package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
- package/dist/harness/session/jsonl/codec.js +34 -219
- package/dist/harness/session/jsonl/codec.js.map +1 -1
- package/dist/harness/session/jsonl/fork.d.ts +38 -0
- package/dist/harness/session/jsonl/fork.d.ts.map +1 -0
- package/dist/harness/session/jsonl/fork.js +266 -0
- package/dist/harness/session/jsonl/fork.js.map +1 -0
- package/dist/harness/session/jsonl/index.d.ts +4 -0
- package/dist/harness/session/jsonl/index.d.ts.map +1 -0
- package/dist/harness/session/jsonl/index.js +4 -0
- package/dist/harness/session/jsonl/index.js.map +1 -0
- package/dist/harness/session/jsonl/io.d.ts +14 -0
- package/dist/harness/session/jsonl/io.d.ts.map +1 -0
- package/dist/harness/session/jsonl/io.js +86 -0
- package/dist/harness/session/jsonl/io.js.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts +44 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.js +454 -0
- package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
- package/dist/harness/session/jsonl/repo.d.ts +27 -24
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
- package/dist/harness/session/jsonl/repo.js +276 -151
- package/dist/harness/session/jsonl/repo.js.map +1 -1
- package/dist/harness/session/jsonl/storage.d.ts +39 -41
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
- package/dist/harness/session/jsonl/storage.js +175 -196
- package/dist/harness/session/jsonl/storage.js.map +1 -1
- package/dist/harness/session/jsonl/types.d.ts +23 -22
- package/dist/harness/session/jsonl/types.d.ts.map +1 -1
- package/dist/harness/session/jsonl/types.js +2 -1
- package/dist/harness/session/jsonl/types.js.map +1 -1
- package/dist/harness/session/memory.d.ts +44 -40
- package/dist/harness/session/memory.d.ts.map +1 -1
- package/dist/harness/session/memory.js +333 -115
- package/dist/harness/session/memory.js.map +1 -1
- package/dist/harness/session/mutation-line.d.ts +8 -0
- package/dist/harness/session/mutation-line.d.ts.map +1 -0
- package/dist/harness/session/mutation-line.js +22 -0
- package/dist/harness/session/mutation-line.js.map +1 -0
- package/dist/harness/session/session.d.ts +78 -51
- package/dist/harness/session/session.d.ts.map +1 -1
- package/dist/harness/session/session.js +327 -207
- package/dist/harness/session/session.js.map +1 -1
- package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
- package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/datasets.js +24 -0
- package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
- package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
- package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
- package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
- package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
- package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/storage.js +114 -0
- package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts +23 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.js +809 -0
- package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
- package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
- package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/storage.js +600 -0
- package/dist/harness/session/testing/conformance/storage.js.map +1 -0
- package/dist/harness/session/testing/gating-storage.d.ts +26 -0
- package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
- package/dist/harness/session/testing/gating-storage.js +103 -0
- package/dist/harness/session/testing/gating-storage.js.map +1 -0
- package/dist/harness/session/testing/index.d.ts +9 -2
- package/dist/harness/session/testing/index.d.ts.map +1 -1
- package/dist/harness/session/testing/index.js +8 -1
- package/dist/harness/session/testing/index.js.map +1 -1
- package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
- package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
- package/dist/harness/session/testing/instrumented-storage.js +19 -0
- package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
- package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
- package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
- package/dist/harness/session/testing/storage-decorator.js +40 -0
- package/dist/harness/session/testing/storage-decorator.js.map +1 -0
- package/dist/harness/session/testing/types.d.ts +5 -7
- package/dist/harness/session/testing/types.d.ts.map +1 -1
- package/dist/harness/session/testing/types.js.map +1 -1
- package/dist/harness/session/types.d.ts +425 -265
- package/dist/harness/session/types.d.ts.map +1 -1
- package/dist/harness/session/types.js +7 -7
- package/dist/harness/session/types.js.map +1 -1
- package/dist/harness/session/values.d.ts +95 -0
- package/dist/harness/session/values.d.ts.map +1 -0
- package/dist/harness/session/values.js +81 -0
- package/dist/harness/session/values.js.map +1 -0
- package/dist/harness/skills.d.ts +3 -2
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +29 -26
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/telemetry.d.ts +64 -43
- package/dist/harness/telemetry.d.ts.map +1 -1
- package/dist/harness/telemetry.js +45 -26
- package/dist/harness/telemetry.js.map +1 -1
- package/dist/harness/tools/bash.d.ts +4 -4
- package/dist/harness/tools/bash.d.ts.map +1 -1
- package/dist/harness/tools/bash.js +66 -93
- package/dist/harness/tools/bash.js.map +1 -1
- package/dist/harness/tools/edit.d.ts.map +1 -1
- package/dist/harness/tools/edit.js +17 -13
- package/dist/harness/tools/edit.js.map +1 -1
- package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
- package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/harness/tools/file-mutation-queue.js +5 -5
- package/dist/harness/tools/file-mutation-queue.js.map +1 -1
- package/dist/harness/tools/path-utils.d.ts +3 -2
- package/dist/harness/tools/path-utils.d.ts.map +1 -1
- package/dist/harness/tools/path-utils.js +5 -5
- package/dist/harness/tools/path-utils.js.map +1 -1
- package/dist/harness/tools/read.d.ts +2 -1
- package/dist/harness/tools/read.d.ts.map +1 -1
- package/dist/harness/tools/read.js +4 -6
- package/dist/harness/tools/read.js.map +1 -1
- package/dist/harness/tools/write.d.ts.map +1 -1
- package/dist/harness/tools/write.js +12 -8
- package/dist/harness/tools/write.js.map +1 -1
- package/dist/harness/types.d.ts +128 -45
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
- package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
- package/dist/harness/utils/adaptive-publisher.js +79 -0
- package/dist/harness/utils/adaptive-publisher.js.map +1 -0
- package/dist/harness/utils/output-capture.d.ts +31 -0
- package/dist/harness/utils/output-capture.d.ts.map +1 -0
- package/dist/harness/utils/output-capture.js +217 -0
- package/dist/harness/utils/output-capture.js.map +1 -0
- package/dist/harness/utils/shell-output.d.ts +19 -5
- package/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/dist/harness/utils/shell-output.js +65 -192
- package/dist/harness/utils/shell-output.js.map +1 -1
- package/dist/harness/utils/truncate.d.ts +1 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -1
- package/dist/harness/utils/truncate.js +1 -1
- package/dist/harness/utils/truncate.js.map +1 -1
- package/dist/harness/utils/usage.d.ts +4 -0
- package/dist/harness/utils/usage.d.ts.map +1 -0
- package/dist/harness/utils/usage.js +33 -0
- package/dist/harness/utils/usage.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -6
- package/dist/index.js.map +1 -1
- package/dist/search/index.d.ts +24 -16
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +1 -1
- package/dist/search/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +27 -6
- package/dist/harness/reducer.d.ts +0 -100
- package/dist/harness/reducer.d.ts.map +0 -1
- package/dist/harness/reducer.js +0 -420
- package/dist/harness/reducer.js.map +0 -1
- package/dist/harness/session/jsonl/errors.d.ts +0 -9
- package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
- package/dist/harness/session/jsonl/errors.js +0 -18
- package/dist/harness/session/jsonl/errors.js.map +0 -1
- package/dist/harness/session/jsonl.d.ts +0 -3
- package/dist/harness/session/jsonl.d.ts.map +0 -1
- package/dist/harness/session/jsonl.js +0 -2
- package/dist/harness/session/jsonl.js.map +0 -1
- package/dist/harness/session/state.d.ts +0 -65
- package/dist/harness/session/state.d.ts.map +0 -1
- package/dist/harness/session/state.js +0 -320
- package/dist/harness/session/state.js.map +0 -1
- package/dist/harness/session/testing/conformance.d.ts +0 -4
- package/dist/harness/session/testing/conformance.d.ts.map +0 -1
- package/dist/harness/session/testing/conformance.js +0 -762
- package/dist/harness/session/testing/conformance.js.map +0 -1
- package/dist/search/scanning.d.ts +0 -29
- package/dist/search/scanning.d.ts.map +0 -1
- package/dist/search/scanning.js +0 -103
- package/dist/search/scanning.js.map +0 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { AdaptivePublisher } from "./adaptive-publisher.js";
|
|
2
|
+
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, truncateTail, utf8ByteLength } from "./truncate.js";
|
|
3
|
+
export const OUTPUT_MIN_EMIT_INTERVAL_MS = 100;
|
|
4
|
+
export const OUTPUT_TARGET_BYTES_PER_SECOND = 100 * 1024;
|
|
5
|
+
const INVALID_SHELL_OUTPUT = /[\x00-\x08\x0b-\x1f\ufff9-\ufffb]/g;
|
|
6
|
+
const textEncoder = new TextEncoder();
|
|
7
|
+
const textDecoder = new TextDecoder();
|
|
8
|
+
/**
|
|
9
|
+
* Maintains and publishes one bounded shell-output view.
|
|
10
|
+
*
|
|
11
|
+
* Writes received while publication is rate-limited collapse into the latest
|
|
12
|
+
* view. Small changes remain responsive; complete window turnovers purchase a
|
|
13
|
+
* proportionally longer delay. The first update after idle and an explicit
|
|
14
|
+
* final flush are immediate.
|
|
15
|
+
*/
|
|
16
|
+
export class OutputCapture {
|
|
17
|
+
#maxBytes;
|
|
18
|
+
#maxLines;
|
|
19
|
+
#retain;
|
|
20
|
+
#context;
|
|
21
|
+
#onUpdate;
|
|
22
|
+
#decoder = new TextDecoder();
|
|
23
|
+
#buffer = "";
|
|
24
|
+
#bufferBytes = 0;
|
|
25
|
+
#totalBytes = 0;
|
|
26
|
+
#newlines = 0;
|
|
27
|
+
#endsWithNewline = true;
|
|
28
|
+
#currentLineBytes = 0;
|
|
29
|
+
#spillPath;
|
|
30
|
+
#disposed = false;
|
|
31
|
+
#publisher;
|
|
32
|
+
constructor(options, context, handlers) {
|
|
33
|
+
this.#maxBytes = options?.limits.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
34
|
+
this.#maxLines = options?.limits.maxLines ?? DEFAULT_MAX_LINES;
|
|
35
|
+
this.#retain = options?.limits.retain ?? "tail";
|
|
36
|
+
this.#context = context;
|
|
37
|
+
this.#onUpdate = handlers.onUpdate;
|
|
38
|
+
if (!Number.isFinite(this.#maxBytes) || this.#maxBytes <= 0) {
|
|
39
|
+
throw new TypeError("Output maxBytes must be a positive finite number");
|
|
40
|
+
}
|
|
41
|
+
if (!Number.isInteger(this.#maxLines) || this.#maxLines <= 0) {
|
|
42
|
+
throw new TypeError("Output maxLines must be a positive integer");
|
|
43
|
+
}
|
|
44
|
+
this.#publisher = new AdaptivePublisher({
|
|
45
|
+
snapshot: () => this.snapshot(),
|
|
46
|
+
update: updateFrom,
|
|
47
|
+
measure: (update) => utf8ByteLength(JSON.stringify(update)),
|
|
48
|
+
publish: (update) => this.#onUpdate?.(update, this.#context),
|
|
49
|
+
onError: handlers.onError,
|
|
50
|
+
minIntervalMs: OUTPUT_MIN_EMIT_INTERVAL_MS,
|
|
51
|
+
targetBytesPerSecond: OUTPUT_TARGET_BYTES_PER_SECOND,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
get truncated() {
|
|
55
|
+
return this.#totalBytes > this.#maxBytes || this.#totalLines() > this.#maxLines;
|
|
56
|
+
}
|
|
57
|
+
push(chunk) {
|
|
58
|
+
if (this.#disposed)
|
|
59
|
+
return;
|
|
60
|
+
if (typeof chunk === "string") {
|
|
61
|
+
this.#appendText(this.#decoder.decode());
|
|
62
|
+
this.#appendText(chunk);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.#appendText(this.#decoder.decode(chunk, { stream: true }));
|
|
66
|
+
}
|
|
67
|
+
finish() {
|
|
68
|
+
if (this.#disposed)
|
|
69
|
+
return;
|
|
70
|
+
this.#appendText(this.#decoder.decode());
|
|
71
|
+
}
|
|
72
|
+
setSpillPath(path) {
|
|
73
|
+
if (this.#disposed || this.#spillPath === path)
|
|
74
|
+
return;
|
|
75
|
+
this.#spillPath = path;
|
|
76
|
+
this.#publisher.markDirty();
|
|
77
|
+
this.flush();
|
|
78
|
+
}
|
|
79
|
+
snapshot() {
|
|
80
|
+
const retained = this.#retain === "head"
|
|
81
|
+
? truncateHead(this.#buffer, { maxBytes: this.#maxBytes, maxLines: this.#maxLines })
|
|
82
|
+
: truncateTail(this.#buffer, { maxBytes: this.#maxBytes, maxLines: this.#maxLines });
|
|
83
|
+
const totalLines = this.#totalLines();
|
|
84
|
+
const truncated = this.truncated;
|
|
85
|
+
const { content, ...truncation } = retained;
|
|
86
|
+
return {
|
|
87
|
+
text: sanitizeShellOutput(content),
|
|
88
|
+
truncation: {
|
|
89
|
+
...truncation,
|
|
90
|
+
truncated,
|
|
91
|
+
truncatedBy: truncated ? (totalLines > this.#maxLines ? "lines" : "bytes") : null,
|
|
92
|
+
totalBytes: this.#totalBytes,
|
|
93
|
+
totalLines,
|
|
94
|
+
},
|
|
95
|
+
...(this.#spillPath === undefined ? {} : { spillPath: this.#spillPath }),
|
|
96
|
+
...(retained.lastLinePartial ? { lastLineBytes: this.#currentLineBytes } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
flush() {
|
|
100
|
+
if (this.#disposed)
|
|
101
|
+
return;
|
|
102
|
+
this.#publisher.flush(true);
|
|
103
|
+
}
|
|
104
|
+
dispose() {
|
|
105
|
+
this.#publisher.dispose();
|
|
106
|
+
this.#disposed = true;
|
|
107
|
+
}
|
|
108
|
+
#appendText(text) {
|
|
109
|
+
if (text === "")
|
|
110
|
+
return;
|
|
111
|
+
const textBytes = utf8ByteLength(text);
|
|
112
|
+
this.#totalBytes += textBytes;
|
|
113
|
+
this.#newlines += countNewlines(text);
|
|
114
|
+
this.#endsWithNewline = text.endsWith("\n");
|
|
115
|
+
const lastNewline = text.lastIndexOf("\n");
|
|
116
|
+
this.#currentLineBytes =
|
|
117
|
+
lastNewline === -1 ? this.#currentLineBytes + textBytes : utf8ByteLength(text.slice(lastNewline + 1));
|
|
118
|
+
this.#buffer += text;
|
|
119
|
+
this.#bufferBytes += textBytes;
|
|
120
|
+
const guard = this.#maxBytes * 2;
|
|
121
|
+
if (this.#bufferBytes > guard * 2) {
|
|
122
|
+
this.#buffer =
|
|
123
|
+
this.#retain === "tail"
|
|
124
|
+
? trimToLastUtf8Bytes(this.#buffer, guard)
|
|
125
|
+
: trimToFirstUtf8Bytes(this.#buffer, guard);
|
|
126
|
+
this.#bufferBytes = utf8ByteLength(this.#buffer);
|
|
127
|
+
}
|
|
128
|
+
this.#publisher.markDirty();
|
|
129
|
+
}
|
|
130
|
+
#totalLines() {
|
|
131
|
+
return this.#newlines + (this.#endsWithNewline || this.#totalBytes === 0 ? 0 : 1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function applyShellOutputUpdate(current, update) {
|
|
135
|
+
switch (update.kind) {
|
|
136
|
+
case "replace":
|
|
137
|
+
return update.output;
|
|
138
|
+
case "append":
|
|
139
|
+
return { text: `${current?.text ?? ""}${update.text}`, ...update.metadata };
|
|
140
|
+
case "slide":
|
|
141
|
+
return { text: `${current?.text.slice(update.drop) ?? ""}${update.text}`, ...update.metadata };
|
|
142
|
+
case "metadata":
|
|
143
|
+
return { text: current?.text ?? "", ...update.metadata };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function updateFrom(previous, current) {
|
|
147
|
+
if (previous === undefined)
|
|
148
|
+
return { kind: "replace", output: current };
|
|
149
|
+
const metadata = {
|
|
150
|
+
truncation: current.truncation,
|
|
151
|
+
...(current.spillPath === undefined ? {} : { spillPath: current.spillPath }),
|
|
152
|
+
...(current.lastLineBytes === undefined ? {} : { lastLineBytes: current.lastLineBytes }),
|
|
153
|
+
};
|
|
154
|
+
if (current.text === previous.text)
|
|
155
|
+
return { kind: "metadata", metadata };
|
|
156
|
+
if (current.text.length > previous.text.length && current.text.slice(0, previous.text.length) === previous.text) {
|
|
157
|
+
return { kind: "append", text: current.text.slice(previous.text.length), metadata };
|
|
158
|
+
}
|
|
159
|
+
const shared = suffixPrefixOverlap(previous.text, current.text, Math.min(previous.text.length, current.text.length, current.truncation.maxBytes * 2));
|
|
160
|
+
if (shared > 0) {
|
|
161
|
+
return {
|
|
162
|
+
kind: "slide",
|
|
163
|
+
drop: previous.text.length - shared,
|
|
164
|
+
text: current.text.slice(shared),
|
|
165
|
+
metadata,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return { kind: "replace", output: current };
|
|
169
|
+
}
|
|
170
|
+
function suffixPrefixOverlap(before, after, scan) {
|
|
171
|
+
if (before.length === 0 || after.length === 0 || scan === 0)
|
|
172
|
+
return 0;
|
|
173
|
+
const tail = before.length > scan ? before.slice(before.length - scan) : before;
|
|
174
|
+
for (const probeLength of [Math.min(64, after.length), 1]) {
|
|
175
|
+
const probe = after.slice(0, probeLength);
|
|
176
|
+
let candidates = 0;
|
|
177
|
+
for (let index = tail.indexOf(probe); index !== -1; index = tail.indexOf(probe, index + 1)) {
|
|
178
|
+
if (++candidates > 8)
|
|
179
|
+
break;
|
|
180
|
+
const overlapLength = tail.length - index;
|
|
181
|
+
if (overlapLength <= after.length && tail.slice(index) === after.slice(0, overlapLength)) {
|
|
182
|
+
return overlapLength;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (probeLength === 1)
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
export function sanitizeShellOutput(text) {
|
|
191
|
+
return text.replace(INVALID_SHELL_OUTPUT, "");
|
|
192
|
+
}
|
|
193
|
+
function countNewlines(text) {
|
|
194
|
+
let count = 0;
|
|
195
|
+
for (let index = text.indexOf("\n"); index !== -1; index = text.indexOf("\n", index + 1))
|
|
196
|
+
count++;
|
|
197
|
+
return count;
|
|
198
|
+
}
|
|
199
|
+
function trimToLastUtf8Bytes(text, maxBytes) {
|
|
200
|
+
const bytes = textEncoder.encode(text);
|
|
201
|
+
if (bytes.length <= maxBytes)
|
|
202
|
+
return text;
|
|
203
|
+
let start = bytes.length - maxBytes;
|
|
204
|
+
while (start < bytes.length && ((bytes[start] ?? 0) & 0xc0) === 0x80)
|
|
205
|
+
start++;
|
|
206
|
+
return textDecoder.decode(bytes.subarray(start));
|
|
207
|
+
}
|
|
208
|
+
function trimToFirstUtf8Bytes(text, maxBytes) {
|
|
209
|
+
const bytes = textEncoder.encode(text);
|
|
210
|
+
if (bytes.length <= maxBytes)
|
|
211
|
+
return text;
|
|
212
|
+
let end = maxBytes;
|
|
213
|
+
while (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80)
|
|
214
|
+
end--;
|
|
215
|
+
return textDecoder.decode(bytes.subarray(0, end));
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=output-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-capture.js","sourceRoot":"","sources":["../../../src/harness/utils/output-capture.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"../context.ts\";\nimport type { ShellOutputCaptureOptions, ShellOutputMetadata, ShellOutputUpdate, ShellOutputView } from \"../types.ts\";\nimport { AdaptivePublisher } from \"./adaptive-publisher.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, truncateTail, utf8ByteLength } from \"./truncate.ts\";\n\nexport const OUTPUT_MIN_EMIT_INTERVAL_MS = 100;\nexport const OUTPUT_TARGET_BYTES_PER_SECOND = 100 * 1024;\n\nconst INVALID_SHELL_OUTPUT = /[\\x00-\\x08\\x0b-\\x1f\\ufff9-\\ufffb]/g;\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\ninterface OutputCaptureHandlers {\n\tonUpdate?: (update: ShellOutputUpdate, context: Context) => void;\n\tonError(error: unknown): void;\n}\n\n/**\n * Maintains and publishes one bounded shell-output view.\n *\n * Writes received while publication is rate-limited collapse into the latest\n * view. Small changes remain responsive; complete window turnovers purchase a\n * proportionally longer delay. The first update after idle and an explicit\n * final flush are immediate.\n */\nexport class OutputCapture {\n\treadonly #maxBytes: number;\n\treadonly #maxLines: number;\n\treadonly #retain: \"head\" | \"tail\";\n\treadonly #context: Context;\n\treadonly #onUpdate: OutputCaptureHandlers[\"onUpdate\"];\n\n\treadonly #decoder = new TextDecoder();\n\t#buffer = \"\";\n\t#bufferBytes = 0;\n\t#totalBytes = 0;\n\t#newlines = 0;\n\t#endsWithNewline = true;\n\t#currentLineBytes = 0;\n\t#spillPath: string | undefined;\n\t#disposed = false;\n\treadonly #publisher: AdaptivePublisher<ShellOutputView, ShellOutputUpdate>;\n\n\tconstructor(options: ShellOutputCaptureOptions | undefined, context: Context, handlers: OutputCaptureHandlers) {\n\t\tthis.#maxBytes = options?.limits.maxBytes ?? DEFAULT_MAX_BYTES;\n\t\tthis.#maxLines = options?.limits.maxLines ?? DEFAULT_MAX_LINES;\n\t\tthis.#retain = options?.limits.retain ?? \"tail\";\n\t\tthis.#context = context;\n\t\tthis.#onUpdate = handlers.onUpdate;\n\t\tif (!Number.isFinite(this.#maxBytes) || this.#maxBytes <= 0) {\n\t\t\tthrow new TypeError(\"Output maxBytes must be a positive finite number\");\n\t\t}\n\t\tif (!Number.isInteger(this.#maxLines) || this.#maxLines <= 0) {\n\t\t\tthrow new TypeError(\"Output maxLines must be a positive integer\");\n\t\t}\n\t\tthis.#publisher = new AdaptivePublisher({\n\t\t\tsnapshot: () => this.snapshot(),\n\t\t\tupdate: updateFrom,\n\t\t\tmeasure: (update) => utf8ByteLength(JSON.stringify(update)),\n\t\t\tpublish: (update) => this.#onUpdate?.(update, this.#context),\n\t\t\tonError: handlers.onError,\n\t\t\tminIntervalMs: OUTPUT_MIN_EMIT_INTERVAL_MS,\n\t\t\ttargetBytesPerSecond: OUTPUT_TARGET_BYTES_PER_SECOND,\n\t\t});\n\t}\n\n\tget truncated(): boolean {\n\t\treturn this.#totalBytes > this.#maxBytes || this.#totalLines() > this.#maxLines;\n\t}\n\n\tpush(chunk: string | Uint8Array): void {\n\t\tif (this.#disposed) return;\n\t\tif (typeof chunk === \"string\") {\n\t\t\tthis.#appendText(this.#decoder.decode());\n\t\t\tthis.#appendText(chunk);\n\t\t\treturn;\n\t\t}\n\t\tthis.#appendText(this.#decoder.decode(chunk, { stream: true }));\n\t}\n\n\tfinish(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#appendText(this.#decoder.decode());\n\t}\n\n\tsetSpillPath(path: string): void {\n\t\tif (this.#disposed || this.#spillPath === path) return;\n\t\tthis.#spillPath = path;\n\t\tthis.#publisher.markDirty();\n\t\tthis.flush();\n\t}\n\n\tsnapshot(): ShellOutputView {\n\t\tconst retained =\n\t\t\tthis.#retain === \"head\"\n\t\t\t\t? truncateHead(this.#buffer, { maxBytes: this.#maxBytes, maxLines: this.#maxLines })\n\t\t\t\t: truncateTail(this.#buffer, { maxBytes: this.#maxBytes, maxLines: this.#maxLines });\n\t\tconst totalLines = this.#totalLines();\n\t\tconst truncated = this.truncated;\n\t\tconst { content, ...truncation } = retained;\n\t\treturn {\n\t\t\ttext: sanitizeShellOutput(content),\n\t\t\ttruncation: {\n\t\t\t\t...truncation,\n\t\t\t\ttruncated,\n\t\t\t\ttruncatedBy: truncated ? (totalLines > this.#maxLines ? \"lines\" : \"bytes\") : null,\n\t\t\t\ttotalBytes: this.#totalBytes,\n\t\t\t\ttotalLines,\n\t\t\t},\n\t\t\t...(this.#spillPath === undefined ? {} : { spillPath: this.#spillPath }),\n\t\t\t...(retained.lastLinePartial ? { lastLineBytes: this.#currentLineBytes } : {}),\n\t\t};\n\t}\n\n\tflush(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#publisher.flush(true);\n\t}\n\n\tdispose(): void {\n\t\tthis.#publisher.dispose();\n\t\tthis.#disposed = true;\n\t}\n\n\t#appendText(text: string): void {\n\t\tif (text === \"\") return;\n\t\tconst textBytes = utf8ByteLength(text);\n\t\tthis.#totalBytes += textBytes;\n\t\tthis.#newlines += countNewlines(text);\n\t\tthis.#endsWithNewline = text.endsWith(\"\\n\");\n\t\tconst lastNewline = text.lastIndexOf(\"\\n\");\n\t\tthis.#currentLineBytes =\n\t\t\tlastNewline === -1 ? this.#currentLineBytes + textBytes : utf8ByteLength(text.slice(lastNewline + 1));\n\t\tthis.#buffer += text;\n\t\tthis.#bufferBytes += textBytes;\n\n\t\tconst guard = this.#maxBytes * 2;\n\t\tif (this.#bufferBytes > guard * 2) {\n\t\t\tthis.#buffer =\n\t\t\t\tthis.#retain === \"tail\"\n\t\t\t\t\t? trimToLastUtf8Bytes(this.#buffer, guard)\n\t\t\t\t\t: trimToFirstUtf8Bytes(this.#buffer, guard);\n\t\t\tthis.#bufferBytes = utf8ByteLength(this.#buffer);\n\t\t}\n\t\tthis.#publisher.markDirty();\n\t}\n\n\t#totalLines(): number {\n\t\treturn this.#newlines + (this.#endsWithNewline || this.#totalBytes === 0 ? 0 : 1);\n\t}\n}\n\nexport function applyShellOutputUpdate(\n\tcurrent: ShellOutputView | undefined,\n\tupdate: ShellOutputUpdate,\n): ShellOutputView {\n\tswitch (update.kind) {\n\t\tcase \"replace\":\n\t\t\treturn update.output;\n\t\tcase \"append\":\n\t\t\treturn { text: `${current?.text ?? \"\"}${update.text}`, ...update.metadata };\n\t\tcase \"slide\":\n\t\t\treturn { text: `${current?.text.slice(update.drop) ?? \"\"}${update.text}`, ...update.metadata };\n\t\tcase \"metadata\":\n\t\t\treturn { text: current?.text ?? \"\", ...update.metadata };\n\t}\n}\n\nfunction updateFrom(previous: ShellOutputView | undefined, current: ShellOutputView): ShellOutputUpdate {\n\tif (previous === undefined) return { kind: \"replace\", output: current };\n\tconst metadata: ShellOutputMetadata = {\n\t\ttruncation: current.truncation,\n\t\t...(current.spillPath === undefined ? {} : { spillPath: current.spillPath }),\n\t\t...(current.lastLineBytes === undefined ? {} : { lastLineBytes: current.lastLineBytes }),\n\t};\n\tif (current.text === previous.text) return { kind: \"metadata\", metadata };\n\tif (current.text.length > previous.text.length && current.text.slice(0, previous.text.length) === previous.text) {\n\t\treturn { kind: \"append\", text: current.text.slice(previous.text.length), metadata };\n\t}\n\tconst shared = suffixPrefixOverlap(\n\t\tprevious.text,\n\t\tcurrent.text,\n\t\tMath.min(previous.text.length, current.text.length, current.truncation.maxBytes * 2),\n\t);\n\tif (shared > 0) {\n\t\treturn {\n\t\t\tkind: \"slide\",\n\t\t\tdrop: previous.text.length - shared,\n\t\t\ttext: current.text.slice(shared),\n\t\t\tmetadata,\n\t\t};\n\t}\n\treturn { kind: \"replace\", output: current };\n}\n\nfunction suffixPrefixOverlap(before: string, after: string, scan: number): number {\n\tif (before.length === 0 || after.length === 0 || scan === 0) return 0;\n\tconst tail = before.length > scan ? before.slice(before.length - scan) : before;\n\tfor (const probeLength of [Math.min(64, after.length), 1]) {\n\t\tconst probe = after.slice(0, probeLength);\n\t\tlet candidates = 0;\n\t\tfor (let index = tail.indexOf(probe); index !== -1; index = tail.indexOf(probe, index + 1)) {\n\t\t\tif (++candidates > 8) break;\n\t\t\tconst overlapLength = tail.length - index;\n\t\t\tif (overlapLength <= after.length && tail.slice(index) === after.slice(0, overlapLength)) {\n\t\t\t\treturn overlapLength;\n\t\t\t}\n\t\t}\n\t\tif (probeLength === 1) break;\n\t}\n\treturn 0;\n}\n\nexport function sanitizeShellOutput(text: string): string {\n\treturn text.replace(INVALID_SHELL_OUTPUT, \"\");\n}\n\nfunction countNewlines(text: string): number {\n\tlet count = 0;\n\tfor (let index = text.indexOf(\"\\n\"); index !== -1; index = text.indexOf(\"\\n\", index + 1)) count++;\n\treturn count;\n}\n\nfunction trimToLastUtf8Bytes(text: string, maxBytes: number): string {\n\tconst bytes = textEncoder.encode(text);\n\tif (bytes.length <= maxBytes) return text;\n\tlet start = bytes.length - maxBytes;\n\twhile (start < bytes.length && ((bytes[start] ?? 0) & 0xc0) === 0x80) start++;\n\treturn textDecoder.decode(bytes.subarray(start));\n}\n\nfunction trimToFirstUtf8Bytes(text: string, maxBytes: number): string {\n\tconst bytes = textEncoder.encode(text);\n\tif (bytes.length <= maxBytes) return text;\n\tlet end = maxBytes;\n\twhile (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80) end--;\n\treturn textDecoder.decode(bytes.subarray(0, end));\n}\n"]}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Context } from "../context.ts";
|
|
2
|
+
import { type ExecutionEnv, type ExecutionError, type Result, type ShellExecOptions } from "../types.ts";
|
|
3
|
+
import { sanitizeShellOutput } from "./output-capture.ts";
|
|
2
4
|
import { type TruncationResult } from "./truncate.ts";
|
|
3
5
|
export interface ShellCaptureProgress {
|
|
4
6
|
output: string;
|
|
@@ -6,8 +8,15 @@ export interface ShellCaptureProgress {
|
|
|
6
8
|
fullOutputPath?: string;
|
|
7
9
|
lastLineBytes: number;
|
|
8
10
|
}
|
|
9
|
-
export interface ShellCaptureOptions extends Omit<ShellExecOptions, "
|
|
10
|
-
|
|
11
|
+
export interface ShellCaptureOptions extends Omit<ShellExecOptions, "capture" | "onUpdate"> {
|
|
12
|
+
/**
|
|
13
|
+
* Called with each incremental output chunk.
|
|
14
|
+
*
|
|
15
|
+
* A returned promise is awaited by the execution environment before the result settles, so an
|
|
16
|
+
* observer that rejects fails the execution with a `callback_error` carrying the rejected value
|
|
17
|
+
* as its `cause` instead of escaping as an unhandled rejection.
|
|
18
|
+
*/
|
|
19
|
+
onChunk?: (chunk: string, getProgress: () => ShellCaptureProgress, context: Context) => void | PromiseLike<void>;
|
|
11
20
|
/** Return shell execution failures with captured output instead of as a failed Result. */
|
|
12
21
|
returnExecutionErrors?: boolean;
|
|
13
22
|
}
|
|
@@ -17,6 +26,11 @@ export interface ShellCaptureResult extends ShellCaptureProgress {
|
|
|
17
26
|
truncated: boolean;
|
|
18
27
|
executionError?: ExecutionError;
|
|
19
28
|
}
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Compatibility collector for callers that need one bounded final view.
|
|
31
|
+
* Source-side capture, adaptive publication, and spilling remain owned by the
|
|
32
|
+
* execution environment.
|
|
33
|
+
*/
|
|
34
|
+
export declare function executeShellWithCapture(env: ExecutionEnv, command: string, options: ShellCaptureOptions | undefined, context: Context): Promise<Result<ShellCaptureResult, ExecutionError>>;
|
|
35
|
+
export { sanitizeShellOutput as sanitizeBinaryOutput };
|
|
22
36
|
//# sourceMappingURL=shell-output.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-output.d.ts","sourceRoot":"","sources":["../../../src/harness/utils/shell-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"shell-output.d.ts","sourceRoot":"","sources":["../../../src/harness/utils/shell-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,cAAc,EAGnB,KAAK,MAAM,EACX,KAAK,gBAAgB,EAErB,MAAM,aAAa,CAAC;AACrB,OAAO,EAA0B,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAwC,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAE1G,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,gBAAgB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,UAAU,CAAC;IAC1F;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACjH,0FAA0F;IAC1F,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,kBAAmB,SAAQ,oBAAoB;IAC/D,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC;AAWD;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC5C,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,GAAG,SAAS,EACxC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC,CA2DrD;AAED,OAAO,EAAE,mBAAmB,IAAI,oBAAoB,EAAE,CAAC"}
|
|
@@ -1,200 +1,73 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { err, ok, } from "../types.js";
|
|
2
|
+
import { applyShellOutputUpdate, sanitizeShellOutput } from "./output-capture.js";
|
|
2
3
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "./truncate.js";
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export function sanitizeBinaryOutput(str) {
|
|
10
|
-
return Array.from(str)
|
|
11
|
-
.filter((char) => {
|
|
12
|
-
const code = char.codePointAt(0);
|
|
13
|
-
if (code === undefined)
|
|
14
|
-
return false;
|
|
15
|
-
if (code === 0x09 || code === 0x0a || code === 0x0d)
|
|
16
|
-
return true;
|
|
17
|
-
if (code <= 0x1f)
|
|
18
|
-
return false;
|
|
19
|
-
if (code >= 0xfff9 && code <= 0xfffb)
|
|
20
|
-
return false;
|
|
21
|
-
return true;
|
|
22
|
-
})
|
|
23
|
-
.join("");
|
|
24
|
-
}
|
|
25
|
-
function trimToLastUtf8Bytes(text, maxBytes, encoder) {
|
|
26
|
-
const bytes = encoder.encode(text);
|
|
27
|
-
if (bytes.byteLength <= maxBytes)
|
|
28
|
-
return text;
|
|
29
|
-
let start = bytes.byteLength - maxBytes;
|
|
30
|
-
while (start < bytes.byteLength && ((bytes[start] ?? 0) & 0xc0) === 0x80)
|
|
31
|
-
start++;
|
|
32
|
-
return new TextDecoder().decode(bytes.subarray(start));
|
|
33
|
-
}
|
|
34
|
-
export async function executeShellWithCapture(env, command, options) {
|
|
35
|
-
let tailOutput = "";
|
|
36
|
-
const maxOutputBytes = DEFAULT_MAX_BYTES * 2;
|
|
37
|
-
const encoder = new TextEncoder();
|
|
38
|
-
let totalBytes = 0;
|
|
39
|
-
let completedLines = 0;
|
|
40
|
-
let hasOpenLine = false;
|
|
41
|
-
let currentLineBytes = 0;
|
|
42
|
-
let fullOutputPath;
|
|
43
|
-
let fullOutputRequested = false;
|
|
44
|
-
let acceptingOutput = true;
|
|
45
|
-
let writeChain = Promise.resolve(ok(undefined));
|
|
46
|
-
let captureError;
|
|
47
|
-
const appendFullOutput = (text) => {
|
|
48
|
-
if (!fullOutputRequested || captureError)
|
|
49
|
-
return;
|
|
50
|
-
writeChain = writeChain.then(async (previous) => {
|
|
51
|
-
if (!previous.ok)
|
|
52
|
-
return previous;
|
|
53
|
-
if (!fullOutputPath)
|
|
54
|
-
return err(new ExecutionError("unknown", "Full output path was not created"));
|
|
55
|
-
const appendResult = await env.appendFile(fullOutputPath, text);
|
|
56
|
-
return appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
const ensureFullOutputFile = (initialContent) => {
|
|
60
|
-
if (fullOutputRequested || captureError)
|
|
61
|
-
return;
|
|
62
|
-
fullOutputRequested = true;
|
|
63
|
-
writeChain = writeChain.then(async (previous) => {
|
|
64
|
-
if (!previous.ok)
|
|
65
|
-
return previous;
|
|
66
|
-
const tempFile = await env.createTempFile({ prefix: "bash-", suffix: ".log" });
|
|
67
|
-
if (!tempFile.ok)
|
|
68
|
-
return err(toExecutionError(tempFile.error));
|
|
69
|
-
fullOutputPath = tempFile.value;
|
|
70
|
-
const appendResult = await env.appendFile(tempFile.value, initialContent);
|
|
71
|
-
return appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));
|
|
72
|
-
});
|
|
4
|
+
function progressFrom(output) {
|
|
5
|
+
return {
|
|
6
|
+
output: output.text,
|
|
7
|
+
truncation: { content: output.text, ...output.truncation },
|
|
8
|
+
...(output.spillPath === undefined ? {} : { fullOutputPath: output.spillPath }),
|
|
9
|
+
lastLineBytes: output.lastLineBytes ?? 0,
|
|
73
10
|
};
|
|
74
|
-
|
|
75
|
-
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Compatibility collector for callers that need one bounded final view.
|
|
14
|
+
* Source-side capture, adaptive publication, and spilling remain owned by the
|
|
15
|
+
* execution environment.
|
|
16
|
+
*/
|
|
17
|
+
export async function executeShellWithCapture(env, command, options, context) {
|
|
18
|
+
let output;
|
|
19
|
+
const result = await env.exec(command, {
|
|
20
|
+
cwd: options?.cwd,
|
|
21
|
+
env: options?.env,
|
|
22
|
+
inheritEnv: options?.inheritEnv,
|
|
23
|
+
timeout: options?.timeout,
|
|
24
|
+
capture: {
|
|
25
|
+
limits: { maxBytes: DEFAULT_MAX_BYTES, maxLines: DEFAULT_MAX_LINES, retain: "tail" },
|
|
26
|
+
spill: true,
|
|
27
|
+
},
|
|
28
|
+
onUpdate: (update, updateContext) => {
|
|
29
|
+
const previous = output;
|
|
30
|
+
output = applyShellOutputUpdate(output, update);
|
|
31
|
+
const chunk = update.kind === "append" || update.kind === "slide"
|
|
32
|
+
? update.text
|
|
33
|
+
: update.kind === "replace" && previous === undefined
|
|
34
|
+
? output.text
|
|
35
|
+
: undefined;
|
|
36
|
+
// A metadata-only update and a post-cap replacement contain no new
|
|
37
|
+
// incremental chunk. Reporting their complete view would duplicate bytes
|
|
38
|
+
// for callers that accumulate this compatibility callback.
|
|
39
|
+
// The observer's settlement is returned so the environment can await it.
|
|
40
|
+
if (chunk)
|
|
41
|
+
return options?.onChunk?.(chunk, () => progressFrom(output), updateContext);
|
|
76
42
|
return undefined;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const truncation = {
|
|
88
|
-
...tailTruncation,
|
|
89
|
-
truncated,
|
|
90
|
-
truncatedBy: truncated
|
|
91
|
-
? (tailTruncation.truncatedBy ?? (totalBytes > DEFAULT_MAX_BYTES ? "bytes" : "lines"))
|
|
92
|
-
: null,
|
|
93
|
-
totalLines,
|
|
94
|
-
totalBytes,
|
|
95
|
-
};
|
|
96
|
-
return {
|
|
97
|
-
output: truncated ? truncation.content : tailOutput,
|
|
98
|
-
truncation,
|
|
99
|
-
fullOutputPath,
|
|
100
|
-
lastLineBytes: currentLineBytes,
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
const onChunk = async (chunk) => {
|
|
104
|
-
if (!acceptingOutput)
|
|
105
|
-
return;
|
|
106
|
-
try {
|
|
107
|
-
const text = sanitizeBinaryOutput(chunk).replace(/\r/g, "");
|
|
108
|
-
const textBytes = encoder.encode(text).byteLength;
|
|
109
|
-
totalBytes += textBytes;
|
|
110
|
-
const newlineCount = text.split("\n").length - 1;
|
|
111
|
-
completedLines += newlineCount;
|
|
112
|
-
const lastNewline = text.lastIndexOf("\n");
|
|
113
|
-
if (lastNewline >= 0) {
|
|
114
|
-
const trailingText = text.slice(lastNewline + 1);
|
|
115
|
-
currentLineBytes = encoder.encode(trailingText).byteLength;
|
|
116
|
-
hasOpenLine = trailingText.length > 0;
|
|
117
|
-
}
|
|
118
|
-
else if (text.length > 0) {
|
|
119
|
-
currentLineBytes += textBytes;
|
|
120
|
-
hasOpenLine = true;
|
|
121
|
-
}
|
|
122
|
-
tailOutput += text;
|
|
123
|
-
const totalLines = completedLines + (hasOpenLine ? 1 : 0);
|
|
124
|
-
if ((totalBytes > DEFAULT_MAX_BYTES || totalLines > DEFAULT_MAX_LINES) && !fullOutputRequested) {
|
|
125
|
-
ensureFullOutputFile(tailOutput);
|
|
126
|
-
}
|
|
127
|
-
else if (fullOutputRequested) {
|
|
128
|
-
appendFullOutput(text);
|
|
129
|
-
}
|
|
130
|
-
tailOutput = trimToLastUtf8Bytes(tailOutput, maxOutputBytes, encoder);
|
|
131
|
-
await options?.onChunk?.(text, createProgress);
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
captureError = new ExecutionError("callback_error", toError(error).message, error);
|
|
135
|
-
// Keep the harness execution rejected. Merely recording the callback error
|
|
136
|
-
// here turns a failed observer into a fulfilled capture result, preventing
|
|
137
|
-
// the harness abort path from killing the child process.
|
|
138
|
-
throw error;
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
try {
|
|
142
|
-
const result = await env.exec(command, {
|
|
143
|
-
cwd: options?.cwd,
|
|
144
|
-
env: options?.env,
|
|
145
|
-
inheritEnv: options?.inheritEnv,
|
|
146
|
-
timeout: options?.timeout,
|
|
147
|
-
abortSignal: options?.abortSignal,
|
|
148
|
-
onStdout: onChunk,
|
|
149
|
-
onStderr: onChunk,
|
|
150
|
-
});
|
|
151
|
-
acceptingOutput = false;
|
|
152
|
-
let progress = createProgress();
|
|
153
|
-
if (progress.truncation.truncated && !fullOutputRequested)
|
|
154
|
-
ensureFullOutputFile(tailOutput);
|
|
155
|
-
const writeResult = await writeChain;
|
|
156
|
-
if (!writeResult.ok) {
|
|
157
|
-
return err(withCleanupError(writeResult.error, await cleanupFullOutput()));
|
|
158
|
-
}
|
|
159
|
-
if (captureError) {
|
|
160
|
-
return err(withCleanupError(captureError, await cleanupFullOutput()));
|
|
43
|
+
},
|
|
44
|
+
}, context);
|
|
45
|
+
if (output === undefined) {
|
|
46
|
+
const { content, ...truncation } = truncateTail("");
|
|
47
|
+
output = { text: content, truncation };
|
|
48
|
+
}
|
|
49
|
+
const progress = progressFrom(output);
|
|
50
|
+
if (!result.ok) {
|
|
51
|
+
if (result.error.code === "aborted" || context.abortSignal?.aborted) {
|
|
52
|
+
return ok({ ...progress, exitCode: undefined, cancelled: true, truncated: progress.truncation.truncated });
|
|
161
53
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
exitCode: undefined,
|
|
171
|
-
cancelled: true,
|
|
172
|
-
truncated: progress.truncation.truncated,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
if (options?.returnExecutionErrors) {
|
|
176
|
-
return ok({
|
|
177
|
-
...progress,
|
|
178
|
-
exitCode: undefined,
|
|
179
|
-
cancelled: false,
|
|
180
|
-
truncated: progress.truncation.truncated,
|
|
181
|
-
executionError: result.error,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return err(result.error);
|
|
54
|
+
if (options?.returnExecutionErrors) {
|
|
55
|
+
return ok({
|
|
56
|
+
...progress,
|
|
57
|
+
exitCode: undefined,
|
|
58
|
+
cancelled: false,
|
|
59
|
+
truncated: progress.truncation.truncated,
|
|
60
|
+
executionError: result.error,
|
|
61
|
+
});
|
|
185
62
|
}
|
|
186
|
-
|
|
187
|
-
return ok({
|
|
188
|
-
...progress,
|
|
189
|
-
exitCode: cancelled ? undefined : result.value.exitCode,
|
|
190
|
-
cancelled,
|
|
191
|
-
truncated: progress.truncation.truncated,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
acceptingOutput = false;
|
|
196
|
-
const primaryError = toExecutionError(error);
|
|
197
|
-
return err(withCleanupError(primaryError, await cleanupFullOutput()));
|
|
63
|
+
return err(result.error);
|
|
198
64
|
}
|
|
65
|
+
return ok({
|
|
66
|
+
...progress,
|
|
67
|
+
exitCode: result.value.exitCode,
|
|
68
|
+
cancelled: false,
|
|
69
|
+
truncated: result.value.truncation.truncated,
|
|
70
|
+
});
|
|
199
71
|
}
|
|
72
|
+
export { sanitizeShellOutput as sanitizeBinaryOutput };
|
|
200
73
|
//# sourceMappingURL=shell-output.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-output.js","sourceRoot":"","sources":["../../../src/harness/utils/shell-output.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ExecutionEnv, ExecutionError, err, ok, type Result, type ShellExecOptions, toError } from \"../types.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type TruncationResult, truncateTail } from \"./truncate.ts\";\n\nexport interface ShellCaptureProgress {\n\toutput: string;\n\ttruncation: TruncationResult;\n\tfullOutputPath?: string;\n\tlastLineBytes: number;\n}\n\nexport interface ShellCaptureOptions extends Omit<ShellExecOptions, \"onStdout\" | \"onStderr\"> {\n\tonChunk?: (chunk: string, getProgress: () => ShellCaptureProgress) => void | PromiseLike<void>;\n\t/** Return shell execution failures with captured output instead of as a failed Result. */\n\treturnExecutionErrors?: boolean;\n}\n\nexport interface ShellCaptureResult extends ShellCaptureProgress {\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\texecutionError?: ExecutionError;\n}\n\nfunction toExecutionError(error: unknown): ExecutionError {\n\tif (error instanceof ExecutionError) return error;\n\tconst cause = toError(error);\n\treturn new ExecutionError(\"unknown\", cause.message, error);\n}\n\nexport function sanitizeBinaryOutput(str: string): string {\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\tconst code = char.codePointAt(0);\n\t\t\tif (code === undefined) return false;\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\t\t\tif (code <= 0x1f) return false;\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n\nfunction trimToLastUtf8Bytes(text: string, maxBytes: number, encoder: { encode(input?: string): Uint8Array }): string {\n\tconst bytes = encoder.encode(text);\n\tif (bytes.byteLength <= maxBytes) return text;\n\tlet start = bytes.byteLength - maxBytes;\n\twhile (start < bytes.byteLength && ((bytes[start] ?? 0) & 0xc0) === 0x80) start++;\n\treturn new TextDecoder().decode(bytes.subarray(start));\n}\n\nexport async function executeShellWithCapture(\n\tenv: ExecutionEnv,\n\tcommand: string,\n\toptions?: ShellCaptureOptions,\n): Promise<Result<ShellCaptureResult, ExecutionError>> {\n\tlet tailOutput = \"\";\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\tconst encoder = new TextEncoder();\n\n\tlet totalBytes = 0;\n\tlet completedLines = 0;\n\tlet hasOpenLine = false;\n\tlet currentLineBytes = 0;\n\tlet fullOutputPath: string | undefined;\n\tlet fullOutputRequested = false;\n\tlet acceptingOutput = true;\n\tlet writeChain: Promise<Result<void, ExecutionError>> = Promise.resolve(ok(undefined));\n\tlet captureError: ExecutionError | undefined;\n\n\tconst appendFullOutput = (text: string): void => {\n\t\tif (!fullOutputRequested || captureError) return;\n\t\twriteChain = writeChain.then(async (previous) => {\n\t\t\tif (!previous.ok) return previous;\n\t\t\tif (!fullOutputPath) return err(new ExecutionError(\"unknown\", \"Full output path was not created\"));\n\t\t\tconst appendResult = await env.appendFile(fullOutputPath, text);\n\t\t\treturn appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));\n\t\t});\n\t};\n\n\tconst ensureFullOutputFile = (initialContent: string): void => {\n\t\tif (fullOutputRequested || captureError) return;\n\t\tfullOutputRequested = true;\n\t\twriteChain = writeChain.then(async (previous) => {\n\t\t\tif (!previous.ok) return previous;\n\t\t\tconst tempFile = await env.createTempFile({ prefix: \"bash-\", suffix: \".log\" });\n\t\t\tif (!tempFile.ok) return err(toExecutionError(tempFile.error));\n\t\t\tfullOutputPath = tempFile.value;\n\t\t\tconst appendResult = await env.appendFile(tempFile.value, initialContent);\n\t\t\treturn appendResult.ok ? ok(undefined) : err(toExecutionError(appendResult.error));\n\t\t});\n\t};\n\n\tconst cleanupFullOutput = async (): Promise<ExecutionError | undefined> => {\n\t\tif (!fullOutputPath) return undefined;\n\t\tconst result = await env.remove(fullOutputPath, { force: true });\n\t\treturn result.ok ? undefined : toExecutionError(result.error);\n\t};\n\n\tconst withCleanupError = (primary: ExecutionError, cleanupError: ExecutionError | undefined): ExecutionError =>\n\t\tcleanupError\n\t\t\t? new ExecutionError(primary.code, primary.message, new AggregateError([primary, cleanupError]))\n\t\t\t: primary;\n\n\tconst createProgress = (): ShellCaptureProgress => {\n\t\tconst tailTruncation = truncateTail(tailOutput);\n\t\tconst totalLines = completedLines + (hasOpenLine ? 1 : 0);\n\t\tconst truncated = totalLines > DEFAULT_MAX_LINES || totalBytes > DEFAULT_MAX_BYTES;\n\t\tconst truncation: TruncationResult = {\n\t\t\t...tailTruncation,\n\t\t\ttruncated,\n\t\t\ttruncatedBy: truncated\n\t\t\t\t? (tailTruncation.truncatedBy ?? (totalBytes > DEFAULT_MAX_BYTES ? \"bytes\" : \"lines\"))\n\t\t\t\t: null,\n\t\t\ttotalLines,\n\t\t\ttotalBytes,\n\t\t};\n\t\treturn {\n\t\t\toutput: truncated ? truncation.content : tailOutput,\n\t\t\ttruncation,\n\t\t\tfullOutputPath,\n\t\t\tlastLineBytes: currentLineBytes,\n\t\t};\n\t};\n\n\tconst onChunk = async (chunk: string): Promise<void> => {\n\t\tif (!acceptingOutput) return;\n\t\ttry {\n\t\t\tconst text = sanitizeBinaryOutput(chunk).replace(/\\r/g, \"\");\n\t\t\tconst textBytes = encoder.encode(text).byteLength;\n\t\t\ttotalBytes += textBytes;\n\t\t\tconst newlineCount = text.split(\"\\n\").length - 1;\n\t\t\tcompletedLines += newlineCount;\n\t\t\tconst lastNewline = text.lastIndexOf(\"\\n\");\n\t\t\tif (lastNewline >= 0) {\n\t\t\t\tconst trailingText = text.slice(lastNewline + 1);\n\t\t\t\tcurrentLineBytes = encoder.encode(trailingText).byteLength;\n\t\t\t\thasOpenLine = trailingText.length > 0;\n\t\t\t} else if (text.length > 0) {\n\t\t\t\tcurrentLineBytes += textBytes;\n\t\t\t\thasOpenLine = true;\n\t\t\t}\n\n\t\t\ttailOutput += text;\n\t\t\tconst totalLines = completedLines + (hasOpenLine ? 1 : 0);\n\t\t\tif ((totalBytes > DEFAULT_MAX_BYTES || totalLines > DEFAULT_MAX_LINES) && !fullOutputRequested) {\n\t\t\t\tensureFullOutputFile(tailOutput);\n\t\t\t} else if (fullOutputRequested) {\n\t\t\t\tappendFullOutput(text);\n\t\t\t}\n\t\t\ttailOutput = trimToLastUtf8Bytes(tailOutput, maxOutputBytes, encoder);\n\t\t\tawait options?.onChunk?.(text, createProgress);\n\t\t} catch (error) {\n\t\t\tcaptureError = new ExecutionError(\"callback_error\", toError(error).message, error);\n\t\t\t// Keep the harness execution rejected. Merely recording the callback error\n\t\t\t// here turns a failed observer into a fulfilled capture result, preventing\n\t\t\t// the harness abort path from killing the child process.\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await env.exec(command, {\n\t\t\tcwd: options?.cwd,\n\t\t\tenv: options?.env,\n\t\t\tinheritEnv: options?.inheritEnv,\n\t\t\ttimeout: options?.timeout,\n\t\t\tabortSignal: options?.abortSignal,\n\t\t\tonStdout: onChunk,\n\t\t\tonStderr: onChunk,\n\t\t});\n\t\tacceptingOutput = false;\n\t\tlet progress = createProgress();\n\t\tif (progress.truncation.truncated && !fullOutputRequested) ensureFullOutputFile(tailOutput);\n\t\tconst writeResult = await writeChain;\n\t\tif (!writeResult.ok) {\n\t\t\treturn err(withCleanupError(writeResult.error, await cleanupFullOutput()));\n\t\t}\n\t\tif (captureError) {\n\t\t\treturn err(withCleanupError(captureError, await cleanupFullOutput()));\n\t\t}\n\t\tprogress = createProgress();\n\n\t\tif (!result.ok) {\n\t\t\tif (result.error.code === \"aborted\" || options?.abortSignal?.aborted) {\n\t\t\t\tconst cleanupError = await cleanupFullOutput();\n\t\t\t\tif (cleanupError) return err(cleanupError);\n\t\t\t\treturn ok({\n\t\t\t\t\t...progress,\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: progress.truncation.truncated,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (options?.returnExecutionErrors) {\n\t\t\t\treturn ok({\n\t\t\t\t\t...progress,\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: false,\n\t\t\t\t\ttruncated: progress.truncation.truncated,\n\t\t\t\t\texecutionError: result.error,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn err(result.error);\n\t\t}\n\t\tconst cancelled = options?.abortSignal?.aborted ?? false;\n\t\treturn ok({\n\t\t\t...progress,\n\t\t\texitCode: cancelled ? undefined : result.value.exitCode,\n\t\t\tcancelled,\n\t\t\ttruncated: progress.truncation.truncated,\n\t\t});\n\t} catch (error) {\n\t\tacceptingOutput = false;\n\t\tconst primaryError = toExecutionError(error);\n\t\treturn err(withCleanupError(primaryError, await cleanupFullOutput()));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"shell-output.js","sourceRoot":"","sources":["../../../src/harness/utils/shell-output.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,GAAG,EACH,EAAE,GAIF,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAyB,YAAY,EAAE,MAAM,eAAe,CAAC;AA6B1G,SAAS,YAAY,CAAC,MAAuB;IAC5C,OAAO;QACN,MAAM,EAAE,MAAM,CAAC,IAAI;QACnB,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE;QAC1D,GAAG,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QAC/E,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,CAAC;KACxC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,GAAiB,EACjB,OAAe,EACf,OAAwC,EACxC,OAAgB;IAEhB,IAAI,MAAmC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAC5B,OAAO,EACP;QACC,GAAG,EAAE,OAAO,EAAE,GAAG;QACjB,GAAG,EAAE,OAAO,EAAE,GAAG;QACjB,UAAU,EAAE,OAAO,EAAE,UAAU;QAC/B,OAAO,EAAE,OAAO,EAAE,OAAO;QACzB,OAAO,EAAE;YACR,MAAM,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE;YACpF,KAAK,EAAE,IAAI;SACX;QACD,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE;YACnC,MAAM,QAAQ,GAAG,MAAM,CAAC;YACxB,MAAM,GAAG,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAChD,MAAM,KAAK,GACV,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;gBAClD,CAAC,CAAC,MAAM,CAAC,IAAI;gBACb,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS;oBACpD,CAAC,CAAC,MAAM,CAAC,IAAI;oBACb,CAAC,CAAC,SAAS,CAAC;YACf,mEAAmE;YACnE,yEAAyE;YACzE,2DAA2D;YAC3D,yEAAyE;YACzE,IAAI,KAAK;gBAAE,OAAO,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,MAAO,CAAC,EAAE,aAAa,CAAC,CAAC;YACxF,OAAO,SAAS,CAAC;QAClB,CAAC;KACD,EACD,OAAO,CACP,CAAC;IAEF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;QACpD,MAAM,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACxC,CAAC;IACD,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;YACrE,OAAO,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5G,CAAC;QACD,IAAI,OAAO,EAAE,qBAAqB,EAAE,CAAC;YACpC,OAAO,EAAE,CAAC;gBACT,GAAG,QAAQ;gBACX,QAAQ,EAAE,SAAS;gBACnB,SAAS,EAAE,KAAK;gBAChB,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS;gBACxC,cAAc,EAAE,MAAM,CAAC,KAAK;aAC5B,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,CAAC;QACT,GAAG,QAAQ;QACX,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC/B,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS;KAC5C,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,mBAAmB,IAAI,oBAAoB,EAAE,CAAC","sourcesContent":["import type { Context } from \"../context.ts\";\nimport {\n\ttype ExecutionEnv,\n\ttype ExecutionError,\n\terr,\n\tok,\n\ttype Result,\n\ttype ShellExecOptions,\n\ttype ShellOutputView,\n} from \"../types.ts\";\nimport { applyShellOutputUpdate, sanitizeShellOutput } from \"./output-capture.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type TruncationResult, truncateTail } from \"./truncate.ts\";\n\nexport interface ShellCaptureProgress {\n\toutput: string;\n\ttruncation: TruncationResult;\n\tfullOutputPath?: string;\n\tlastLineBytes: number;\n}\n\nexport interface ShellCaptureOptions extends Omit<ShellExecOptions, \"capture\" | \"onUpdate\"> {\n\t/**\n\t * Called with each incremental output chunk.\n\t *\n\t * A returned promise is awaited by the execution environment before the result settles, so an\n\t * observer that rejects fails the execution with a `callback_error` carrying the rejected value\n\t * as its `cause` instead of escaping as an unhandled rejection.\n\t */\n\tonChunk?: (chunk: string, getProgress: () => ShellCaptureProgress, context: Context) => void | PromiseLike<void>;\n\t/** Return shell execution failures with captured output instead of as a failed Result. */\n\treturnExecutionErrors?: boolean;\n}\n\nexport interface ShellCaptureResult extends ShellCaptureProgress {\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\texecutionError?: ExecutionError;\n}\n\nfunction progressFrom(output: ShellOutputView): ShellCaptureProgress {\n\treturn {\n\t\toutput: output.text,\n\t\ttruncation: { content: output.text, ...output.truncation },\n\t\t...(output.spillPath === undefined ? {} : { fullOutputPath: output.spillPath }),\n\t\tlastLineBytes: output.lastLineBytes ?? 0,\n\t};\n}\n\n/**\n * Compatibility collector for callers that need one bounded final view.\n * Source-side capture, adaptive publication, and spilling remain owned by the\n * execution environment.\n */\nexport async function executeShellWithCapture(\n\tenv: ExecutionEnv,\n\tcommand: string,\n\toptions: ShellCaptureOptions | undefined,\n\tcontext: Context,\n): Promise<Result<ShellCaptureResult, ExecutionError>> {\n\tlet output: ShellOutputView | undefined;\n\tconst result = await env.exec(\n\t\tcommand,\n\t\t{\n\t\t\tcwd: options?.cwd,\n\t\t\tenv: options?.env,\n\t\t\tinheritEnv: options?.inheritEnv,\n\t\t\ttimeout: options?.timeout,\n\t\t\tcapture: {\n\t\t\t\tlimits: { maxBytes: DEFAULT_MAX_BYTES, maxLines: DEFAULT_MAX_LINES, retain: \"tail\" },\n\t\t\t\tspill: true,\n\t\t\t},\n\t\t\tonUpdate: (update, updateContext) => {\n\t\t\t\tconst previous = output;\n\t\t\t\toutput = applyShellOutputUpdate(output, update);\n\t\t\t\tconst chunk =\n\t\t\t\t\tupdate.kind === \"append\" || update.kind === \"slide\"\n\t\t\t\t\t\t? update.text\n\t\t\t\t\t\t: update.kind === \"replace\" && previous === undefined\n\t\t\t\t\t\t\t? output.text\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\t// A metadata-only update and a post-cap replacement contain no new\n\t\t\t\t// incremental chunk. Reporting their complete view would duplicate bytes\n\t\t\t\t// for callers that accumulate this compatibility callback.\n\t\t\t\t// The observer's settlement is returned so the environment can await it.\n\t\t\t\tif (chunk) return options?.onChunk?.(chunk, () => progressFrom(output!), updateContext);\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t},\n\t\tcontext,\n\t);\n\n\tif (output === undefined) {\n\t\tconst { content, ...truncation } = truncateTail(\"\");\n\t\toutput = { text: content, truncation };\n\t}\n\tconst progress = progressFrom(output);\n\tif (!result.ok) {\n\t\tif (result.error.code === \"aborted\" || context.abortSignal?.aborted) {\n\t\t\treturn ok({ ...progress, exitCode: undefined, cancelled: true, truncated: progress.truncation.truncated });\n\t\t}\n\t\tif (options?.returnExecutionErrors) {\n\t\t\treturn ok({\n\t\t\t\t...progress,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: false,\n\t\t\t\ttruncated: progress.truncation.truncated,\n\t\t\t\texecutionError: result.error,\n\t\t\t});\n\t\t}\n\t\treturn err(result.error);\n\t}\n\treturn ok({\n\t\t...progress,\n\t\texitCode: result.value.exitCode,\n\t\tcancelled: false,\n\t\ttruncated: result.value.truncation.truncated,\n\t});\n}\n\nexport { sanitizeShellOutput as sanitizeBinaryOutput };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../../src/harness/utils/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAChC,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;IACtC,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,eAAe,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,qBAAqB,EAAE,OAAO,CAAC;IAC/B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../../src/harness/utils/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAChC,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;IACtC,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,eAAe,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,qBAAqB,EAAE,OAAO,CAAC;IAC/B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AASD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0BtD;AAgCD;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAkF/F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAyE/F;AA2CD;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAA6B,GACrC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAKzC"}
|