@snappedly-tools/shipyard 0.5.0 → 0.7.0-staging.36081473933

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.
Files changed (89) hide show
  1. package/README.md +64 -117
  2. package/dist/MountConfig-BHnKnA4h.d.ts +145 -0
  3. package/dist/{chunk-WAXEMZUV.js → chunk-44I2BL6E.js} +225 -33
  4. package/dist/chunk-44I2BL6E.js.map +1 -0
  5. package/dist/{chunk-56TDSWFU.js → chunk-JI3HDDMS.js} +3 -3
  6. package/dist/{chunk-56TDSWFU.js.map → chunk-JI3HDDMS.js.map} +1 -1
  7. package/dist/{chunk-FDYOTN55.js → chunk-L6PX5QTU.js} +253 -878
  8. package/dist/chunk-L6PX5QTU.js.map +1 -0
  9. package/dist/index.d.ts +117 -97
  10. package/dist/index.js +349 -353
  11. package/dist/index.js.map +1 -1
  12. package/dist/main.js +285 -153
  13. package/dist/main.js.map +1 -1
  14. package/dist/sandboxes/docker.d.ts +1 -3
  15. package/dist/sandboxes/docker.js +2 -4
  16. package/dist/templates/parallel-planner/block-scope.sh +160 -0
  17. package/dist/templates/parallel-planner/conflict-prompt.md +22 -0
  18. package/dist/templates/parallel-planner/handoff.sh +175 -0
  19. package/dist/templates/parallel-planner/implement-prompt.md +10 -56
  20. package/dist/templates/parallel-planner/main.mts +493 -189
  21. package/dist/templates/parallel-planner/merge-prompt.md +9 -21
  22. package/dist/templates/parallel-planner/plan-prompt.md +5 -31
  23. package/dist/templates/parallel-planner/planner-branch.mts +151 -0
  24. package/dist/templates/parallel-planner/select-issues.mjs +788 -0
  25. package/dist/templates/parallel-planner/setup.sh +51 -0
  26. package/dist/templates/parallel-planner/spec-wave-prompt.md +16 -0
  27. package/dist/templates/parallel-planner/template.json +1 -1
  28. package/dist/templates/parallel-planner/triage-prompt.md +5 -0
  29. package/dist/templates/parallel-planner/verify-triage.sh +19 -0
  30. package/dist/templates/parallel-planner-with-review/block-scope.sh +160 -0
  31. package/dist/templates/parallel-planner-with-review/conflict-prompt.md +22 -0
  32. package/dist/templates/parallel-planner-with-review/handoff.sh +175 -0
  33. package/dist/templates/parallel-planner-with-review/implement-prompt.md +10 -56
  34. package/dist/templates/parallel-planner-with-review/main.mts +533 -207
  35. package/dist/templates/parallel-planner-with-review/merge-prompt.md +9 -21
  36. package/dist/templates/parallel-planner-with-review/plan-prompt.md +5 -31
  37. package/dist/templates/parallel-planner-with-review/planner-branch.mts +151 -0
  38. package/dist/templates/parallel-planner-with-review/review-prompt.md +11 -49
  39. package/dist/templates/parallel-planner-with-review/select-issues.mjs +788 -0
  40. package/dist/templates/parallel-planner-with-review/setup.sh +51 -0
  41. package/dist/templates/parallel-planner-with-review/spec-wave-prompt.md +16 -0
  42. package/dist/templates/parallel-planner-with-review/template.json +1 -1
  43. package/dist/templates/parallel-planner-with-review/triage-prompt.md +5 -0
  44. package/dist/templates/parallel-planner-with-review/verify-triage.sh +19 -0
  45. package/dist/templates/sequential-reviewer/block-scope.sh +160 -0
  46. package/dist/templates/sequential-reviewer/handoff.sh +175 -0
  47. package/dist/templates/sequential-reviewer/implement-prompt.md +12 -46
  48. package/dist/templates/sequential-reviewer/main.mts +248 -103
  49. package/dist/templates/sequential-reviewer/review-prompt.md +11 -49
  50. package/dist/templates/sequential-reviewer/select-issues.mjs +788 -0
  51. package/dist/templates/sequential-reviewer/setup.sh +51 -0
  52. package/dist/templates/sequential-reviewer/template.json +1 -1
  53. package/dist/templates/sequential-reviewer/triage-prompt.md +5 -0
  54. package/dist/templates/sequential-reviewer/verify-triage.sh +19 -0
  55. package/dist/templates/shared/block-scope.sh +160 -0
  56. package/dist/templates/shared/handoff.sh +175 -0
  57. package/dist/templates/shared/select-issues.mjs +788 -0
  58. package/dist/templates/shared/setup.sh +51 -0
  59. package/dist/templates/shared/triage-prompt.md +5 -0
  60. package/dist/templates/shared/verify-triage.sh +19 -0
  61. package/dist/templates/simple-loop/block-scope.sh +160 -0
  62. package/dist/templates/simple-loop/handoff.sh +175 -0
  63. package/dist/templates/simple-loop/main.mts +239 -45
  64. package/dist/templates/simple-loop/prompt.md +12 -46
  65. package/dist/templates/simple-loop/select-issues.mjs +788 -0
  66. package/dist/templates/simple-loop/setup.sh +51 -0
  67. package/dist/templates/simple-loop/template.json +1 -1
  68. package/dist/templates/simple-loop/triage-prompt.md +5 -0
  69. package/dist/templates/simple-loop/verify-triage.sh +19 -0
  70. package/package.json +1 -17
  71. package/dist/MountConfig-bZoCs4Dd.d.ts +0 -26
  72. package/dist/SandboxProvider-XJQqEdSf.d.ts +0 -261
  73. package/dist/chunk-ACD46ZM4.js +0 -136
  74. package/dist/chunk-ACD46ZM4.js.map +0 -1
  75. package/dist/chunk-FDYOTN55.js.map +0 -1
  76. package/dist/chunk-KMGNFXKN.js +0 -38
  77. package/dist/chunk-KMGNFXKN.js.map +0 -1
  78. package/dist/chunk-SOJTAJTF.js +0 -78
  79. package/dist/chunk-SOJTAJTF.js.map +0 -1
  80. package/dist/chunk-WAXEMZUV.js.map +0 -1
  81. package/dist/sandboxes/no-sandbox.d.ts +0 -37
  82. package/dist/sandboxes/no-sandbox.js +0 -4
  83. package/dist/sandboxes/no-sandbox.js.map +0 -1
  84. package/dist/sandboxes/vercel.d.ts +0 -104
  85. package/dist/sandboxes/vercel.js +0 -166
  86. package/dist/sandboxes/vercel.js.map +0 -1
  87. package/dist/templates/blank/main.mts +0 -13
  88. package/dist/templates/blank/prompt.md +0 -12
  89. package/dist/templates/blank/template.json +0 -4
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if [[ -n ${GH_REPO:-} ]]; then
5
+ [[ "$GH_REPO" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || { echo "Invalid GH_REPO" >&2; exit 1; }
6
+ git remote set-url origin "https://github.com/$GH_REPO.git"
7
+ fi
8
+
9
+ # Install for this sandbox's checkout. Host node_modules can contain binaries
10
+ # for a different OS and cannot cover dependencies added by the agent later.
11
+ if [[ -f package.json ]]; then
12
+ manager=$(node -e 'try { const p = require("./package.json").packageManager; process.stdout.write(p ? p.split("@")[0] : "") } catch {}')
13
+ if [[ -z "$manager" ]]; then
14
+ if [[ -f bun.lock || -f bun.lockb ]]; then manager=bun
15
+ elif [[ -f pnpm-lock.yaml ]]; then manager=pnpm
16
+ elif [[ -f yarn.lock ]]; then manager=yarn
17
+ else manager=npm; fi
18
+ fi
19
+ case "$manager" in
20
+ npm) npm install ;;
21
+ pnpm) corepack pnpm install ;;
22
+ yarn) corepack yarn install ;;
23
+ bun)
24
+ if ! command -v bun >/dev/null; then curl -fsSL https://bun.com/install | bash; export PATH="$HOME/.bun/bin:$PATH"; fi
25
+ bun install ;;
26
+ *) echo "Unsupported package manager: $manager" >&2; exit 1 ;;
27
+ esac
28
+ fi
29
+
30
+ source_dir=$(mktemp -d)
31
+ trap 'rm -rf "$source_dir"' EXIT
32
+ if ! git clone --depth 1 https://github.com/snappedly/skills.git "$source_dir/repo"; then
33
+ echo "Could not install Snappedly skills; agent work has not started." >&2
34
+ exit 1
35
+ fi
36
+
37
+ mkdir -p "$HOME/.agents/skills" "$HOME/.claude/skills"
38
+ while IFS= read -r -d '' skill_file; do
39
+ skill_dir=${skill_file%/SKILL.md}
40
+ skill_name=${skill_dir##*/}
41
+ rm -rf "$HOME/.agents/skills/$skill_name" "$HOME/.claude/skills/$skill_name"
42
+ cp -R "$skill_dir" "$HOME/.agents/skills/$skill_name"
43
+ ln -sfn "$HOME/.agents/skills/$skill_name" "$HOME/.claude/skills/$skill_name"
44
+ done < <(find "$source_dir/repo/skills" -name SKILL.md -print0)
45
+
46
+ for skill in triage implement implement-spec code-cleanup code-review tdd; do
47
+ if [[ ! -s "$HOME/.agents/skills/$skill/SKILL.md" || -z $(find "$source_dir/repo/skills" -path "*/$skill/SKILL.md" -print -quit) ]]; then
48
+ echo "Snappedly skill installation incomplete: $skill" >&2
49
+ exit 1
50
+ fi
51
+ done
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "simple-loop",
3
- "description": "Picks issues one by one and closes them"
3
+ "description": "Implements standalone issues or whole specs sequentially and opens review PRs"
4
4
  }
@@ -0,0 +1,5 @@
1
+ Triage GitHub issue #{{TASK_ID}} before Shipyard implementation.
2
+
3
+ Follow `/triage` and the repository's `docs/agents/triage.md` label mapping. Read the full issue and comments, verify its claim against the codebase, then apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
4
+
5
+ Report the applied state and evidence. Shipyard will independently re-read GitHub labels and implement only an open issue carrying both `shipyard` and the sole triage state `ready-for-agent`.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ issue=${1:?issue number required}
5
+ repo=${2:?GitHub repository required}
6
+ [[ "$issue" =~ ^[0-9]+$ ]] || { echo "Invalid issue number" >&2; exit 1; }
7
+ [[ "$repo" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || { echo "Invalid GitHub repository" >&2; exit 1; }
8
+
9
+ gh issue view "$issue" --repo "$repo" --json state,labels |
10
+ node -e '
11
+ const issue = JSON.parse(require("node:fs").readFileSync(0, "utf8"));
12
+ const labels = new Set(issue.labels.map((label) => label.name));
13
+ const states = ["needs-triage", "needs-info", "ready-for-agent", "ready-for-human", "wontfix"];
14
+ if (issue.state !== "OPEN" || !labels.has("shipyard") ||
15
+ states.filter((state) => labels.has(state)).join() !== "ready-for-agent") {
16
+ console.error("Issue cannot be implemented: requires an open issue with shipyard and only ready-for-agent as its triage state");
17
+ process.exit(1);
18
+ }
19
+ '
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snappedly-tools/shipyard",
3
- "version": "0.5.0",
3
+ "version": "0.7.0-staging.36081473933",
4
4
  "description": "Run AI coding agents in isolated sandboxes",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,14 +13,6 @@
13
13
  "./sandboxes/docker": {
14
14
  "import": "./dist/sandboxes/docker.js",
15
15
  "types": "./dist/sandboxes/docker.d.ts"
16
- },
17
- "./sandboxes/vercel": {
18
- "import": "./dist/sandboxes/vercel.js",
19
- "types": "./dist/sandboxes/vercel.d.ts"
20
- },
21
- "./sandboxes/no-sandbox": {
22
- "import": "./dist/sandboxes/no-sandbox.js",
23
- "types": "./dist/sandboxes/no-sandbox.d.ts"
24
16
  }
25
17
  },
26
18
  "bin": {
@@ -90,14 +82,6 @@
90
82
  "tsx": "^4.23.13",
91
83
  "zod": "^4.6.5"
92
84
  },
93
- "peerDependencies": {
94
- "@vercel/sandbox": ">=1.0.0"
95
- },
96
- "peerDependenciesMeta": {
97
- "@vercel/sandbox": {
98
- "optional": true
99
- }
100
- },
101
85
  "files": [
102
86
  "dist",
103
87
  "LICENSE",
@@ -1,26 +0,0 @@
1
- /**
2
- * User-facing mount configuration for bind-mount sandbox providers.
3
- *
4
- * Each entry describes a host directory to mount into the sandbox container.
5
- */
6
- /** A single bind-mount descriptor for the Docker provider. */
7
- interface MountConfig {
8
- /**
9
- * Path on the host. Supports:
10
- * - Absolute paths (`/data/cache`)
11
- * - Tilde-expanded paths (`~/data` → `<home>/data`)
12
- * - Relative paths (`data` or `./data`) — resolved from `process.cwd()`
13
- */
14
- readonly hostPath: string;
15
- /**
16
- * Path inside the sandbox container. Supports:
17
- * - Absolute paths (`/mnt/data`)
18
- * - Tilde-expanded paths (`~/.npm` → `/home/agent/.npm`) — expanded using the provider's sandbox home directory
19
- * - Relative paths (`data` or `./data`) — resolved from the worktree directory (`/home/agent/workspace`)
20
- */
21
- readonly sandboxPath: string;
22
- /** Mount as read-only. Defaults to `false`. */
23
- readonly readonly?: boolean;
24
- }
25
-
26
- export type { MountConfig as M };
@@ -1,261 +0,0 @@
1
- /**
2
- * Sandbox provider types — the pluggable interface for sandbox runtimes.
3
- *
4
- * Provider authors implement a small Promise-based interface. Shipyard
5
- * handles worktree creation, git mount resolution, and commit extraction.
6
- */
7
- /** Result of executing a command inside a sandbox. */
8
- interface ExecResult {
9
- readonly stdout: string;
10
- readonly stderr: string;
11
- readonly exitCode: number;
12
- }
13
- /** Options for interactiveExec — the streams the provider should wire to the spawned process. */
14
- interface InteractiveExecOptions {
15
- readonly stdin: NodeJS.ReadableStream;
16
- readonly stdout: NodeJS.WritableStream;
17
- readonly stderr: NodeJS.WritableStream;
18
- readonly cwd?: string;
19
- /** Terminate the interactive process when the caller cancels the session. */
20
- readonly signal?: AbortSignal;
21
- }
22
- /** Handle to a running bind-mount sandbox. */
23
- interface BindMountSandboxHandle {
24
- /** Absolute path to the worktree inside the sandbox. */
25
- readonly worktreePath: string;
26
- /**
27
- * Execute a command in the sandbox.
28
- *
29
- * Implementations MUST support line-by-line streaming via `onLine`. This is
30
- * how Shipyard delivers live feedback to the user and enforces idle timeouts —
31
- * without a streaming implementation, neither will work. A buffered/batch
32
- * implementation that only calls `onLine` after the process exits does NOT
33
- * satisfy this contract.
34
- *
35
- * When `stdin` is set, the implementation pipes the string to the child
36
- * process's stdin and closes it. This avoids the Linux 128 KB per-arg limit.
37
- */
38
- exec(command: string, options?: {
39
- onLine?: (line: string) => void;
40
- cwd?: string;
41
- sudo?: boolean;
42
- stdin?: string;
43
- /** Abort the command when the caller's operation is cancelled. */
44
- signal?: AbortSignal;
45
- /** Reject/terminate the command after this many combined output bytes. */
46
- maxOutputBytes?: number;
47
- }): Promise<ExecResult>;
48
- /**
49
- * Launch an interactive process inside the sandbox.
50
- * Optional — providers that support interactive sessions implement this.
51
- * The provider detects TTY mode from the streams (e.g. stdin.isTTY) and
52
- * allocates a pseudo-terminal accordingly.
53
- * Implementations MUST terminate the underlying process when `signal` aborts.
54
- */
55
- interactiveExec?(args: string[], options: InteractiveExecOptions): Promise<{
56
- exitCode: number;
57
- }>;
58
- /** Copy a single file from the host into the sandbox. */
59
- copyFileIn(hostPath: string, sandboxPath: string): Promise<void>;
60
- /** Copy a single file from the sandbox to the host. */
61
- copyFileOut(sandboxPath: string, hostPath: string): Promise<void>;
62
- /** Tear down the sandbox. */
63
- close(): Promise<void>;
64
- }
65
- /** Options passed to a bind-mount provider's `create` function. */
66
- interface BindMountCreateOptions {
67
- /** Host-side path to the worktree directory. */
68
- readonly worktreePath: string;
69
- /** Host-side path to the original repo root. */
70
- readonly hostRepoPath: string;
71
- /** Volume mounts to apply (host:sandbox pairs). */
72
- readonly mounts: Array<{
73
- hostPath: string;
74
- sandboxPath: string;
75
- readonly?: boolean;
76
- }>;
77
- /** Environment variables to inject into the sandbox. */
78
- readonly env: Record<string, string>;
79
- }
80
- /** Configuration for createBindMountSandboxProvider. */
81
- interface BindMountSandboxProviderConfig {
82
- /** Human-readable name for this provider (e.g. "docker"). */
83
- readonly name: string;
84
- /** Environment variables injected by this provider. Merged at launch time. */
85
- readonly env?: Record<string, string>;
86
- /**
87
- * Absolute path to the home directory inside the sandbox (e.g. `"/home/agent"`).
88
- * Used to expand `~` in user-provided `sandboxPath` mount configs.
89
- * Set to `undefined` for providers that do not have a fixed home directory.
90
- */
91
- readonly sandboxHomedir?: string;
92
- /** Create a sandbox handle from the given options. */
93
- readonly create: (options: BindMountCreateOptions) => Promise<BindMountSandboxHandle>;
94
- }
95
- /** Handle to a running isolated sandbox (extends bind-mount with file transfer). */
96
- interface IsolatedSandboxHandle {
97
- /** Absolute path to the worktree inside the sandbox. */
98
- readonly worktreePath: string;
99
- /**
100
- * Execute a command in the sandbox.
101
- *
102
- * Implementations MUST support line-by-line streaming via `onLine`. This is
103
- * how Shipyard delivers live feedback to the user and enforces idle timeouts —
104
- * without a streaming implementation, neither will work. A buffered/batch
105
- * implementation that only calls `onLine` after the process exits does NOT
106
- * satisfy this contract.
107
- *
108
- * When `stdin` is set, the implementation pipes the string to the child
109
- * process's stdin and closes it. This avoids the Linux 128 KB per-arg limit.
110
- */
111
- exec(command: string, options?: {
112
- onLine?: (line: string) => void;
113
- cwd?: string;
114
- sudo?: boolean;
115
- stdin?: string;
116
- /** Abort the command when the caller's operation is cancelled. */
117
- signal?: AbortSignal;
118
- /** Reject/terminate the command after this many combined output bytes. */
119
- maxOutputBytes?: number;
120
- }): Promise<ExecResult>;
121
- /**
122
- * Launch an interactive process inside the sandbox.
123
- * Optional — providers that support interactive sessions implement this.
124
- * The provider detects TTY mode from the streams (e.g. stdin.isTTY) and
125
- * allocates a pseudo-terminal accordingly.
126
- * Implementations MUST terminate the underlying process when `signal` aborts.
127
- */
128
- interactiveExec?(args: string[], options: InteractiveExecOptions): Promise<{
129
- exitCode: number;
130
- }>;
131
- /** Copy a file or directory from the host into the sandbox. */
132
- copyIn(hostPath: string, sandboxPath: string): Promise<void>;
133
- /** Copy a single file from the sandbox to the host. */
134
- copyFileOut(sandboxPath: string, hostPath: string): Promise<void>;
135
- /** Tear down the sandbox. */
136
- close(): Promise<void>;
137
- }
138
- /** Options passed to an isolated provider's `create` function. */
139
- interface IsolatedCreateOptions {
140
- /** Original host repository path, used only for image naming; never mounted. */
141
- readonly hostRepoPath?: string;
142
- /** Environment variables to inject into the sandbox. */
143
- readonly env: Record<string, string>;
144
- }
145
- /** Configuration for createIsolatedSandboxProvider. */
146
- interface IsolatedSandboxProviderConfig {
147
- /** Human-readable name for this provider (e.g. "vercel"). */
148
- readonly name: string;
149
- /** Environment variables injected by this provider. Merged at launch time. */
150
- readonly env?: Record<string, string>;
151
- /** Create an isolated sandbox handle from the given options. */
152
- readonly create: (options: IsolatedCreateOptions) => Promise<IsolatedSandboxHandle>;
153
- }
154
- /** A bind-mount sandbox provider. */
155
- interface BindMountSandboxProvider {
156
- /** Human-readable provider name. */
157
- readonly name: string;
158
- /** Environment variables injected by this provider. */
159
- readonly env: Record<string, string>;
160
- /**
161
- * Absolute path to the home directory inside the sandbox (e.g. `"/home/agent"`).
162
- * `undefined` when the provider does not declare a sandbox home directory.
163
- */
164
- readonly sandboxHomedir: string | undefined;
165
- }
166
- /** An isolated sandbox provider. */
167
- interface IsolatedSandboxProvider {
168
- /** Human-readable provider name. */
169
- readonly name: string;
170
- /** Environment variables injected by this provider. */
171
- readonly env: Record<string, string>;
172
- }
173
- /** Handle to a no-sandbox session — runs commands directly on the host. */
174
- interface NoSandboxHandle {
175
- /** Absolute path to the worktree on the host. */
176
- readonly worktreePath: string;
177
- /**
178
- * Execute a command on the host.
179
- *
180
- * Implementations MUST support line-by-line streaming via `onLine`. This is
181
- * how Shipyard delivers live feedback to the user and enforces idle timeouts —
182
- * without a streaming implementation, neither will work.
183
- *
184
- * When `stdin` is set, the implementation pipes the string to the child
185
- * process's stdin and closes it. This avoids the Linux 128 KB per-arg limit.
186
- */
187
- exec(command: string, options?: {
188
- onLine?: (line: string) => void;
189
- cwd?: string;
190
- sudo?: boolean;
191
- stdin?: string;
192
- /** Abort the command when the caller's operation is cancelled. */
193
- signal?: AbortSignal;
194
- /** Reject/terminate the command after this many combined output bytes. */
195
- maxOutputBytes?: number;
196
- }): Promise<ExecResult>;
197
- /**
198
- * Launch an interactive process on the host with inherited stdio.
199
- */
200
- interactiveExec(args: string[], options: InteractiveExecOptions): Promise<{
201
- exitCode: number;
202
- }>;
203
- /** No-op — no container to tear down. */
204
- close(): Promise<void>;
205
- }
206
- /** A no-sandbox provider — runs the agent directly on the host with no container isolation. */
207
- interface NoSandboxProvider {
208
- /** Human-readable provider name. */
209
- readonly name: string;
210
- /** Environment variables injected by this provider. */
211
- readonly env: Record<string, string>;
212
- }
213
- /** Head strategy: agent writes directly to host working directory. Bind-mount only. */
214
- interface HeadBranchStrategy {
215
- readonly type: "head";
216
- }
217
- /** Merge-to-head strategy: temp branch, merge back to HEAD, delete temp branch. */
218
- interface MergeToHeadBranchStrategy {
219
- readonly type: "merge-to-head";
220
- }
221
- /** Branch strategy: commits land on an explicit named branch. */
222
- interface NamedBranchStrategy {
223
- readonly type: "branch";
224
- readonly branch: string;
225
- /**
226
- * Git ref to use as the starting point when creating a new branch.
227
- * Only used when the branch doesn't already exist — ignored otherwise.
228
- * Callers are responsible for ensuring the ref is current (e.g. `git fetch`).
229
- * Defaults to `HEAD` when omitted.
230
- */
231
- readonly baseBranch?: string;
232
- }
233
- /** Branch strategy for bind-mount providers (all three variants). */
234
- type BindMountBranchStrategy = HeadBranchStrategy | MergeToHeadBranchStrategy | NamedBranchStrategy;
235
- /** Branch strategy for isolated providers (no head — can't write to host). */
236
- type IsolatedBranchStrategy = MergeToHeadBranchStrategy | NamedBranchStrategy;
237
- /** Branch strategy for no-sandbox providers (all three — same as bind-mount). */
238
- type NoSandboxBranchStrategy = HeadBranchStrategy | MergeToHeadBranchStrategy | NamedBranchStrategy;
239
- /** Union of all branch strategy variants. */
240
- type BranchStrategy = BindMountBranchStrategy | IsolatedBranchStrategy | NoSandboxBranchStrategy;
241
- /**
242
- * A sandbox provider — the pluggable unit that `run()`, `interactive()`, and
243
- * `createSandbox()` accept. Tagged for internal dispatch: "bind-mount",
244
- * "isolated", or "none". When `NoSandboxProvider` is used, the agent runs
245
- * directly on the host with no container isolation — opt in at your own risk.
246
- */
247
- type SandboxProvider = BindMountSandboxProvider | IsolatedSandboxProvider | NoSandboxProvider;
248
- /** @deprecated Use `SandboxProvider` — it now includes `NoSandboxProvider`. */
249
- type AnySandboxProvider = SandboxProvider;
250
- /**
251
- * Create a bind-mount sandbox provider from a config object.
252
- * The returned provider can be passed to `run()` or `createSandbox()`.
253
- */
254
- declare const createBindMountSandboxProvider: (config: BindMountSandboxProviderConfig) => BindMountSandboxProvider;
255
- /**
256
- * Create an isolated sandbox provider from a config object.
257
- * The returned provider can be passed to `run()` or `createSandbox()`.
258
- */
259
- declare const createIsolatedSandboxProvider: (config: IsolatedSandboxProviderConfig) => IsolatedSandboxProvider;
260
-
261
- export { type AnySandboxProvider as A, type BindMountSandboxHandle as B, type ExecResult as E, type HeadBranchStrategy as H, type IsolatedSandboxProvider as I, type MergeToHeadBranchStrategy as M, type NoSandboxProvider as N, type SandboxProvider as S, type BranchStrategy as a, type NamedBranchStrategy as b, type BindMountBranchStrategy as c, type BindMountCreateOptions as d, type BindMountSandboxProvider as e, type BindMountSandboxProviderConfig as f, type InteractiveExecOptions as g, type IsolatedBranchStrategy as h, type IsolatedCreateOptions as i, type IsolatedSandboxHandle as j, type IsolatedSandboxProviderConfig as k, type NoSandboxBranchStrategy as l, type NoSandboxHandle as m, createBindMountSandboxProvider as n, createIsolatedSandboxProvider as o };
@@ -1,136 +0,0 @@
1
- import { createInterface } from 'readline';
2
-
3
- // src/boundedTail.ts
4
- var MAX_TAIL_CHARS = 64 * 1024;
5
- var OutputByteCounter = class {
6
- constructor(maxBytes) {
7
- this.maxBytes = maxBytes;
8
- }
9
- totalBytes = 0;
10
- limitExceeded = false;
11
- add(chunk) {
12
- this.totalBytes += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.byteLength;
13
- if (this.totalBytes > this.maxBytes) this.limitExceeded = true;
14
- }
15
- get exceeded() {
16
- return this.limitExceeded;
17
- }
18
- };
19
- var BoundedTail = class {
20
- items = [];
21
- totalChars = 0;
22
- maxChars;
23
- separator;
24
- /**
25
- * @param maxChars Maximum length of the joined tail. Defaults to {@link MAX_TAIL_CHARS}.
26
- * @param separator String placed between items by {@link toString}. Must match
27
- * how the caller would otherwise have joined the accumulated chunks (e.g.
28
- * `"\n"` for line streams, `""` for raw chunk streams).
29
- */
30
- constructor(maxChars = MAX_TAIL_CHARS, separator = "") {
31
- this.maxChars = maxChars;
32
- this.separator = separator;
33
- }
34
- /** Append one item to the tail, evicting oldest items to stay within budget. */
35
- push(item) {
36
- const bounded = item.length > this.maxChars ? item.slice(item.length - this.maxChars) : item;
37
- this.totalChars += bounded.length + (this.items.length > 0 ? this.separator.length : 0);
38
- this.items.push(bounded);
39
- while (this.totalChars > this.maxChars && this.items.length > 1) {
40
- const dropped = this.items.shift();
41
- this.totalChars -= dropped.length + this.separator.length;
42
- }
43
- }
44
- /** Join the retained tail into a single string (length ≤ `maxChars`). */
45
- toString() {
46
- return this.items.join(this.separator);
47
- }
48
- };
49
- var StreamedProcessOutput = class {
50
- maxOutputBytes;
51
- byteCounter;
52
- stdoutTail;
53
- stderrTail;
54
- onLine;
55
- outputLimitError;
56
- constructor(options) {
57
- this.maxOutputBytes = options.maxOutputBytes;
58
- this.byteCounter = options.maxOutputBytes === void 0 ? void 0 : new OutputByteCounter(options.maxOutputBytes);
59
- const tailChars = options.maxOutputBytes ?? options.maxOutputTailChars ?? MAX_TAIL_CHARS;
60
- this.stdoutTail = new BoundedTail(tailChars, "\n");
61
- this.stderrTail = new BoundedTail(tailChars, "");
62
- this.onLine = options.onLine ?? (() => {
63
- });
64
- }
65
- checkLimit(chunk) {
66
- if (this.byteCounter === void 0 || this.outputLimitError !== void 0) {
67
- return this.outputLimitError;
68
- }
69
- this.byteCounter.add(chunk);
70
- if (this.byteCounter.exceeded) {
71
- this.outputLimitError = new Error(
72
- `Sandbox command output exceeded ${this.maxOutputBytes} bytes`
73
- );
74
- }
75
- return this.outputLimitError;
76
- }
77
- addStdoutLine(line) {
78
- this.stdoutTail.push(line);
79
- this.onLine(line);
80
- }
81
- addStderr(chunk) {
82
- this.stderrTail.push(chunk);
83
- }
84
- get error() {
85
- return this.outputLimitError;
86
- }
87
- result(exitCode) {
88
- return {
89
- stdout: this.stdoutTail.toString(),
90
- stderr: this.stderrTail.toString(),
91
- exitCode
92
- };
93
- }
94
- };
95
- var collectProcessOutput = (process, options, resolve, reject) => {
96
- if (options.onLine || options.maxOutputBytes !== void 0) {
97
- const output = new StreamedProcessOutput(options);
98
- const checkLimit = (chunk) => {
99
- if (output.checkLimit(chunk) !== void 0) process.kill();
100
- };
101
- process.stdout.on("data", checkLimit);
102
- process.stderr.on("data", checkLimit);
103
- const lines = createInterface({ input: process.stdout });
104
- lines.on("line", (line) => output.addStdoutLine(line));
105
- process.stderr.on("data", (chunk) => {
106
- output.addStderr(chunk.toString());
107
- });
108
- process.onClose((code) => {
109
- if (output.error !== void 0) {
110
- reject(output.error);
111
- return;
112
- }
113
- resolve(output.result(code ?? 0));
114
- });
115
- return;
116
- }
117
- const stdoutChunks = [];
118
- const stderrChunks = [];
119
- process.stdout.on("data", (chunk) => {
120
- stdoutChunks.push(chunk.toString());
121
- });
122
- process.stderr.on("data", (chunk) => {
123
- stderrChunks.push(chunk.toString());
124
- });
125
- process.onClose((code) => {
126
- resolve({
127
- stdout: stdoutChunks.join(""),
128
- stderr: stderrChunks.join(""),
129
- exitCode: code ?? 0
130
- });
131
- });
132
- };
133
-
134
- export { MAX_TAIL_CHARS, StreamedProcessOutput, collectProcessOutput };
135
- //# sourceMappingURL=chunk-ACD46ZM4.js.map
136
- //# sourceMappingURL=chunk-ACD46ZM4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/boundedTail.ts","../src/processOutput.ts"],"names":[],"mappings":";;;AAmBO,IAAM,iBAAiB,EAAA,GAAK;AAQ5B,IAAM,oBAAN,MAAwB;AAAA,EAI7B,YAA6B,QAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EAAmB;AAAA,EAHxC,UAAA,GAAa,CAAA;AAAA,EACb,aAAA,GAAgB,KAAA;AAAA,EAIxB,IAAI,KAAA,EAAkC;AACpC,IAAA,IAAA,CAAK,UAAA,IACH,OAAO,KAAA,KAAU,QAAA,GAAW,OAAO,UAAA,CAAW,KAAK,IAAI,KAAA,CAAM,UAAA;AAC/D,IAAA,IAAI,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,QAAA,OAAe,aAAA,GAAgB,IAAA;AAAA,EAC5D;AAAA,EAEA,IAAI,QAAA,GAAoB;AACtB,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd;AACF,CAAA;AAaO,IAAM,cAAN,MAAkB;AAAA,EACN,QAAkB,EAAC;AAAA,EAC5B,UAAA,GAAa,CAAA;AAAA,EACJ,QAAA;AAAA,EACA,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQjB,WAAA,CAAY,QAAA,GAAmB,cAAA,EAAgB,SAAA,GAAY,EAAA,EAAI;AAC7D,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAChB,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AAAA;AAAA,EAGA,KAAK,IAAA,EAAoB;AACvB,IAAA,MAAM,OAAA,GACJ,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,QAAA,GACf,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,QAAQ,CAAA,GACtC,IAAA;AACN,IAAA,IAAA,CAAK,UAAA,IACH,QAAQ,MAAA,IAAU,IAAA,CAAK,MAAM,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,MAAA,GAAS,CAAA,CAAA;AACpE,IAAA,IAAA,CAAK,KAAA,CAAM,KAAK,OAAO,CAAA;AACvB,IAAA,OAAO,KAAK,UAAA,GAAa,IAAA,CAAK,YAAY,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,EAAG;AAC/D,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,KAAA,EAAM;AACjC,MAAA,IAAA,CAAK,UAAA,IAAc,OAAA,CAAQ,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,MAAA;AAAA,IACrD;AAAA,EACF;AAAA;AAAA,EAGA,QAAA,GAAmB;AACjB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,SAAS,CAAA;AAAA,EACvC;AACF,CAAA;ACvEO,IAAM,wBAAN,MAA4B;AAAA,EAChB,cAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACT,gBAAA;AAAA,EAER,YAAY,OAAA,EAA+B;AACzC,IAAA,IAAA,CAAK,iBAAiB,OAAA,CAAQ,cAAA;AAC9B,IAAA,IAAA,CAAK,WAAA,GACH,QAAQ,cAAA,KAAmB,MAAA,GACvB,SACA,IAAI,iBAAA,CAAkB,QAAQ,cAAc,CAAA;AAClD,IAAA,MAAM,SAAA,GACJ,OAAA,CAAQ,cAAA,IAAkB,OAAA,CAAQ,kBAAA,IAAsB,cAAA;AAC1D,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,WAAA,CAAY,SAAA,EAAW,IAAI,CAAA;AACjD,IAAA,IAAA,CAAK,UAAA,GAAa,IAAI,WAAA,CAAY,SAAA,EAAW,EAAE,CAAA;AAC/C,IAAA,IAAA,CAAK,MAAA,GAAS,OAAA,CAAQ,MAAA,KAAW,MAAM;AAAA,IAAC,CAAA,CAAA;AAAA,EAC1C;AAAA,EAEA,WAAW,KAAA,EAA+C;AACxD,IAAA,IAAI,IAAA,CAAK,WAAA,KAAgB,MAAA,IAAa,IAAA,CAAK,qBAAqB,MAAA,EAAW;AACzE,MAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,IACd;AACA,IAAA,IAAA,CAAK,WAAA,CAAY,IAAI,KAAK,CAAA;AAC1B,IAAA,IAAI,IAAA,CAAK,YAAY,QAAA,EAAU;AAC7B,MAAA,IAAA,CAAK,mBAAmB,IAAI,KAAA;AAAA,QAC1B,CAAA,gCAAA,EAAmC,KAAK,cAAc,CAAA,MAAA;AAAA,OACxD;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,EACd;AAAA,EAEA,cAAc,IAAA,EAAoB;AAChC,IAAA,IAAA,CAAK,UAAA,CAAW,KAAK,IAAI,CAAA;AACzB,IAAA,IAAA,CAAK,OAAO,IAAI,CAAA;AAAA,EAClB;AAAA,EAEA,UAAU,KAAA,EAAqB;AAC7B,IAAA,IAAA,CAAK,UAAA,CAAW,KAAK,KAAK,CAAA;AAAA,EAC5B;AAAA,EAEA,IAAI,KAAA,GAA2B;AAC7B,IAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,EACd;AAAA,EAEA,OAAO,QAAA,EAAuC;AAC5C,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,IAAA,CAAK,UAAA,CAAW,QAAA,EAAS;AAAA,MACjC,MAAA,EAAQ,IAAA,CAAK,UAAA,CAAW,QAAA,EAAS;AAAA,MACjC;AAAA,KACF;AAAA,EACF;AACF;AAEO,IAAM,oBAAA,GAAuB,CAClC,OAAA,EAMA,OAAA,EACA,SACA,MAAA,KACS;AACT,EAAA,IAAI,OAAA,CAAQ,MAAA,IAAU,OAAA,CAAQ,cAAA,KAAmB,MAAA,EAAW;AAC1D,IAAA,MAAM,MAAA,GAAS,IAAI,qBAAA,CAAsB,OAAO,CAAA;AAChD,IAAA,MAAM,UAAA,GAAa,CAAC,KAAA,KAAwB;AAC1C,MAAA,IAAI,OAAO,UAAA,CAAW,KAAK,CAAA,KAAM,MAAA,UAAmB,IAAA,EAAK;AAAA,IAC3D,CAAA;AACA,IAAA,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,UAAU,CAAA;AACpC,IAAA,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,UAAU,CAAA;AACpC,IAAA,MAAM,QAAQ,eAAA,CAAgB,EAAE,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AACvD,IAAA,KAAA,CAAM,GAAG,MAAA,EAAQ,CAAC,SAAS,MAAA,CAAO,aAAA,CAAc,IAAI,CAAC,CAAA;AACrD,IAAA,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB;AAC3C,MAAA,MAAA,CAAO,SAAA,CAAU,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA,IACnC,CAAC,CAAA;AACD,IAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,IAAA,KAAS;AACxB,MAAA,IAAI,MAAA,CAAO,UAAU,MAAA,EAAW;AAC9B,QAAA,MAAA,CAAO,OAAO,KAAK,CAAA;AACnB,QAAA;AAAA,MACF;AACA,MAAA,OAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,IAAA,IAAQ,CAAC,CAAC,CAAA;AAAA,IAClC,CAAC,CAAA;AACD,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,eAAyB,EAAC;AAChC,EAAA,MAAM,eAAyB,EAAC;AAChC,EAAA,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB;AAC3C,IAAA,YAAA,CAAa,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA,EACpC,CAAC,CAAA;AACD,EAAA,OAAA,CAAQ,MAAA,CAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB;AAC3C,IAAA,YAAA,CAAa,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,CAAA;AAAA,EACpC,CAAC,CAAA;AACD,EAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,IAAA,KAAS;AACxB,IAAA,OAAA,CAAQ;AAAA,MACN,MAAA,EAAQ,YAAA,CAAa,IAAA,CAAK,EAAE,CAAA;AAAA,MAC5B,MAAA,EAAQ,YAAA,CAAa,IAAA,CAAK,EAAE,CAAA;AAAA,MAC5B,UAAU,IAAA,IAAQ;AAAA,KACnB,CAAA;AAAA,EACH,CAAC,CAAA;AACH","file":"chunk-ACD46ZM4.js","sourcesContent":["/**\n * A bounded, rolling tail of streamed output — a pure, provider-agnostic\n * utility shared by every sandbox provider that streams `exec` output.\n *\n * When a provider streams output line-by-line, it accumulates the stream only\n * to build the returned `ExecResult.stdout`/`stderr`. Consumers read just the\n * tail of that value (e.g. the last lines of an error, or a fallback for the\n * agent's final result), so retaining the whole stream is unnecessary — and,\n * once the accumulated string passes V8's ~512MB max string length, fatal: a\n * naive `chunks.join()` throws `RangeError: Invalid string length`, which on a\n * long agent run crashes the whole orchestration.\n */\n\n/**\n * Default maximum number of characters retained in a bounded output tail.\n *\n * 64KiB sits comfortably above any agent completion signal or structured-output\n * payload while staying far below V8's max string length.\n */\nexport const MAX_TAIL_CHARS = 64 * 1024;\n\n/**\n * Counts streamed output without retaining it. Providers use this when a\n * caller needs a hard byte ceiling for a one-shot command (for example prompt\n * expansion), so an untrusted command cannot exhaust memory before its output\n * is inspected.\n */\nexport class OutputByteCounter {\n private totalBytes = 0;\n private limitExceeded = false;\n\n constructor(private readonly maxBytes: number) {}\n\n add(chunk: string | Uint8Array): void {\n this.totalBytes +=\n typeof chunk === \"string\" ? Buffer.byteLength(chunk) : chunk.byteLength;\n if (this.totalBytes > this.maxBytes) this.limitExceeded = true;\n }\n\n get exceeded(): boolean {\n return this.limitExceeded;\n }\n}\n\n/**\n * A fixed-size rolling tail of strings, bounded by total character length.\n *\n * `push` appends to the tail; once the joined length would exceed `maxChars`,\n * the oldest items are dropped from the front. A single item longer than\n * `maxChars` is truncated to its own tail, so a newline-free blob can't\n * overflow on one push. `toString` joins the retained items, and its length is\n * always at most `maxChars`.\n *\n * The running length counter is encapsulated so callers can't desync it.\n */\nexport class BoundedTail {\n private readonly items: string[] = [];\n private totalChars = 0;\n private readonly maxChars: number;\n private readonly separator: string;\n\n /**\n * @param maxChars Maximum length of the joined tail. Defaults to {@link MAX_TAIL_CHARS}.\n * @param separator String placed between items by {@link toString}. Must match\n * how the caller would otherwise have joined the accumulated chunks (e.g.\n * `\"\\n\"` for line streams, `\"\"` for raw chunk streams).\n */\n constructor(maxChars: number = MAX_TAIL_CHARS, separator = \"\") {\n this.maxChars = maxChars;\n this.separator = separator;\n }\n\n /** Append one item to the tail, evicting oldest items to stay within budget. */\n push(item: string): void {\n const bounded =\n item.length > this.maxChars\n ? item.slice(item.length - this.maxChars)\n : item;\n this.totalChars +=\n bounded.length + (this.items.length > 0 ? this.separator.length : 0);\n this.items.push(bounded);\n while (this.totalChars > this.maxChars && this.items.length > 1) {\n const dropped = this.items.shift()!;\n this.totalChars -= dropped.length + this.separator.length;\n }\n }\n\n /** Join the retained tail into a single string (length ≤ `maxChars`). */\n toString(): string {\n return this.items.join(this.separator);\n }\n}\n","import type { Readable } from \"node:stream\";\nimport { createInterface } from \"node:readline\";\nimport {\n BoundedTail,\n MAX_TAIL_CHARS,\n OutputByteCounter,\n} from \"./boundedTail.js\";\n\nexport interface ProcessOutputResult {\n readonly stdout: string;\n readonly stderr: string;\n readonly exitCode: number;\n}\n\nexport interface ProcessOutputOptions {\n readonly onLine?: (line: string) => void;\n readonly maxOutputBytes?: number;\n readonly maxOutputTailChars?: number;\n}\n\nexport class StreamedProcessOutput {\n private readonly maxOutputBytes: number | undefined;\n private readonly byteCounter: OutputByteCounter | undefined;\n private readonly stdoutTail: BoundedTail;\n private readonly stderrTail: BoundedTail;\n private readonly onLine: (line: string) => void;\n private outputLimitError: Error | undefined;\n\n constructor(options: ProcessOutputOptions) {\n this.maxOutputBytes = options.maxOutputBytes;\n this.byteCounter =\n options.maxOutputBytes === undefined\n ? undefined\n : new OutputByteCounter(options.maxOutputBytes);\n const tailChars =\n options.maxOutputBytes ?? options.maxOutputTailChars ?? MAX_TAIL_CHARS;\n this.stdoutTail = new BoundedTail(tailChars, \"\\n\");\n this.stderrTail = new BoundedTail(tailChars, \"\");\n this.onLine = options.onLine ?? (() => {});\n }\n\n checkLimit(chunk: string | Uint8Array): Error | undefined {\n if (this.byteCounter === undefined || this.outputLimitError !== undefined) {\n return this.outputLimitError;\n }\n this.byteCounter.add(chunk);\n if (this.byteCounter.exceeded) {\n this.outputLimitError = new Error(\n `Sandbox command output exceeded ${this.maxOutputBytes} bytes`,\n );\n }\n return this.outputLimitError;\n }\n\n addStdoutLine(line: string): void {\n this.stdoutTail.push(line);\n this.onLine(line);\n }\n\n addStderr(chunk: string): void {\n this.stderrTail.push(chunk);\n }\n\n get error(): Error | undefined {\n return this.outputLimitError;\n }\n\n result(exitCode: number): ProcessOutputResult {\n return {\n stdout: this.stdoutTail.toString(),\n stderr: this.stderrTail.toString(),\n exitCode,\n };\n }\n}\n\nexport const collectProcessOutput = (\n process: {\n readonly stdout: Readable;\n readonly stderr: Readable;\n readonly kill: () => void;\n readonly onClose: (listener: (code: number | null) => void) => void;\n },\n options: ProcessOutputOptions,\n resolve: (result: ProcessOutputResult) => void,\n reject: (error: Error) => void,\n): void => {\n if (options.onLine || options.maxOutputBytes !== undefined) {\n const output = new StreamedProcessOutput(options);\n const checkLimit = (chunk: Buffer): void => {\n if (output.checkLimit(chunk) !== undefined) process.kill();\n };\n process.stdout.on(\"data\", checkLimit);\n process.stderr.on(\"data\", checkLimit);\n const lines = createInterface({ input: process.stdout });\n lines.on(\"line\", (line) => output.addStdoutLine(line));\n process.stderr.on(\"data\", (chunk: Buffer) => {\n output.addStderr(chunk.toString());\n });\n process.onClose((code) => {\n if (output.error !== undefined) {\n reject(output.error);\n return;\n }\n resolve(output.result(code ?? 0));\n });\n return;\n }\n\n const stdoutChunks: string[] = [];\n const stderrChunks: string[] = [];\n process.stdout.on(\"data\", (chunk: Buffer) => {\n stdoutChunks.push(chunk.toString());\n });\n process.stderr.on(\"data\", (chunk: Buffer) => {\n stderrChunks.push(chunk.toString());\n });\n process.onClose((code) => {\n resolve({\n stdout: stdoutChunks.join(\"\"),\n stderr: stderrChunks.join(\"\"),\n exitCode: code ?? 0,\n });\n });\n};\n"]}