@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
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { constants,
|
|
4
|
-
import { access, appendFile, lstat, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, writeFile, } from "node:fs/promises";
|
|
5
|
-
import { homedir, tmpdir } from "node:os";
|
|
3
|
+
import { constants, createWriteStream, existsSync } from "node:fs";
|
|
4
|
+
import { access, appendFile, lstat, mkdir, mkdtemp, open as openFile, readdir, readFile, realpath, rename, rm, writeFile, } from "node:fs/promises";
|
|
5
|
+
import { homedir, constants as osConstants, tmpdir } from "node:os";
|
|
6
6
|
import { basename, isAbsolute, join, resolve } from "node:path";
|
|
7
|
-
import { createInterface } from "node:readline";
|
|
8
7
|
import { fileURLToPath } from "node:url";
|
|
9
8
|
import { ExecutionError, err, FileError, ok, toError, } from "../types.js";
|
|
9
|
+
import { OutputCapture } from "../utils/output-capture.js";
|
|
10
10
|
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
11
11
|
const MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;
|
|
12
12
|
const EXIT_STDIO_GRACE_MS = 100;
|
|
13
|
+
const SPILL_HIGH_WATER_MARK = 8 * 1024 * 1024;
|
|
13
14
|
function resolveTimeoutMs(timeout) {
|
|
14
15
|
if (timeout === undefined)
|
|
15
16
|
return ok(undefined);
|
|
@@ -289,11 +290,12 @@ function killProcessTree(pid) {
|
|
|
289
290
|
}
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
|
-
function waitForChildProcess(child) {
|
|
293
|
+
function waitForChildProcess(child, spillIsDraining) {
|
|
293
294
|
return new Promise((resolvePromise, reject) => {
|
|
294
295
|
let settled = false;
|
|
295
296
|
let exited = false;
|
|
296
297
|
let exitCode = null;
|
|
298
|
+
let exitSignal = null;
|
|
297
299
|
let postExitTimer;
|
|
298
300
|
let stdoutEnded = child.stdout === null;
|
|
299
301
|
let stderrEnded = child.stderr === null;
|
|
@@ -308,23 +310,28 @@ function waitForChildProcess(child) {
|
|
|
308
310
|
child.stdout?.removeListener("data", onData);
|
|
309
311
|
child.stderr?.removeListener("data", onData);
|
|
310
312
|
};
|
|
311
|
-
const finalize = (
|
|
313
|
+
const finalize = () => {
|
|
312
314
|
if (settled)
|
|
313
315
|
return;
|
|
314
316
|
settled = true;
|
|
315
317
|
cleanup();
|
|
316
318
|
child.stdout?.destroy();
|
|
317
319
|
child.stderr?.destroy();
|
|
318
|
-
resolvePromise(code);
|
|
320
|
+
resolvePromise({ code: exitCode, signal: exitSignal });
|
|
319
321
|
};
|
|
320
322
|
const maybeFinalizeAfterExit = () => {
|
|
321
323
|
if (exited && stdoutEnded && stderrEnded)
|
|
322
|
-
finalize(
|
|
324
|
+
finalize();
|
|
323
325
|
};
|
|
324
326
|
const armIdleTimer = () => {
|
|
325
327
|
if (postExitTimer)
|
|
326
328
|
clearTimeout(postExitTimer);
|
|
327
|
-
postExitTimer = setTimeout(() =>
|
|
329
|
+
postExitTimer = setTimeout(() => {
|
|
330
|
+
if (spillIsDraining())
|
|
331
|
+
armIdleTimer();
|
|
332
|
+
else
|
|
333
|
+
finalize();
|
|
334
|
+
}, EXIT_STDIO_GRACE_MS);
|
|
328
335
|
};
|
|
329
336
|
const onData = () => {
|
|
330
337
|
if (exited && !settled)
|
|
@@ -345,14 +352,19 @@ function waitForChildProcess(child) {
|
|
|
345
352
|
cleanup();
|
|
346
353
|
reject(error);
|
|
347
354
|
};
|
|
348
|
-
const onExit = (code) => {
|
|
355
|
+
const onExit = (code, signal) => {
|
|
349
356
|
exited = true;
|
|
350
357
|
exitCode = code;
|
|
358
|
+
exitSignal = signal;
|
|
351
359
|
maybeFinalizeAfterExit();
|
|
352
360
|
if (!settled)
|
|
353
361
|
armIdleTimer();
|
|
354
362
|
};
|
|
355
|
-
const onClose = (code) =>
|
|
363
|
+
const onClose = (code, signal) => {
|
|
364
|
+
exitCode = code;
|
|
365
|
+
exitSignal = signal;
|
|
366
|
+
finalize();
|
|
367
|
+
};
|
|
356
368
|
child.stdout?.once("end", onStdoutEnd);
|
|
357
369
|
child.stderr?.once("end", onStderrEnd);
|
|
358
370
|
child.stdout?.on("data", onData);
|
|
@@ -363,6 +375,71 @@ function waitForChildProcess(child) {
|
|
|
363
375
|
});
|
|
364
376
|
}
|
|
365
377
|
const NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS = 5_000;
|
|
378
|
+
/** Strict LF reader; Node readline does not report whether its final line was newline-terminated. */
|
|
379
|
+
class NodeTextLineReader {
|
|
380
|
+
constructor(file, path) {
|
|
381
|
+
this.decoder = new TextDecoder();
|
|
382
|
+
this.chunk = new Uint8Array(64 * 1024);
|
|
383
|
+
this.byteOffset = 0;
|
|
384
|
+
this.buffered = "";
|
|
385
|
+
this.ended = false;
|
|
386
|
+
this.closed = false;
|
|
387
|
+
this.file = file;
|
|
388
|
+
this.path = path;
|
|
389
|
+
}
|
|
390
|
+
async readLine(context) {
|
|
391
|
+
const aborted = abortResult(context.abortSignal, this.path);
|
|
392
|
+
if (aborted)
|
|
393
|
+
return aborted;
|
|
394
|
+
if (this.closed)
|
|
395
|
+
return err(new FileError("invalid", "Text line reader is closed", this.path));
|
|
396
|
+
try {
|
|
397
|
+
while (true) {
|
|
398
|
+
const newline = this.buffered.indexOf("\n");
|
|
399
|
+
if (newline !== -1) {
|
|
400
|
+
const text = this.buffered.slice(0, newline);
|
|
401
|
+
this.buffered = this.buffered.slice(newline + 1);
|
|
402
|
+
return ok({ text, terminated: true });
|
|
403
|
+
}
|
|
404
|
+
if (this.ended) {
|
|
405
|
+
if (this.buffered.length === 0)
|
|
406
|
+
return ok(undefined);
|
|
407
|
+
const text = this.buffered;
|
|
408
|
+
this.buffered = "";
|
|
409
|
+
return ok({ text, terminated: false });
|
|
410
|
+
}
|
|
411
|
+
// Explicit positions allow an aborted read to be retried without skipping bytes.
|
|
412
|
+
const { bytesRead } = await this.file.read(this.chunk, 0, this.chunk.length, this.byteOffset);
|
|
413
|
+
const afterReadAbort = abortResult(context.abortSignal, this.path);
|
|
414
|
+
if (afterReadAbort)
|
|
415
|
+
return afterReadAbort;
|
|
416
|
+
this.byteOffset += bytesRead;
|
|
417
|
+
if (bytesRead === 0) {
|
|
418
|
+
this.buffered += this.decoder.decode();
|
|
419
|
+
this.ended = true;
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
this.buffered += this.decoder.decode(this.chunk.subarray(0, bytesRead), { stream: true });
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
return err(toFileError(error, this.path));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async close(_context) {
|
|
431
|
+
if (this.closed)
|
|
432
|
+
return;
|
|
433
|
+
this.closed = true;
|
|
434
|
+
this.buffered = "";
|
|
435
|
+
try {
|
|
436
|
+
await this.file.close();
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
// Closing is best-effort, including after cancellation or an earlier I/O failure.
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
366
443
|
export class NodeExecutionEnv {
|
|
367
444
|
constructor(options) {
|
|
368
445
|
this.activeChildPids = new Set();
|
|
@@ -370,14 +447,15 @@ export class NodeExecutionEnv {
|
|
|
370
447
|
this.shellPath = options.shellPath;
|
|
371
448
|
this.shellEnv = options.shellEnv;
|
|
372
449
|
}
|
|
373
|
-
async absolutePath(path) {
|
|
450
|
+
async absolutePath(path, _context) {
|
|
374
451
|
return ok(resolvePath(this.cwd, path));
|
|
375
452
|
}
|
|
376
|
-
async joinPath(parts) {
|
|
453
|
+
async joinPath(parts, _context) {
|
|
377
454
|
return ok(join(...parts));
|
|
378
455
|
}
|
|
379
|
-
async exec(command, options) {
|
|
380
|
-
|
|
456
|
+
async exec(command, options, context) {
|
|
457
|
+
const signal = context.abortSignal;
|
|
458
|
+
if (signal?.aborted)
|
|
381
459
|
return err(new ExecutionError("aborted", "aborted"));
|
|
382
460
|
const timeoutMsResult = resolveTimeoutMs(options?.timeout);
|
|
383
461
|
if (!timeoutMsResult.ok)
|
|
@@ -395,31 +473,137 @@ export class NodeExecutionEnv {
|
|
|
395
473
|
return err(new ExecutionError("spawn_error", `Working directory does not exist: ${cwd}\nCannot execute bash commands.`, cause));
|
|
396
474
|
}
|
|
397
475
|
return await new Promise((resolvePromise) => {
|
|
398
|
-
let stdout = "";
|
|
399
|
-
let stderr = "";
|
|
400
476
|
let settled = false;
|
|
401
477
|
let timedOut = false;
|
|
402
478
|
let callbackError;
|
|
479
|
+
let spillError;
|
|
403
480
|
const callbackPromises = new Set();
|
|
404
481
|
let child;
|
|
405
482
|
let timeoutId;
|
|
483
|
+
const spillPrefix = [];
|
|
484
|
+
let spillPath;
|
|
485
|
+
const spillQueue = [];
|
|
486
|
+
let spillStart;
|
|
487
|
+
let spillStream;
|
|
488
|
+
let spillBackpressured = false;
|
|
406
489
|
const onAbort = () => {
|
|
407
|
-
if (child?.pid)
|
|
490
|
+
if (child?.pid)
|
|
408
491
|
killProcessTree(child.pid);
|
|
409
|
-
}
|
|
410
492
|
};
|
|
493
|
+
const failCallback = (error) => {
|
|
494
|
+
if (callbackError !== undefined)
|
|
495
|
+
return;
|
|
496
|
+
const cause = toError(error);
|
|
497
|
+
// The raw rejection value is the cause so observers that reject with a non-Error
|
|
498
|
+
// payload can still be identified by callers.
|
|
499
|
+
callbackError = new ExecutionError("callback_error", cause.message, error);
|
|
500
|
+
onAbort();
|
|
501
|
+
};
|
|
502
|
+
// An observer may return a promise. Track it so a rejection fails the execution with a
|
|
503
|
+
// callback error instead of escaping as an unhandled rejection.
|
|
504
|
+
const observeUpdate = options?.onUpdate;
|
|
505
|
+
const trackedOnUpdate = observeUpdate === undefined
|
|
506
|
+
? undefined
|
|
507
|
+
: (update, updateContext) => {
|
|
508
|
+
const settlement = observeUpdate(update, updateContext);
|
|
509
|
+
if (settlement === undefined || settlement === null)
|
|
510
|
+
return;
|
|
511
|
+
const tracked = Promise.resolve(settlement).then(() => undefined, (error) => {
|
|
512
|
+
failCallback(error);
|
|
513
|
+
});
|
|
514
|
+
callbackPromises.add(tracked);
|
|
515
|
+
void tracked.finally(() => callbackPromises.delete(tracked));
|
|
516
|
+
};
|
|
517
|
+
let capture;
|
|
518
|
+
try {
|
|
519
|
+
capture = new OutputCapture(options?.capture, context, {
|
|
520
|
+
onUpdate: trackedOnUpdate,
|
|
521
|
+
onError: failCallback,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
const cause = toError(error);
|
|
526
|
+
resolvePromise(err(new ExecutionError("unknown", cause.message, cause)));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
411
529
|
const settle = (result) => {
|
|
530
|
+
if (settled)
|
|
531
|
+
return;
|
|
532
|
+
settled = true;
|
|
412
533
|
if (timeoutId)
|
|
413
534
|
clearTimeout(timeoutId);
|
|
414
|
-
if (
|
|
415
|
-
|
|
535
|
+
if (signal)
|
|
536
|
+
signal.removeEventListener("abort", onAbort);
|
|
416
537
|
if (child?.pid)
|
|
417
538
|
this.activeChildPids.delete(child.pid);
|
|
418
|
-
|
|
419
|
-
return;
|
|
420
|
-
settled = true;
|
|
539
|
+
capture.dispose();
|
|
421
540
|
resolvePromise(result);
|
|
422
541
|
};
|
|
542
|
+
const pauseOutput = () => {
|
|
543
|
+
child?.stdout?.pause();
|
|
544
|
+
child?.stderr?.pause();
|
|
545
|
+
};
|
|
546
|
+
const resumeOutput = () => {
|
|
547
|
+
if (callbackError || spillError || timedOut || signal?.aborted || spillBackpressured)
|
|
548
|
+
return;
|
|
549
|
+
child?.stdout?.resume();
|
|
550
|
+
child?.stderr?.resume();
|
|
551
|
+
};
|
|
552
|
+
const failSpill = (error) => {
|
|
553
|
+
if (spillError !== undefined)
|
|
554
|
+
return;
|
|
555
|
+
const cause = toError(error);
|
|
556
|
+
spillError = new ExecutionError("unknown", `Failed to preserve complete shell output: ${cause.message}`, cause);
|
|
557
|
+
spillBackpressured = false;
|
|
558
|
+
onAbort();
|
|
559
|
+
};
|
|
560
|
+
const writeSpill = (chunk) => {
|
|
561
|
+
if (spillStream === undefined || chunk.length === 0)
|
|
562
|
+
return;
|
|
563
|
+
if (spillStream.write(chunk) || spillBackpressured)
|
|
564
|
+
return;
|
|
565
|
+
spillBackpressured = true;
|
|
566
|
+
pauseOutput();
|
|
567
|
+
spillStream.once("drain", () => {
|
|
568
|
+
spillBackpressured = false;
|
|
569
|
+
resumeOutput();
|
|
570
|
+
});
|
|
571
|
+
};
|
|
572
|
+
const startSpill = (chunk) => {
|
|
573
|
+
if (spillStream !== undefined) {
|
|
574
|
+
writeSpill(chunk);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
spillQueue.push(chunk);
|
|
578
|
+
if (spillStart !== undefined)
|
|
579
|
+
return;
|
|
580
|
+
pauseOutput();
|
|
581
|
+
spillStart = (async () => {
|
|
582
|
+
const created = await this.createTempFile({ prefix: "pi-output-", suffix: ".log" }, context);
|
|
583
|
+
if (!created.ok)
|
|
584
|
+
throw created.error;
|
|
585
|
+
spillPath = created.value;
|
|
586
|
+
capture.setSpillPath(spillPath);
|
|
587
|
+
spillStream = createWriteStream(spillPath, { flags: "a", highWaterMark: SPILL_HIGH_WATER_MARK });
|
|
588
|
+
spillStream.on("error", failSpill);
|
|
589
|
+
for (const queued of spillQueue)
|
|
590
|
+
writeSpill(queued);
|
|
591
|
+
spillQueue.length = 0;
|
|
592
|
+
})()
|
|
593
|
+
.catch(failSpill)
|
|
594
|
+
.finally(resumeOutput);
|
|
595
|
+
};
|
|
596
|
+
const finishSpill = async () => {
|
|
597
|
+
await spillStart;
|
|
598
|
+
const stream = spillStream;
|
|
599
|
+
if (stream === undefined || spillError !== undefined || stream.destroyed)
|
|
600
|
+
return;
|
|
601
|
+
await new Promise((resolveFinish) => {
|
|
602
|
+
stream.once("error", () => resolveFinish());
|
|
603
|
+
stream.once("finish", resolveFinish);
|
|
604
|
+
stream.end();
|
|
605
|
+
});
|
|
606
|
+
};
|
|
423
607
|
try {
|
|
424
608
|
const commandFromStdin = shellConfig.value.commandTransport === "stdin";
|
|
425
609
|
child = spawn(shellConfig.value.shell, commandFromStdin ? shellConfig.value.args : [...shellConfig.value.args, command], {
|
|
@@ -442,64 +626,75 @@ export class NodeExecutionEnv {
|
|
|
442
626
|
return;
|
|
443
627
|
}
|
|
444
628
|
timeoutId =
|
|
445
|
-
timeoutMs
|
|
446
|
-
?
|
|
629
|
+
timeoutMs === undefined
|
|
630
|
+
? undefined
|
|
631
|
+
: setTimeout(() => {
|
|
447
632
|
timedOut = true;
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
: undefined;
|
|
453
|
-
if (options?.abortSignal) {
|
|
454
|
-
if (options.abortSignal.aborted) {
|
|
633
|
+
onAbort();
|
|
634
|
+
}, timeoutMs);
|
|
635
|
+
if (signal) {
|
|
636
|
+
if (signal.aborted)
|
|
455
637
|
onAbort();
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
options.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
459
|
-
}
|
|
638
|
+
else
|
|
639
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
460
640
|
}
|
|
461
|
-
|
|
462
|
-
child.stderr?.setEncoding("utf8");
|
|
463
|
-
const handleChunk = (chunk, callback, append) => {
|
|
464
|
-
append(chunk);
|
|
465
|
-
if (callback === undefined)
|
|
466
|
-
return;
|
|
467
|
-
const callbackPromise = Promise.resolve()
|
|
468
|
-
.then(() => callback(chunk))
|
|
469
|
-
.catch((error) => {
|
|
470
|
-
if (!callbackError) {
|
|
471
|
-
const cause = toError(error);
|
|
472
|
-
callbackError = new ExecutionError("callback_error", cause.message, error);
|
|
473
|
-
onAbort();
|
|
474
|
-
}
|
|
475
|
-
throw error;
|
|
476
|
-
});
|
|
477
|
-
callbackPromises.add(callbackPromise);
|
|
478
|
-
void callbackPromise.then(() => callbackPromises.delete(callbackPromise), () => callbackPromises.delete(callbackPromise));
|
|
479
|
-
};
|
|
480
|
-
child.stdout?.on("data", (chunk) => handleChunk(chunk, options?.onStdout, (value) => (stdout += value)));
|
|
481
|
-
child.stderr?.on("data", (chunk) => handleChunk(chunk, options?.onStderr, (value) => (stderr += value)));
|
|
482
|
-
void waitForChildProcess(child).then(async (code) => {
|
|
483
|
-
// Normal command completion must not be held hostage by an observer that
|
|
484
|
-
// never settles. Abort completion uses the shorter cancellation path above;
|
|
485
|
-
// this generous bound preserves slow, legitimate callbacks without hanging
|
|
486
|
-
// the execution forever.
|
|
487
|
-
const callbacksSettled = Promise.allSettled([...callbackPromises]);
|
|
488
|
-
let callbackTimeout;
|
|
489
|
-
const callbackBound = new Promise((resolve) => {
|
|
490
|
-
callbackTimeout = setTimeout(() => resolve(false), NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS);
|
|
491
|
-
callbackTimeout.unref?.();
|
|
492
|
-
});
|
|
641
|
+
const feed = (chunk) => {
|
|
493
642
|
try {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
643
|
+
const wasTruncated = capture.truncated;
|
|
644
|
+
capture.push(chunk);
|
|
645
|
+
if (!options?.capture?.spill || chunk.length === 0)
|
|
497
646
|
return;
|
|
647
|
+
if (spillPath !== undefined || wasTruncated) {
|
|
648
|
+
startSpill(chunk);
|
|
498
649
|
}
|
|
650
|
+
else if (capture.truncated) {
|
|
651
|
+
for (const prefix of spillPrefix)
|
|
652
|
+
startSpill(prefix);
|
|
653
|
+
spillPrefix.length = 0;
|
|
654
|
+
startSpill(chunk);
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
spillPrefix.push(chunk);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
failCallback(error);
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
child.stdout?.on("data", feed);
|
|
665
|
+
child.stderr?.on("data", feed);
|
|
666
|
+
void waitForChildProcess(child, () => spillError === undefined &&
|
|
667
|
+
spillStart !== undefined &&
|
|
668
|
+
(spillStream === undefined || spillBackpressured)).then(async ({ code, signal: exitSignal }) => {
|
|
669
|
+
await finishSpill();
|
|
670
|
+
try {
|
|
671
|
+
capture.finish();
|
|
672
|
+
capture.flush();
|
|
499
673
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
674
|
+
catch (error) {
|
|
675
|
+
failCallback(error);
|
|
676
|
+
}
|
|
677
|
+
// Normal command completion must not be held hostage by an observer that never
|
|
678
|
+
// settles. Abort completion uses the shorter cancellation path above; this generous
|
|
679
|
+
// bound preserves slow, legitimate observers without hanging the execution forever.
|
|
680
|
+
if (callbackPromises.size > 0) {
|
|
681
|
+
const callbacksSettled = Promise.allSettled([...callbackPromises]);
|
|
682
|
+
let callbackTimeout;
|
|
683
|
+
const callbackBound = new Promise((resolve) => {
|
|
684
|
+
callbackTimeout = setTimeout(() => resolve(false), NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS);
|
|
685
|
+
callbackTimeout.unref?.();
|
|
686
|
+
});
|
|
687
|
+
try {
|
|
688
|
+
const observersSettled = await Promise.race([callbacksSettled.then(() => true), callbackBound]);
|
|
689
|
+
if (!observersSettled && !callbackError) {
|
|
690
|
+
settle(err(new ExecutionError("callback_error", `Output callback did not settle within ${NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS}ms`)));
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
finally {
|
|
695
|
+
if (callbackTimeout)
|
|
696
|
+
clearTimeout(callbackTimeout);
|
|
697
|
+
}
|
|
503
698
|
}
|
|
504
699
|
if (callbackError) {
|
|
505
700
|
settle(err(callbackError));
|
|
@@ -509,104 +704,135 @@ export class NodeExecutionEnv {
|
|
|
509
704
|
settle(err(new ExecutionError("timeout", `timeout:${options?.timeout}`)));
|
|
510
705
|
return;
|
|
511
706
|
}
|
|
512
|
-
if (
|
|
707
|
+
if (signal?.aborted) {
|
|
513
708
|
settle(err(new ExecutionError("aborted", "aborted")));
|
|
514
709
|
return;
|
|
515
710
|
}
|
|
516
|
-
|
|
711
|
+
if (spillError) {
|
|
712
|
+
settle(err(spillError));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const output = capture.snapshot();
|
|
716
|
+
// A process killed by a signal (e.g. OOM killer) has no exit code; map it
|
|
717
|
+
// to the conventional 128 + signal number so callers do not mistake it
|
|
718
|
+
// for a successful exit.
|
|
719
|
+
const exitCode = code ?? (exitSignal ? 128 + (osConstants.signals[exitSignal] ?? 0) : 1);
|
|
720
|
+
settle(ok({
|
|
721
|
+
exitCode,
|
|
722
|
+
truncation: output.truncation,
|
|
723
|
+
...(output.spillPath === undefined ? {} : { spillPath: output.spillPath }),
|
|
724
|
+
...(output.lastLineBytes === undefined ? {} : { lastLineBytes: output.lastLineBytes }),
|
|
725
|
+
}));
|
|
517
726
|
}, (error) => settle(err(new ExecutionError("spawn_error", error.message, error))));
|
|
518
727
|
});
|
|
519
728
|
}
|
|
520
|
-
async
|
|
729
|
+
async openTextLineReader(path, context) {
|
|
521
730
|
const resolved = resolvePath(this.cwd, path);
|
|
522
|
-
const aborted = abortResult(abortSignal, resolved);
|
|
731
|
+
const aborted = abortResult(context.abortSignal, resolved);
|
|
523
732
|
if (aborted)
|
|
524
733
|
return aborted;
|
|
525
734
|
try {
|
|
526
|
-
|
|
735
|
+
const file = await openFile(resolved, "r");
|
|
736
|
+
const afterOpenAbort = abortResult(context.abortSignal, resolved);
|
|
737
|
+
if (afterOpenAbort) {
|
|
738
|
+
await file.close().catch(() => undefined);
|
|
739
|
+
return afterOpenAbort;
|
|
740
|
+
}
|
|
741
|
+
return ok(new NodeTextLineReader(file, resolved));
|
|
527
742
|
}
|
|
528
743
|
catch (error) {
|
|
529
744
|
return err(toFileError(error, resolved));
|
|
530
745
|
}
|
|
531
746
|
}
|
|
532
|
-
async
|
|
747
|
+
async readTextFile(path, context) {
|
|
533
748
|
const resolved = resolvePath(this.cwd, path);
|
|
534
|
-
const
|
|
749
|
+
const signal = context.abortSignal;
|
|
750
|
+
const aborted = abortResult(signal, resolved);
|
|
535
751
|
if (aborted)
|
|
536
752
|
return aborted;
|
|
753
|
+
try {
|
|
754
|
+
return ok(await readFile(resolved, { encoding: "utf8", signal }));
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
return err(toFileError(error, resolved));
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
async readTextLines(path, options, context) {
|
|
537
761
|
if (options?.maxLines !== undefined && options.maxLines <= 0)
|
|
538
762
|
return ok([]);
|
|
539
|
-
|
|
540
|
-
|
|
763
|
+
const opened = await this.openTextLineReader(path, context);
|
|
764
|
+
if (!opened.ok)
|
|
765
|
+
return opened;
|
|
766
|
+
const lines = [];
|
|
541
767
|
try {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
if (loopAbort)
|
|
548
|
-
return loopAbort;
|
|
549
|
-
lines.push(line);
|
|
550
|
-
if (options?.maxLines !== undefined && lines.length >= options.maxLines)
|
|
768
|
+
while (options?.maxLines === undefined || lines.length < options.maxLines) {
|
|
769
|
+
const line = await opened.value.readLine(context);
|
|
770
|
+
if (!line.ok)
|
|
771
|
+
return line;
|
|
772
|
+
if (line.value === undefined)
|
|
551
773
|
break;
|
|
774
|
+
lines.push(line.value.text);
|
|
552
775
|
}
|
|
553
|
-
const afterReadAbort = abortResult(options?.abortSignal, resolved);
|
|
554
|
-
if (afterReadAbort)
|
|
555
|
-
return afterReadAbort;
|
|
556
776
|
return ok(lines);
|
|
557
777
|
}
|
|
558
|
-
catch (error) {
|
|
559
|
-
return err(toFileError(error, resolved));
|
|
560
|
-
}
|
|
561
778
|
finally {
|
|
562
|
-
|
|
563
|
-
stream?.destroy();
|
|
779
|
+
await opened.value.close(context);
|
|
564
780
|
}
|
|
565
781
|
}
|
|
566
|
-
async readBinaryFile(path,
|
|
782
|
+
async readBinaryFile(path, context) {
|
|
567
783
|
const resolved = resolvePath(this.cwd, path);
|
|
568
|
-
const
|
|
784
|
+
const signal = context.abortSignal;
|
|
785
|
+
const aborted = abortResult(signal, resolved);
|
|
569
786
|
if (aborted)
|
|
570
787
|
return aborted;
|
|
571
788
|
try {
|
|
572
|
-
return ok(await readFile(resolved, { signal
|
|
789
|
+
return ok(await readFile(resolved, { signal }));
|
|
573
790
|
}
|
|
574
791
|
catch (error) {
|
|
575
792
|
return err(toFileError(error, resolved));
|
|
576
793
|
}
|
|
577
794
|
}
|
|
578
|
-
async writeFile(path, content,
|
|
795
|
+
async writeFile(path, content, context) {
|
|
579
796
|
const resolved = resolvePath(this.cwd, path);
|
|
580
|
-
const
|
|
797
|
+
const signal = context.abortSignal;
|
|
798
|
+
const aborted = abortResult(signal, resolved);
|
|
581
799
|
if (aborted)
|
|
582
800
|
return aborted;
|
|
583
801
|
try {
|
|
584
802
|
await mkdir(resolve(resolved, ".."), { recursive: true });
|
|
585
|
-
const afterMkdirAbort = abortResult(
|
|
803
|
+
const afterMkdirAbort = abortResult(signal, resolved);
|
|
586
804
|
if (afterMkdirAbort)
|
|
587
805
|
return afterMkdirAbort;
|
|
588
|
-
await writeFile(resolved, content, { signal
|
|
806
|
+
await writeFile(resolved, content, { signal });
|
|
589
807
|
return ok(undefined);
|
|
590
808
|
}
|
|
591
809
|
catch (error) {
|
|
592
810
|
return err(toFileError(error, resolved));
|
|
593
811
|
}
|
|
594
812
|
}
|
|
595
|
-
async appendFile(path, content) {
|
|
813
|
+
async appendFile(path, content, context) {
|
|
596
814
|
const resolved = resolvePath(this.cwd, path);
|
|
815
|
+
const signal = context.abortSignal;
|
|
816
|
+
const aborted = abortResult(signal, resolved);
|
|
817
|
+
if (aborted)
|
|
818
|
+
return aborted;
|
|
597
819
|
try {
|
|
598
820
|
await mkdir(resolve(resolved, ".."), { recursive: true });
|
|
821
|
+
const afterMkdirAbort = abortResult(signal, resolved);
|
|
822
|
+
if (afterMkdirAbort)
|
|
823
|
+
return afterMkdirAbort;
|
|
599
824
|
await appendFile(resolved, content);
|
|
600
|
-
|
|
825
|
+
const afterAppendAbort = abortResult(signal, resolved);
|
|
826
|
+
return afterAppendAbort ?? ok(undefined);
|
|
601
827
|
}
|
|
602
828
|
catch (error) {
|
|
603
829
|
return err(toFileError(error, resolved));
|
|
604
830
|
}
|
|
605
831
|
}
|
|
606
|
-
async renameFile(sourcePath, destinationPath,
|
|
832
|
+
async renameFile(sourcePath, destinationPath, context) {
|
|
607
833
|
const source = resolvePath(this.cwd, sourcePath);
|
|
608
834
|
const destination = resolvePath(this.cwd, destinationPath);
|
|
609
|
-
const aborted = abortResult(abortSignal, destination);
|
|
835
|
+
const aborted = abortResult(context.abortSignal, destination);
|
|
610
836
|
if (aborted)
|
|
611
837
|
return aborted;
|
|
612
838
|
try {
|
|
@@ -617,8 +843,11 @@ export class NodeExecutionEnv {
|
|
|
617
843
|
return err(toFileError(error, source));
|
|
618
844
|
}
|
|
619
845
|
}
|
|
620
|
-
async fileInfo(path) {
|
|
846
|
+
async fileInfo(path, context) {
|
|
621
847
|
const resolved = resolvePath(this.cwd, path);
|
|
848
|
+
const aborted = abortResult(context.abortSignal, resolved);
|
|
849
|
+
if (aborted)
|
|
850
|
+
return aborted;
|
|
622
851
|
try {
|
|
623
852
|
return fileInfoFromStats(resolved, await lstat(resolved));
|
|
624
853
|
}
|
|
@@ -626,16 +855,17 @@ export class NodeExecutionEnv {
|
|
|
626
855
|
return err(toFileError(error, resolved));
|
|
627
856
|
}
|
|
628
857
|
}
|
|
629
|
-
async listDir(path,
|
|
858
|
+
async listDir(path, context) {
|
|
630
859
|
const resolved = resolvePath(this.cwd, path);
|
|
631
|
-
const
|
|
860
|
+
const signal = context.abortSignal;
|
|
861
|
+
const aborted = abortResult(signal, resolved);
|
|
632
862
|
if (aborted)
|
|
633
863
|
return aborted;
|
|
634
864
|
try {
|
|
635
865
|
const entries = await readdir(resolved, { withFileTypes: true });
|
|
636
866
|
const infos = [];
|
|
637
867
|
for (const entry of entries) {
|
|
638
|
-
const loopAbort = abortResult(
|
|
868
|
+
const loopAbort = abortResult(signal, resolved);
|
|
639
869
|
if (loopAbort)
|
|
640
870
|
return loopAbort;
|
|
641
871
|
const entryPath = resolve(resolved, entry.name);
|
|
@@ -654,8 +884,11 @@ export class NodeExecutionEnv {
|
|
|
654
884
|
return err(toFileError(error, resolved));
|
|
655
885
|
}
|
|
656
886
|
}
|
|
657
|
-
async canonicalPath(path) {
|
|
887
|
+
async canonicalPath(path, context) {
|
|
658
888
|
const resolved = resolvePath(this.cwd, path);
|
|
889
|
+
const aborted = abortResult(context.abortSignal, resolved);
|
|
890
|
+
if (aborted)
|
|
891
|
+
return aborted;
|
|
659
892
|
try {
|
|
660
893
|
return ok(await realpath(resolved));
|
|
661
894
|
}
|
|
@@ -663,16 +896,19 @@ export class NodeExecutionEnv {
|
|
|
663
896
|
return err(toFileError(error, resolved));
|
|
664
897
|
}
|
|
665
898
|
}
|
|
666
|
-
async exists(path) {
|
|
667
|
-
const result = await this.fileInfo(path);
|
|
899
|
+
async exists(path, context) {
|
|
900
|
+
const result = await this.fileInfo(path, context);
|
|
668
901
|
if (result.ok)
|
|
669
902
|
return ok(true);
|
|
670
903
|
if (result.error.code === "not_found")
|
|
671
904
|
return ok(false);
|
|
672
905
|
return err(result.error);
|
|
673
906
|
}
|
|
674
|
-
async createDir(path, options) {
|
|
907
|
+
async createDir(path, options, context) {
|
|
675
908
|
const resolved = resolvePath(this.cwd, path);
|
|
909
|
+
const aborted = abortResult(context.abortSignal, resolved);
|
|
910
|
+
if (aborted)
|
|
911
|
+
return aborted;
|
|
676
912
|
try {
|
|
677
913
|
await mkdir(resolved, { recursive: options?.recursive ?? true });
|
|
678
914
|
return ok(undefined);
|
|
@@ -681,8 +917,11 @@ export class NodeExecutionEnv {
|
|
|
681
917
|
return err(toFileError(error, resolved));
|
|
682
918
|
}
|
|
683
919
|
}
|
|
684
|
-
async remove(path, options) {
|
|
920
|
+
async remove(path, options, context) {
|
|
685
921
|
const resolved = resolvePath(this.cwd, path);
|
|
922
|
+
const aborted = abortResult(context.abortSignal, resolved);
|
|
923
|
+
if (aborted)
|
|
924
|
+
return aborted;
|
|
686
925
|
try {
|
|
687
926
|
await rm(resolved, { recursive: options?.recursive ?? false, force: options?.force ?? false });
|
|
688
927
|
return ok(undefined);
|
|
@@ -691,16 +930,20 @@ export class NodeExecutionEnv {
|
|
|
691
930
|
return err(toFileError(error, resolved));
|
|
692
931
|
}
|
|
693
932
|
}
|
|
694
|
-
async createTempDir(prefix
|
|
933
|
+
async createTempDir(prefix, context) {
|
|
934
|
+
const aborted = abortResult(context.abortSignal);
|
|
935
|
+
if (aborted)
|
|
936
|
+
return aborted;
|
|
695
937
|
try {
|
|
938
|
+
prefix ??= "tmp-";
|
|
696
939
|
return ok(await mkdtemp(join(tmpdir(), prefix)));
|
|
697
940
|
}
|
|
698
941
|
catch (error) {
|
|
699
942
|
return err(toFileError(error));
|
|
700
943
|
}
|
|
701
944
|
}
|
|
702
|
-
async createTempFile(options) {
|
|
703
|
-
const dir = await this.createTempDir("tmp-");
|
|
945
|
+
async createTempFile(options, context) {
|
|
946
|
+
const dir = await this.createTempDir("tmp-", context);
|
|
704
947
|
if (!dir.ok)
|
|
705
948
|
return dir;
|
|
706
949
|
const filePath = join(dir.value, `${options?.prefix ?? ""}${randomUUID()}${options?.suffix ?? ""}`);
|
|
@@ -712,7 +955,7 @@ export class NodeExecutionEnv {
|
|
|
712
955
|
return err(toFileError(error, filePath));
|
|
713
956
|
}
|
|
714
957
|
}
|
|
715
|
-
async cleanup() {
|
|
958
|
+
async cleanup(_context) {
|
|
716
959
|
for (const pid of this.activeChildPids)
|
|
717
960
|
killProcessTree(pid);
|
|
718
961
|
this.activeChildPids.clear();
|