stitchkit 0.103.11 → 0.103.12
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/CHANGELOG.md +19 -0
- package/dist/agent-runtime/coding-shell-process.d.ts.map +1 -1
- package/dist/agent-runtime/sandbox-process-owner.d.ts +1 -1
- package/dist/agent-runtime/sandbox-process-owner.d.ts.map +1 -1
- package/dist/durability/json.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/coding-tools.js +2 -2
- package/dist/entrypoints/agent-runtime/harness.js +2 -2
- package/dist/entrypoints/agent-runtime/sandbox.js +20 -16
- package/dist/entrypoints/agent-runtime.js +2 -2
- package/dist/entrypoints/application/diagnostic-journal.js +1 -1
- package/dist/entrypoints/files.js +1 -1
- package/dist/entrypoints/primitives.js +4 -3
- package/dist/entrypoints/process.js +1 -1
- package/dist/entrypoints/tools.js +1 -1
- package/dist/{index-ary5s7k0.js → index-69nvze1q.js} +1 -2
- package/dist/{index-g4jxe7ve.js → index-806d5phm.js} +3 -2
- package/dist/{index-32jkwkdm.js → index-f330qqyg.js} +253 -165
- package/dist/index-fm1c1a08.js +138 -0
- package/dist/{index-ttf0y7b8.js → index-xvdd2rbb.js} +37 -18
- package/dist/{index-9jb61j5a.js → index-zp4jpkp0.js} +1 -1
- package/dist/internal/exclusive-lock.d.ts +1 -1
- package/dist/internal/exclusive-lock.d.ts.map +1 -1
- package/dist/internal/json-data.d.ts +8 -2
- package/dist/internal/json-data.d.ts.map +1 -1
- package/dist/internal/json-string-size.d.ts +3 -0
- package/dist/internal/json-string-size.d.ts.map +1 -0
- package/dist/primitives/canonical-json.d.ts.map +1 -1
- package/dist/process/cleanup.d.ts +12 -0
- package/dist/process/cleanup.d.ts.map +1 -0
- package/dist/process/command-lifetime.d.ts +19 -0
- package/dist/process/command-lifetime.d.ts.map +1 -0
- package/dist/process/command-owner.d.ts +0 -3
- package/dist/process/command-owner.d.ts.map +1 -1
- package/dist/process/group.d.ts.map +1 -1
- package/dist/process/launch.d.ts +15 -6
- package/dist/process/launch.d.ts.map +1 -1
- package/dist/process/owned-child.d.ts +10 -0
- package/dist/process/owned-child.d.ts.map +1 -0
- package/dist/process/start-failure.d.ts +2 -5
- package/dist/process/start-failure.d.ts.map +1 -1
- package/dist/process/transport.d.ts +30 -0
- package/dist/process/transport.d.ts.map +1 -0
- package/llms/agent-runtime-sandbox.txt +9 -1
- package/llms/process.txt +29 -4
- package/llms-full.txt +38 -5
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/dist/index-c1jmtxa0.js +0 -74
|
@@ -116,22 +116,22 @@ async function signalUntilSettled(pid, signal, deadline) {
|
|
|
116
116
|
signalGroup(pid, signal);
|
|
117
117
|
return;
|
|
118
118
|
} catch (error) {
|
|
119
|
-
if (!deniedOnDarwin(error) ||
|
|
119
|
+
if (!deniedOnDarwin(error) || performance.now() >= deadline)
|
|
120
120
|
throw error;
|
|
121
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(10, deadline -
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(10, deadline - performance.now())));
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
async function stopCommandGroup(pid, graceMs, cleanupTimeoutMs = 2000, force = false) {
|
|
126
|
-
const deadline =
|
|
126
|
+
const deadline = performance.now() + graceMs + cleanupTimeoutMs;
|
|
127
127
|
if (force) {
|
|
128
128
|
await signalUntilSettled(pid, "SIGKILL", deadline);
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
await signalUntilSettled(pid, "SIGTERM", deadline);
|
|
132
|
-
const until =
|
|
133
|
-
while (
|
|
134
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(10, until -
|
|
132
|
+
const until = performance.now() + graceMs;
|
|
133
|
+
while (performance.now() < until && exists(pid))
|
|
134
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(10, until - performance.now())));
|
|
135
135
|
await signalUntilSettled(pid, "SIGKILL", deadline);
|
|
136
136
|
}
|
|
137
137
|
function commandCleanupError(cause) {
|
|
@@ -144,9 +144,188 @@ function waitForCommandClose(closed, timeoutMs) {
|
|
|
144
144
|
return raceAbort(closed, deadline.signal).finally(() => deadline.cancel());
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
// src/process/cleanup.ts
|
|
148
|
+
async function cleanupNativeCommand(input) {
|
|
149
|
+
const cleanup = await Promise.allSettled([
|
|
150
|
+
input.settle(input.event ?? { kind: "error", cause: input.error }),
|
|
151
|
+
Promise.resolve().then(() => input.transport?.destroy()),
|
|
152
|
+
Promise.resolve().then(input.signal).then(async () => {
|
|
153
|
+
if (!input.transport)
|
|
154
|
+
return;
|
|
155
|
+
try {
|
|
156
|
+
await waitForCommandClose(input.transport.released, input.timeoutMs);
|
|
157
|
+
} catch (cause) {
|
|
158
|
+
throw new Error(`Command handles remained open: ${[...input.transport.pendingHandles].join(", ")}`, { cause });
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
]);
|
|
162
|
+
const failures = cleanup.flatMap((item) => item.status === "rejected" && item.reason !== input.error ? [item.reason] : []);
|
|
163
|
+
if (failures.length)
|
|
164
|
+
throw commandCleanupError(new AggregateError([input.error, ...failures], "Command failed and cleanup failed"));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/process/command-lifetime.ts
|
|
168
|
+
function createCommandLifetime(input) {
|
|
169
|
+
const acceptSettlement = (error) => {
|
|
170
|
+
if (error instanceof NativeCommandError2 && error.code === "COMMAND_CLEANUP")
|
|
171
|
+
throw error;
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
result: input.result,
|
|
175
|
+
async terminate() {
|
|
176
|
+
let refused = false;
|
|
177
|
+
let refusal;
|
|
178
|
+
if (!input.finished()) {
|
|
179
|
+
try {
|
|
180
|
+
await input.signal();
|
|
181
|
+
} catch (error) {
|
|
182
|
+
refused = true;
|
|
183
|
+
refusal = error;
|
|
184
|
+
input.controller.abort(error);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
await waitForCommandClose(input.result, input.cleanupTimeoutMs);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (!input.finished())
|
|
190
|
+
input.controller.abort(error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
await input.result.then(() => {
|
|
194
|
+
return;
|
|
195
|
+
}, acceptSettlement);
|
|
196
|
+
if (refused)
|
|
197
|
+
throw commandCleanupError(refusal);
|
|
198
|
+
},
|
|
199
|
+
async stop() {
|
|
200
|
+
if (!input.finished())
|
|
201
|
+
input.controller.abort(new DOMException("Command stopped", "AbortError"));
|
|
202
|
+
await input.result.then(() => {
|
|
203
|
+
return;
|
|
204
|
+
}, acceptSettlement);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
147
209
|
// src/process/launch.ts
|
|
210
|
+
import { Readable as Readable2 } from "node:stream";
|
|
211
|
+
|
|
212
|
+
// src/process/owned-child.ts
|
|
148
213
|
import { spawn } from "node:child_process";
|
|
214
|
+
var groups = new WeakSet;
|
|
215
|
+
function spawnOwnedCommand(input) {
|
|
216
|
+
const child = spawn(input.executable, [...input.args], {
|
|
217
|
+
cwd: input.cwd,
|
|
218
|
+
env: input.env,
|
|
219
|
+
detached: input.group,
|
|
220
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
221
|
+
});
|
|
222
|
+
if (input.group)
|
|
223
|
+
groups.add(child);
|
|
224
|
+
return child;
|
|
225
|
+
}
|
|
226
|
+
function ownsCommandGroup(child) {
|
|
227
|
+
return groups.has(child);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/process/transport.ts
|
|
149
231
|
import { Readable, Writable } from "node:stream";
|
|
232
|
+
function createCommandTransport(child) {
|
|
233
|
+
const leader = Promise.withResolvers();
|
|
234
|
+
const closed = Promise.withResolvers();
|
|
235
|
+
const released = Promise.withResolvers();
|
|
236
|
+
const pendingHandles = new Set(["leader", "launcher", "stdout", "stderr"]);
|
|
237
|
+
const destructors = new Map;
|
|
238
|
+
let stdout;
|
|
239
|
+
let stderr;
|
|
240
|
+
let stdin;
|
|
241
|
+
let isClosed = false;
|
|
242
|
+
const release = (name) => {
|
|
243
|
+
pendingHandles.delete(name);
|
|
244
|
+
if (pendingHandles.size === 0)
|
|
245
|
+
released.resolve();
|
|
246
|
+
};
|
|
247
|
+
const settled = (event) => {
|
|
248
|
+
leader.resolve(event);
|
|
249
|
+
release("leader");
|
|
250
|
+
};
|
|
251
|
+
const pipe = (name, source) => {
|
|
252
|
+
if (isClosed || source.closed)
|
|
253
|
+
release(name);
|
|
254
|
+
else
|
|
255
|
+
source.once("close", () => release(name));
|
|
256
|
+
};
|
|
257
|
+
return {
|
|
258
|
+
child,
|
|
259
|
+
leader: leader.promise,
|
|
260
|
+
closed: closed.promise,
|
|
261
|
+
released: released.promise,
|
|
262
|
+
pendingHandles,
|
|
263
|
+
get stdin() {
|
|
264
|
+
return stdin;
|
|
265
|
+
},
|
|
266
|
+
observe(onFailure) {
|
|
267
|
+
child.on("close", (exitCode, signal) => {
|
|
268
|
+
isClosed = true;
|
|
269
|
+
closed.resolve({ exitCode, signal });
|
|
270
|
+
settled({ kind: "exit", exitCode, signal });
|
|
271
|
+
pendingHandles.clear();
|
|
272
|
+
released.resolve();
|
|
273
|
+
});
|
|
274
|
+
child.on("error", (cause) => {
|
|
275
|
+
settled({ kind: "error", cause });
|
|
276
|
+
onFailure(cause);
|
|
277
|
+
});
|
|
278
|
+
child.on("exit", (exitCode, signal) => settled({ kind: "exit", exitCode, signal }));
|
|
279
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
280
|
+
settled({ kind: "exit", exitCode: child.exitCode, signal: child.signalCode });
|
|
281
|
+
},
|
|
282
|
+
capture() {
|
|
283
|
+
stdout = child.stdout;
|
|
284
|
+
destructors.set("stdout", () => stdout?.destroy());
|
|
285
|
+
stderr = child.stderr;
|
|
286
|
+
destructors.set("stderr", () => stderr?.destroy());
|
|
287
|
+
const input = "stdin" in child ? child.stdin : undefined;
|
|
288
|
+
if (input instanceof Writable) {
|
|
289
|
+
stdin = input;
|
|
290
|
+
pendingHandles.add("stdin");
|
|
291
|
+
destructors.set("stdin", () => input.destroy());
|
|
292
|
+
pipe("stdin", input);
|
|
293
|
+
}
|
|
294
|
+
if (stdout instanceof Readable)
|
|
295
|
+
pipe("stdout", stdout);
|
|
296
|
+
if (stderr instanceof Readable)
|
|
297
|
+
pipe("stderr", stderr);
|
|
298
|
+
if (stdout instanceof Readable && stderr instanceof Readable)
|
|
299
|
+
release("launcher");
|
|
300
|
+
return { stdout, stderr };
|
|
301
|
+
},
|
|
302
|
+
pipeDestructor(name, destroy) {
|
|
303
|
+
destructors.set(name, destroy);
|
|
304
|
+
},
|
|
305
|
+
destroy() {
|
|
306
|
+
const failures = [];
|
|
307
|
+
for (const name of ["stdin", "stdout", "stderr"]) {
|
|
308
|
+
try {
|
|
309
|
+
const destroy = destructors.get(name);
|
|
310
|
+
if (destroy)
|
|
311
|
+
destroy();
|
|
312
|
+
else if (name === "stdout")
|
|
313
|
+
child.stdout.destroy();
|
|
314
|
+
else if (name === "stderr")
|
|
315
|
+
child.stderr.destroy();
|
|
316
|
+
else if ("stdin" in child && child.stdin instanceof Writable)
|
|
317
|
+
child.stdin.destroy();
|
|
318
|
+
} catch (cause) {
|
|
319
|
+
failures.push(cause);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (failures.length)
|
|
323
|
+
throw new AggregateError(failures, "Command pipe teardown failed");
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/process/launch.ts
|
|
150
329
|
function nativeCommandBudgets(options, driver) {
|
|
151
330
|
const cleanupTimeoutMs = driver?.cleanupTimeoutMs ?? options.cleanupTimeoutMs;
|
|
152
331
|
const timeoutMs = driver?.timeoutMs ?? options.timeoutMs;
|
|
@@ -167,10 +346,9 @@ function nativeCommandOwner(child) {
|
|
|
167
346
|
return owners.get(child);
|
|
168
347
|
}
|
|
169
348
|
function outputStream(source, closed, maxBufferedBytes) {
|
|
170
|
-
if (source instanceof
|
|
349
|
+
if (source instanceof Readable2)
|
|
171
350
|
return {
|
|
172
351
|
bytes: source,
|
|
173
|
-
released: new Promise((resolve) => source.closed ? resolve() : source.once("close", resolve)),
|
|
174
352
|
destroy() {
|
|
175
353
|
source.destroy();
|
|
176
354
|
}
|
|
@@ -227,9 +405,6 @@ function outputStream(source, closed, maxBufferedBytes) {
|
|
|
227
405
|
}
|
|
228
406
|
}
|
|
229
407
|
},
|
|
230
|
-
released: closed.then(() => {
|
|
231
|
-
return;
|
|
232
|
-
}),
|
|
233
408
|
destroy() {
|
|
234
409
|
ended = true;
|
|
235
410
|
queue.length = 0;
|
|
@@ -239,80 +414,41 @@ function outputStream(source, closed, maxBufferedBytes) {
|
|
|
239
414
|
}
|
|
240
415
|
};
|
|
241
416
|
}
|
|
242
|
-
function launchNativeCommand(options, driver, onSpawn, onFailure, onInputFailure) {
|
|
417
|
+
function launchNativeCommand(options, driver, onSpawn, onFailure, onInputFailure, onAcquired) {
|
|
243
418
|
let native;
|
|
244
419
|
const launch = driver?.launch;
|
|
245
420
|
if (!launch)
|
|
246
|
-
native =
|
|
421
|
+
native = spawnOwnedCommand({
|
|
422
|
+
executable: options.executable,
|
|
423
|
+
args: options.args,
|
|
247
424
|
cwd: options.cwd,
|
|
248
425
|
env: options.envPolicy === "ambient" ? { ...process.env, ...options.env } : options.env ?? {},
|
|
249
|
-
|
|
250
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
426
|
+
group: driver?.group ?? true
|
|
251
427
|
});
|
|
252
428
|
const child = launch ? launch() : native;
|
|
253
429
|
if (!child)
|
|
254
430
|
throw new Error("Native command launcher did not supply a process");
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
child.on("close", (exitCode, signal) => resolve({ exitCode, signal }));
|
|
265
|
-
});
|
|
266
|
-
const stdout = outputStream(child.stdout, closed, driver?.maxBufferedBytes ?? 1024 * 1024);
|
|
267
|
-
const stderr = outputStream(child.stderr, closed, driver?.maxBufferedBytes ?? 1024 * 1024);
|
|
268
|
-
const stdin = "stdin" in child && child.stdin instanceof Writable ? child.stdin : undefined;
|
|
269
|
-
const handles = [
|
|
270
|
-
["stdout", stdout.released],
|
|
271
|
-
["stderr", stderr.released]
|
|
272
|
-
];
|
|
273
|
-
if (stdin)
|
|
274
|
-
handles.push([
|
|
275
|
-
"stdin",
|
|
276
|
-
new Promise((resolve) => stdin.closed ? resolve() : stdin.once("close", resolve))
|
|
277
|
-
]);
|
|
278
|
-
const released = Promise.all([
|
|
279
|
-
leader.then(() => pendingHandles.delete("leader")),
|
|
280
|
-
...handles.map(([name, released]) => {
|
|
281
|
-
pendingHandles.add(name);
|
|
282
|
-
return released.then(() => pendingHandles.delete(name));
|
|
283
|
-
}),
|
|
284
|
-
...child.stdout instanceof Readable && child.stderr instanceof Readable ? [] : [closed.then(() => pendingHandles.delete("launcher"))]
|
|
285
|
-
]);
|
|
286
|
-
if (!(child.stdout instanceof Readable && child.stderr instanceof Readable))
|
|
287
|
-
pendingHandles.add("launcher");
|
|
431
|
+
const transport = createCommandTransport(child);
|
|
432
|
+
onAcquired(transport);
|
|
433
|
+
transport.observe(onFailure);
|
|
434
|
+
const pipes = transport.capture();
|
|
435
|
+
const stdout = outputStream(pipes.stdout, transport.closed, driver?.maxBufferedBytes ?? 1024 * 1024);
|
|
436
|
+
transport.pipeDestructor("stdout", () => stdout.destroy());
|
|
437
|
+
const stderr = outputStream(pipes.stderr, transport.closed, driver?.maxBufferedBytes ?? 1024 * 1024);
|
|
438
|
+
transport.pipeDestructor("stderr", () => stderr.destroy());
|
|
439
|
+
const stdin = transport.stdin;
|
|
288
440
|
stdin?.on("error", (error) => {
|
|
289
441
|
if (!(("code" in error) && error.code === "EPIPE"))
|
|
290
442
|
onInputFailure(error);
|
|
291
443
|
});
|
|
292
444
|
return {
|
|
293
|
-
|
|
445
|
+
...transport,
|
|
294
446
|
stdout: stdout.bytes,
|
|
295
447
|
stderr: stderr.bytes,
|
|
296
|
-
leader,
|
|
297
|
-
closed,
|
|
298
|
-
released,
|
|
299
|
-
pendingHandles,
|
|
300
448
|
start() {
|
|
301
449
|
if (native)
|
|
302
450
|
onSpawn?.(native);
|
|
303
451
|
stdin?.end(options.stdin);
|
|
304
|
-
},
|
|
305
|
-
destroy() {
|
|
306
|
-
const failures = [];
|
|
307
|
-
for (const handle of [stdin, stdout, stderr]) {
|
|
308
|
-
try {
|
|
309
|
-
handle?.destroy();
|
|
310
|
-
} catch (cause) {
|
|
311
|
-
failures.push(cause);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (failures.length)
|
|
315
|
-
throw new AggregateError(failures, "Command pipe teardown failed");
|
|
316
452
|
}
|
|
317
453
|
};
|
|
318
454
|
}
|
|
@@ -374,30 +510,12 @@ function createLeaderSettlement(options, signal) {
|
|
|
374
510
|
}
|
|
375
511
|
|
|
376
512
|
// src/process/start-failure.ts
|
|
377
|
-
function failedCommandStart(cause,
|
|
513
|
+
async function failedCommandStart(cause, cleanup) {
|
|
378
514
|
const error = new NativeCommandError2("COMMAND_UNAVAILABLE", "Command could not start", {
|
|
379
515
|
cause
|
|
380
516
|
});
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
await settle({ kind: "error", cause });
|
|
384
|
-
} catch (cleanup) {
|
|
385
|
-
throw commandCleanupError(new AggregateError([error, cleanup], "Command failed and cleanup failed"));
|
|
386
|
-
}
|
|
387
|
-
throw error;
|
|
388
|
-
})();
|
|
389
|
-
result.catch(() => {
|
|
390
|
-
return;
|
|
391
|
-
});
|
|
392
|
-
return {
|
|
393
|
-
result,
|
|
394
|
-
async stop() {
|
|
395
|
-
await result.catch((failure) => {
|
|
396
|
-
if (failure instanceof NativeCommandError2 && failure.code === "COMMAND_CLEANUP")
|
|
397
|
-
throw failure;
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
};
|
|
517
|
+
await cleanup(error);
|
|
518
|
+
throw error;
|
|
401
519
|
}
|
|
402
520
|
|
|
403
521
|
// src/process/command-owner.ts
|
|
@@ -421,6 +539,31 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
421
539
|
else
|
|
422
540
|
deadline?.signal.addEventListener("abort", expired, { once: true });
|
|
423
541
|
const settle = createLeaderSettlement({ ...options, cleanupTimeoutMs }, controller.signal);
|
|
542
|
+
let acquired;
|
|
543
|
+
let finished = false;
|
|
544
|
+
const completion = Promise.withResolvers();
|
|
545
|
+
const result = completion.promise.finally(() => {
|
|
546
|
+
finished = true;
|
|
547
|
+
});
|
|
548
|
+
result.catch(() => {
|
|
549
|
+
return;
|
|
550
|
+
});
|
|
551
|
+
const signalOwned = (force = driver?.force) => {
|
|
552
|
+
const child = acquired?.child;
|
|
553
|
+
if (!child)
|
|
554
|
+
return Promise.resolve();
|
|
555
|
+
return driver?.group === false || !ownsCommandGroup(child) ? Promise.resolve().then(() => {
|
|
556
|
+
if (child.exitCode === null && child.signalCode === null && !child.kill("SIGKILL"))
|
|
557
|
+
throw new Error("Command leader termination was refused");
|
|
558
|
+
}) : stopCommandGroup(child.pid, options.killGraceMs, cleanupTimeoutMs, force);
|
|
559
|
+
};
|
|
560
|
+
const owner = createCommandLifetime({
|
|
561
|
+
result,
|
|
562
|
+
controller,
|
|
563
|
+
finished: () => finished,
|
|
564
|
+
signal: () => signalOwned(true),
|
|
565
|
+
cleanupTimeoutMs
|
|
566
|
+
});
|
|
424
567
|
let spawnFailure;
|
|
425
568
|
let launched;
|
|
426
569
|
try {
|
|
@@ -429,32 +572,30 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
429
572
|
cause: error
|
|
430
573
|
});
|
|
431
574
|
controller.abort(spawnFailure);
|
|
432
|
-
}, (error) => controller.abort(error))
|
|
575
|
+
}, (error) => controller.abort(error), (transport) => {
|
|
576
|
+
acquired = transport;
|
|
577
|
+
registerNativeCommandOwner(transport.child, owner);
|
|
578
|
+
});
|
|
433
579
|
} catch (cause) {
|
|
434
580
|
deadline?.cancel();
|
|
435
581
|
deadline?.signal.removeEventListener("abort", expired);
|
|
436
582
|
options.signal?.removeEventListener("abort", abort);
|
|
437
583
|
controller.abort(cause);
|
|
438
|
-
|
|
584
|
+
failedCommandStart(cause, (error) => cleanupNativeCommand({
|
|
585
|
+
error,
|
|
586
|
+
transport: acquired,
|
|
587
|
+
settle,
|
|
588
|
+
event: { kind: "error", cause },
|
|
589
|
+
signal: () => signalOwned(),
|
|
590
|
+
timeoutMs: cleanupTimeoutMs
|
|
591
|
+
})).then(completion.resolve, completion.reject);
|
|
592
|
+
return owner;
|
|
439
593
|
}
|
|
440
594
|
const stdout = [];
|
|
441
595
|
const stderr = [];
|
|
442
596
|
let total = 0;
|
|
443
|
-
|
|
444
|
-
const {
|
|
445
|
-
child,
|
|
446
|
-
stdout: output,
|
|
447
|
-
stderr: errorOutput,
|
|
448
|
-
leader,
|
|
449
|
-
closed,
|
|
450
|
-
released,
|
|
451
|
-
pendingHandles
|
|
452
|
-
} = launched;
|
|
597
|
+
const { stdout: output, stderr: errorOutput, leader, closed } = launched;
|
|
453
598
|
const cancelCloseDeadline = createLeaderCloseDeadline(leader, driver?.closeTimeoutMs, controller);
|
|
454
|
-
const signalOwned = (force = driver?.force) => driver?.group === false || driver?.launch && child.pid === undefined ? Promise.resolve().then(() => {
|
|
455
|
-
if (child.exitCode === null && child.signalCode === null && !child.kill("SIGKILL"))
|
|
456
|
-
throw new Error("Command leader termination was refused");
|
|
457
|
-
}) : stopCommandGroup(child.pid, options.killGraceMs, cleanupTimeoutMs, force);
|
|
458
599
|
const drain = async (stream, channel, captured) => {
|
|
459
600
|
for await (const chunk of stream) {
|
|
460
601
|
controller.signal.throwIfAborted();
|
|
@@ -474,7 +615,7 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
474
615
|
}), controller.signal);
|
|
475
616
|
}
|
|
476
617
|
};
|
|
477
|
-
const
|
|
618
|
+
const run = (async () => {
|
|
478
619
|
try {
|
|
479
620
|
launched.start();
|
|
480
621
|
const drains = Promise.all([
|
|
@@ -488,23 +629,13 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
488
629
|
return { ...status, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr) };
|
|
489
630
|
} catch (error) {
|
|
490
631
|
controller.abort(error);
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
throw new Error(`Command handles remained open: ${[...pendingHandles].join(", ")}`, {
|
|
499
|
-
cause
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
})
|
|
503
|
-
]);
|
|
504
|
-
const failures = cleanup.flatMap((item) => item.status === "rejected" && item.reason !== error ? [item.reason] : []);
|
|
505
|
-
if (failures.length) {
|
|
506
|
-
throw commandCleanupError(new AggregateError([error, ...failures], "Command failed and cleanup failed"));
|
|
507
|
-
}
|
|
632
|
+
await cleanupNativeCommand({
|
|
633
|
+
error,
|
|
634
|
+
transport: acquired,
|
|
635
|
+
settle,
|
|
636
|
+
signal: () => signalOwned(),
|
|
637
|
+
timeoutMs: cleanupTimeoutMs
|
|
638
|
+
});
|
|
508
639
|
throw error;
|
|
509
640
|
} finally {
|
|
510
641
|
finished = true;
|
|
@@ -515,51 +646,8 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
515
646
|
controller.abort(new Error("Command completed"));
|
|
516
647
|
}
|
|
517
648
|
})();
|
|
518
|
-
|
|
519
|
-
return;
|
|
520
|
-
});
|
|
521
|
-
const owner = {
|
|
522
|
-
result,
|
|
523
|
-
async terminate() {
|
|
524
|
-
let refused = false;
|
|
525
|
-
let refusal;
|
|
526
|
-
if (!finished) {
|
|
527
|
-
try {
|
|
528
|
-
await signalOwned(true);
|
|
529
|
-
} catch (error) {
|
|
530
|
-
refused = true;
|
|
531
|
-
refusal = error;
|
|
532
|
-
controller.abort(error);
|
|
533
|
-
}
|
|
534
|
-
try {
|
|
535
|
-
await waitForCommandClose(result, cleanupTimeoutMs);
|
|
536
|
-
} catch (error) {
|
|
537
|
-
if (!finished)
|
|
538
|
-
controller.abort(error);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
await result.then(() => {
|
|
542
|
-
return;
|
|
543
|
-
}, (error) => {
|
|
544
|
-
if (error instanceof NativeCommandError2 && error.code === "COMMAND_CLEANUP")
|
|
545
|
-
throw error;
|
|
546
|
-
});
|
|
547
|
-
if (refused)
|
|
548
|
-
throw commandCleanupError(refusal);
|
|
549
|
-
},
|
|
550
|
-
async stop() {
|
|
551
|
-
if (!finished)
|
|
552
|
-
controller.abort(new DOMException("Command stopped", "AbortError"));
|
|
553
|
-
await result.then(() => {
|
|
554
|
-
return;
|
|
555
|
-
}, (error) => {
|
|
556
|
-
if (error instanceof NativeCommandError2 && error.code === "COMMAND_CLEANUP")
|
|
557
|
-
throw error;
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
registerNativeCommandOwner(child, owner);
|
|
649
|
+
run.then(completion.resolve, completion.reject);
|
|
562
650
|
return owner;
|
|
563
651
|
}
|
|
564
652
|
|
|
565
|
-
export { NativeCommandError2, stopCommandGroup, nativeCommandOwner, startNativeCommand };
|
|
653
|
+
export { NativeCommandError2, stopCommandGroup, spawnOwnedCommand, ownsCommandGroup, nativeCommandOwner, startNativeCommand };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/internal/json-string-size.ts
|
|
2
|
+
function jsonStringSize(text, budget) {
|
|
3
|
+
if (text.length + 2 > budget)
|
|
4
|
+
return budget + 1;
|
|
5
|
+
let bytes = 2;
|
|
6
|
+
for (let index = 0;index < text.length && bytes <= budget; index++) {
|
|
7
|
+
const unit = text.charCodeAt(index);
|
|
8
|
+
if (unit === 34 || unit === 92)
|
|
9
|
+
bytes += 2;
|
|
10
|
+
else if (unit < 32)
|
|
11
|
+
bytes += unit === 8 || unit === 9 || unit === 10 || unit === 12 || unit === 13 ? 2 : 6;
|
|
12
|
+
else if (unit < 128)
|
|
13
|
+
bytes++;
|
|
14
|
+
else if (unit < 2048)
|
|
15
|
+
bytes += 2;
|
|
16
|
+
else if (unit >= 55296 && unit <= 56319) {
|
|
17
|
+
const next = text.charCodeAt(index + 1);
|
|
18
|
+
if (next >= 56320 && next <= 57343) {
|
|
19
|
+
bytes += 4;
|
|
20
|
+
index++;
|
|
21
|
+
} else
|
|
22
|
+
bytes += 6;
|
|
23
|
+
} else if (unit >= 56320 && unit <= 57343)
|
|
24
|
+
bytes += 6;
|
|
25
|
+
else
|
|
26
|
+
bytes += 3;
|
|
27
|
+
}
|
|
28
|
+
return bytes;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/internal/json-data.ts
|
|
32
|
+
function isJsonData(value, limits, snapshot) {
|
|
33
|
+
const ancestors = new Set;
|
|
34
|
+
let nodes = 0;
|
|
35
|
+
let bytes = 0;
|
|
36
|
+
const byteLimit = limits.maxBytes;
|
|
37
|
+
const charge = (amount) => {
|
|
38
|
+
if (byteLimit === undefined)
|
|
39
|
+
return true;
|
|
40
|
+
bytes += amount;
|
|
41
|
+
return bytes <= byteLimit;
|
|
42
|
+
};
|
|
43
|
+
const chargeText = (text) => byteLimit === undefined || charge(jsonStringSize(text, byteLimit - bytes));
|
|
44
|
+
const visit = (item, depth, target, key) => {
|
|
45
|
+
if (++nodes > limits.maxNodes || depth > limits.maxDepth)
|
|
46
|
+
return false;
|
|
47
|
+
let captured = item;
|
|
48
|
+
if (item === null) {
|
|
49
|
+
if (!charge(4))
|
|
50
|
+
return false;
|
|
51
|
+
} else if (typeof item === "string") {
|
|
52
|
+
if (!chargeText(item))
|
|
53
|
+
return false;
|
|
54
|
+
} else if (typeof item === "boolean") {
|
|
55
|
+
if (!charge(item ? 4 : 5))
|
|
56
|
+
return false;
|
|
57
|
+
} else if (typeof item === "number") {
|
|
58
|
+
if (!Number.isFinite(item) || Object.is(item, -0))
|
|
59
|
+
return false;
|
|
60
|
+
if (byteLimit !== undefined && !charge(String(item).length))
|
|
61
|
+
return false;
|
|
62
|
+
} else {
|
|
63
|
+
if (typeof item !== "object" || ancestors.has(item))
|
|
64
|
+
return false;
|
|
65
|
+
const array = Array.isArray(item);
|
|
66
|
+
const prototype = Object.getPrototypeOf(item);
|
|
67
|
+
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
|
|
68
|
+
return false;
|
|
69
|
+
let length = 0;
|
|
70
|
+
if (array) {
|
|
71
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, "length");
|
|
72
|
+
if (!descriptor || !("value" in descriptor))
|
|
73
|
+
return false;
|
|
74
|
+
const capturedLength = descriptor.value;
|
|
75
|
+
if (typeof capturedLength !== "number" || !Number.isInteger(capturedLength) || capturedLength < 0 || capturedLength > 2 ** 32 - 1)
|
|
76
|
+
return false;
|
|
77
|
+
length = capturedLength;
|
|
78
|
+
if (length > limits.maxNodes - nodes)
|
|
79
|
+
return false;
|
|
80
|
+
if (byteLimit !== undefined && Math.max(2, length * 2 + 1) > byteLimit - bytes)
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const keys = Reflect.ownKeys(item);
|
|
84
|
+
if (keys.length > limits.maxNodes - nodes + (array ? 1 : 0))
|
|
85
|
+
return false;
|
|
86
|
+
if (array && keys.length !== length + 1)
|
|
87
|
+
return false;
|
|
88
|
+
if (!charge(2))
|
|
89
|
+
return false;
|
|
90
|
+
const copy = target ? array ? [] : {} : undefined;
|
|
91
|
+
ancestors.add(item);
|
|
92
|
+
try {
|
|
93
|
+
let members = 0;
|
|
94
|
+
for (const memberKey of keys) {
|
|
95
|
+
if (array && memberKey === "length")
|
|
96
|
+
continue;
|
|
97
|
+
if (typeof memberKey !== "string")
|
|
98
|
+
return false;
|
|
99
|
+
if (array && (!/^(0|[1-9][0-9]*)$/.test(memberKey) || Number(memberKey) >= length))
|
|
100
|
+
return false;
|
|
101
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, memberKey);
|
|
102
|
+
if (!descriptor?.enumerable || !("value" in descriptor))
|
|
103
|
+
return false;
|
|
104
|
+
const memberValue = descriptor.value;
|
|
105
|
+
if (!array && limits.omitObjectUndefined && memberValue === undefined) {
|
|
106
|
+
if (++nodes > limits.maxNodes)
|
|
107
|
+
return false;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (members++ > 0 && !charge(1))
|
|
111
|
+
return false;
|
|
112
|
+
if (!array && (!chargeText(memberKey) || !charge(1)))
|
|
113
|
+
return false;
|
|
114
|
+
if (!visit(memberValue, depth + 1, copy, memberKey))
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
captured = copy;
|
|
118
|
+
} finally {
|
|
119
|
+
ancestors.delete(item);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (target && key !== undefined)
|
|
123
|
+
Object.defineProperty(target, key, {
|
|
124
|
+
value: captured,
|
|
125
|
+
enumerable: true,
|
|
126
|
+
configurable: true,
|
|
127
|
+
writable: true
|
|
128
|
+
});
|
|
129
|
+
return true;
|
|
130
|
+
};
|
|
131
|
+
try {
|
|
132
|
+
return visit(value, 0, snapshot, "value");
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { isJsonData };
|