stitchkit 0.108.1 → 0.108.3
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 +29 -0
- package/dist/entrypoints/agent-runtime/coding-tools.js +3 -2
- package/dist/entrypoints/agent-runtime/sandbox.js +5 -3
- package/dist/entrypoints/cli.js +1 -1
- package/dist/entrypoints/contract.js +1 -1
- package/dist/entrypoints/index.js +1 -1
- package/dist/entrypoints/node.js +1 -1
- package/dist/entrypoints/process/owner-loss.d.ts +3 -0
- package/dist/entrypoints/process/owner-loss.d.ts.map +1 -0
- package/dist/entrypoints/process/owner-loss.js +6 -0
- package/dist/entrypoints/process.js +4 -2
- package/dist/entrypoints/server.js +1 -1
- package/dist/entrypoints/tools.js +1 -1
- package/dist/{index-epg514ys.js → index-dasy32c8.js} +76 -328
- package/dist/index-dj1aycxw.js +639 -0
- package/dist/{index-4qh90tyw.js → index-fgq4w9bf.js} +23 -1
- package/dist/{index-e35py42g.js → index-tycmtxky.js} +6 -4
- package/dist/process/bun-child.d.ts +10 -0
- package/dist/process/bun-child.d.ts.map +1 -1
- package/dist/process/command-owner.d.ts.map +1 -1
- package/dist/process/contract.d.ts +5 -0
- package/dist/process/contract.d.ts.map +1 -1
- package/dist/process/launch.d.ts +1 -1
- package/dist/process/launch.d.ts.map +1 -1
- package/dist/process/owned-child.d.ts +3 -0
- package/dist/process/owned-child.d.ts.map +1 -1
- package/dist/process/owner-loss-control.d.ts +15 -0
- package/dist/process/owner-loss-control.d.ts.map +1 -0
- package/dist/process/owner-loss-guard.d.ts +12 -0
- package/dist/process/owner-loss-guard.d.ts.map +1 -0
- package/dist/process/owner-loss-protocol.d.ts +10 -0
- package/dist/process/owner-loss-protocol.d.ts.map +1 -0
- package/dist/process/owner-loss-socket.d.ts +4 -0
- package/dist/process/owner-loss-socket.d.ts.map +1 -0
- package/dist/tools/cli/args-fields.d.ts.map +1 -1
- package/llms/cli.2.txt +107 -0
- package/llms/cli.txt +8 -107
- package/llms/process-owner-loss.txt +18 -0
- package/llms/process.txt +43 -10
- package/llms/stitchkit.txt +2 -1
- package/llms-full.txt +61 -11
- package/llms.txt +4 -3
- package/package.json +5 -1
- /package/dist/{index-q4hjtyye.js → index-qd8x73e8.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,35 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.108.3] - 2026-10-09
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `stitchkit/process/owner-loss` — `bootstrapNativeCommandOwnerLoss()` lets a single-file Bun
|
|
23
|
+
application keep `stitchkit/process` behind a dynamic import while carrying the same package-owned
|
|
24
|
+
owner-loss guard in its bundle. Call it before application argv routing and skip that routing when
|
|
25
|
+
it returns `true`; consumers do not inspect or copy the private guard protocol. → ADR 0255
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- `stitchkit/cli` — numeric, bigint and boolean `z.literal()` fields now receive their primitive
|
|
30
|
+
values from argv, including homogeneous literal sets, unions, wrappers and array elements. String
|
|
31
|
+
literals retain their lexical form (`007` stays `"007"`), and mixed-type literal sets remain raw
|
|
32
|
+
rather than guessing a type from spelling.
|
|
33
|
+
|
|
34
|
+
## [0.108.2] - 2026-10-09
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- `stitchkit/process` — `runNativeCommand` takes `ownerLoss: 'terminate'`: on Linux and Darwin under
|
|
39
|
+
Bun or Node, a package-owned per-command guard binds the command's process group to a private
|
|
40
|
+
inherited kernel channel. If the JavaScript caller is killed or crashes, channel EOF makes the
|
|
41
|
+
guard KILL its own group without polling, `finally` or PID reclamation. The option requires
|
|
42
|
+
`group: 'own'`; unsupported platforms refuse with `COMMAND_UNAVAILABLE`. It is opt-in because each
|
|
43
|
+
guarded call uses one extra short-lived process and descriptor. Output, stdin, target exit status,
|
|
44
|
+
deadlines and cancellation retain the existing contract; `onLeaderStarted` reports the guard that
|
|
45
|
+
leads the owned group. → ADR 0255
|
|
46
|
+
|
|
18
47
|
## [0.108.1] - 2026-10-07
|
|
19
48
|
|
|
20
49
|
### Fixed
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
AgentCodingToolAuthorizationSchema2,
|
|
7
7
|
AgentCodingToolPathAuthorizationSchema2,
|
|
8
8
|
createAgentCodingTools2
|
|
9
|
-
} from "../../index-
|
|
9
|
+
} from "../../index-tycmtxky.js";
|
|
10
10
|
import"../../index-2kbysk9s.js";
|
|
11
|
-
import"../../index-
|
|
11
|
+
import"../../index-dj1aycxw.js";
|
|
12
12
|
import"../../index-8ks9b85e.js";
|
|
13
|
+
import"../../index-dasy32c8.js";
|
|
13
14
|
import"../../index-zrtrjkbf.js";
|
|
14
15
|
import"../../index-636mggad.js";
|
|
15
16
|
export {
|
|
@@ -8,16 +8,18 @@ import"../../index-q6crhe1n.js";
|
|
|
8
8
|
import"../../index-fynr93w4.js";
|
|
9
9
|
import {
|
|
10
10
|
createAgentCodingTools2
|
|
11
|
-
} from "../../index-
|
|
11
|
+
} from "../../index-tycmtxky.js";
|
|
12
12
|
import"../../index-2kbysk9s.js";
|
|
13
13
|
import {
|
|
14
14
|
COMMAND_CLEANUP_TIMEOUT_MS,
|
|
15
|
-
NativeCommandError2
|
|
15
|
+
NativeCommandError2
|
|
16
|
+
} from "../../index-dj1aycxw.js";
|
|
17
|
+
import {
|
|
16
18
|
spawnOwnedCommand,
|
|
17
19
|
nativeCommandOwner,
|
|
18
20
|
terminateOwnedGroup,
|
|
19
21
|
startNativeCommand
|
|
20
|
-
} from "../../index-
|
|
22
|
+
} from "../../index-dasy32c8.js";
|
|
21
23
|
import"../../index-zrtrjkbf.js";
|
|
22
24
|
import {
|
|
23
25
|
MAX_TIMER_MS
|
package/dist/entrypoints/cli.js
CHANGED
package/dist/entrypoints/node.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner-loss.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/process/owner-loss.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAEhG,OAAO,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC"}
|
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
probeProcessOwner2
|
|
6
6
|
} from "../index-bh2ht9y5.js";
|
|
7
7
|
import {
|
|
8
|
-
NativeCommandError2
|
|
8
|
+
NativeCommandError2
|
|
9
|
+
} from "../index-dj1aycxw.js";
|
|
10
|
+
import {
|
|
9
11
|
startNativeCommand
|
|
10
|
-
} from "../index-
|
|
12
|
+
} from "../index-dasy32c8.js";
|
|
11
13
|
// src/process/command.ts
|
|
12
14
|
function runNativeCommand(input) {
|
|
13
15
|
return startNativeCommand(input).result;
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NativeCommandOptionsSchema,
|
|
3
|
+
NativeCommandError2,
|
|
4
|
+
abortOwnerLossLaunch,
|
|
5
|
+
attachOwnerLossControl,
|
|
6
|
+
transferOwnerLossControl,
|
|
7
|
+
awaitCommandStart,
|
|
8
|
+
ownerLossAvailable,
|
|
9
|
+
ownerLossGuardCommand,
|
|
10
|
+
hostIsBun,
|
|
11
|
+
spawnBunChild,
|
|
12
|
+
spawnBunInheritedChild,
|
|
13
|
+
ownerLossGuardEntry
|
|
14
|
+
} from "./index-dj1aycxw.js";
|
|
1
15
|
import {
|
|
2
16
|
raceAbort
|
|
3
17
|
} from "./index-b0kddvzw.js";
|
|
@@ -9,147 +23,6 @@ import {
|
|
|
9
23
|
assertPositiveSafeInteger
|
|
10
24
|
} from "./index-0s4xebbg.js";
|
|
11
25
|
|
|
12
|
-
// src/process/contract.ts
|
|
13
|
-
import { z } from "zod";
|
|
14
|
-
var NativeCommandSettlementSchema = z.discriminatedUnion("kind", [
|
|
15
|
-
z.object({
|
|
16
|
-
kind: z.literal("exit"),
|
|
17
|
-
exitCode: z.number().int().nullable(),
|
|
18
|
-
signal: z.string().nullable()
|
|
19
|
-
}),
|
|
20
|
-
z.object({
|
|
21
|
-
kind: z.literal("stopped"),
|
|
22
|
-
cause: z.unknown(),
|
|
23
|
-
exitCode: z.number().int().nullable(),
|
|
24
|
-
signal: z.string().nullable()
|
|
25
|
-
}),
|
|
26
|
-
z.object({ kind: z.literal("error"), cause: z.unknown() })
|
|
27
|
-
]);
|
|
28
|
-
var COMMAND_CLEANUP_TIMEOUT_MS = 2000;
|
|
29
|
-
var MAX_COMMAND_STOP_GRACE_MS = 3600000;
|
|
30
|
-
var NativeCommandStopPolicySchema = z.strictObject({
|
|
31
|
-
target: z.enum(["group", "leader"]),
|
|
32
|
-
signal: z.enum(["SIGTERM", "SIGINT", "SIGHUP", "SIGQUIT", "SIGUSR1", "SIGUSR2"]).default("SIGTERM"),
|
|
33
|
-
graceMs: z.number().int().nonnegative().max(MAX_COMMAND_STOP_GRACE_MS),
|
|
34
|
-
killOn: z.custom((v) => v instanceof AbortSignal).optional()
|
|
35
|
-
});
|
|
36
|
-
var REMOVED_NATIVE_COMMAND_OPTIONS = {
|
|
37
|
-
killGraceMs: "stop: { target: 'group', graceMs } (removed in 0.105.0)"
|
|
38
|
-
};
|
|
39
|
-
function unknownOptionMessage(keys) {
|
|
40
|
-
return keys.map((key) => {
|
|
41
|
-
const name = String(key);
|
|
42
|
-
const replacement = Object.hasOwn(REMOVED_NATIVE_COMMAND_OPTIONS, name) ? REMOVED_NATIVE_COMMAND_OPTIONS[name] : undefined;
|
|
43
|
-
return replacement === undefined ? `Unknown runNativeCommand option ${name}` : `Unknown runNativeCommand option ${name}; use ${replacement}`;
|
|
44
|
-
}).join("; ");
|
|
45
|
-
}
|
|
46
|
-
var DEFAULT_GROUP_STOP = {
|
|
47
|
-
target: "group",
|
|
48
|
-
signal: "SIGTERM",
|
|
49
|
-
graceMs: 100
|
|
50
|
-
};
|
|
51
|
-
var DEFAULT_LEADER_STOP = {
|
|
52
|
-
target: "leader",
|
|
53
|
-
signal: "SIGTERM",
|
|
54
|
-
graceMs: 100
|
|
55
|
-
};
|
|
56
|
-
var NativeCommandOptionsSchema = z.strictObject({
|
|
57
|
-
executable: z.string().min(1),
|
|
58
|
-
args: z.array(z.string()).default([]),
|
|
59
|
-
cwd: z.string().optional(),
|
|
60
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
61
|
-
envPolicy: z.enum(["declared-only", "ambient"]).default("declared-only"),
|
|
62
|
-
signal: z.custom((v) => v instanceof AbortSignal).optional(),
|
|
63
|
-
timeoutMs: z.number().int().positive().max(MAX_TIMER_MS).optional(),
|
|
64
|
-
maxOutputBytes: z.number().int().positive().optional(),
|
|
65
|
-
capture: z.boolean().default(false),
|
|
66
|
-
stdin: z.custom((v) => v instanceof Uint8Array).optional(),
|
|
67
|
-
maxStdinBytes: z.number().int().positive().default(1024 * 1024),
|
|
68
|
-
onOutput: z.custom((v) => typeof v === "function").optional(),
|
|
69
|
-
onLeaderSettled: z.custom((v) => typeof v === "function").optional(),
|
|
70
|
-
onLeaderStarted: z.custom((v) => typeof v === "function").optional(),
|
|
71
|
-
drainTimeoutMs: z.number().int().positive().max(MAX_TIMER_MS).optional(),
|
|
72
|
-
group: z.enum(["own", "caller"]).default("own"),
|
|
73
|
-
stdio: z.enum(["pipe", "inherit"]).default("pipe"),
|
|
74
|
-
stop: NativeCommandStopPolicySchema.optional(),
|
|
75
|
-
descendants: z.enum(["terminate-after-leader", "leave"]).optional(),
|
|
76
|
-
cleanupTimeoutMs: z.number().int().positive().max(30000).default(COMMAND_CLEANUP_TIMEOUT_MS)
|
|
77
|
-
}, {
|
|
78
|
-
error: (issue) => issue.code === "unrecognized_keys" ? unknownOptionMessage(issue.keys) : undefined
|
|
79
|
-
}).check((ctx) => {
|
|
80
|
-
const input = ctx.value;
|
|
81
|
-
if (!input.signal && input.timeoutMs === undefined)
|
|
82
|
-
ctx.issues.push({
|
|
83
|
-
code: "custom",
|
|
84
|
-
input: ctx.value,
|
|
85
|
-
message: "Declare a caller signal or a finite timeout"
|
|
86
|
-
});
|
|
87
|
-
if (input.capture && input.maxOutputBytes === undefined)
|
|
88
|
-
ctx.issues.push({
|
|
89
|
-
code: "custom",
|
|
90
|
-
input: ctx.value,
|
|
91
|
-
message: "Capture requires a finite combined output budget"
|
|
92
|
-
});
|
|
93
|
-
if (input.stdin && input.stdin.byteLength > input.maxStdinBytes)
|
|
94
|
-
ctx.issues.push({
|
|
95
|
-
code: "custom",
|
|
96
|
-
input: ctx.value,
|
|
97
|
-
message: "stdin exceeds maxStdinBytes"
|
|
98
|
-
});
|
|
99
|
-
if (input.group === "caller" && input.stop?.target === "group")
|
|
100
|
-
ctx.issues.push({
|
|
101
|
-
code: "custom",
|
|
102
|
-
input: ctx.value,
|
|
103
|
-
message: "group: 'caller' stops only the leader; use stop.target 'leader'"
|
|
104
|
-
});
|
|
105
|
-
if (input.group === "caller" && input.descendants !== undefined)
|
|
106
|
-
ctx.issues.push({
|
|
107
|
-
code: "custom",
|
|
108
|
-
input: ctx.value,
|
|
109
|
-
message: "group: 'caller' has no group of its own, so descendants does not apply"
|
|
110
|
-
});
|
|
111
|
-
if (input.stdio === "inherit") {
|
|
112
|
-
const piped = [
|
|
113
|
-
input.capture && "capture",
|
|
114
|
-
input.onOutput && "onOutput",
|
|
115
|
-
input.stdin && "stdin",
|
|
116
|
-
input.maxOutputBytes !== undefined && "maxOutputBytes",
|
|
117
|
-
input.drainTimeoutMs !== undefined && "drainTimeoutMs"
|
|
118
|
-
].filter((name) => typeof name === "string");
|
|
119
|
-
for (const name of piped)
|
|
120
|
-
ctx.issues.push({
|
|
121
|
-
code: "custom",
|
|
122
|
-
input: ctx.value,
|
|
123
|
-
message: `stdio: 'inherit' leaves no pipe for ${name}`
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}).transform((input) => ({
|
|
127
|
-
...input,
|
|
128
|
-
stop: input.stop ?? (input.group === "caller" ? DEFAULT_LEADER_STOP : DEFAULT_GROUP_STOP),
|
|
129
|
-
descendants: input.descendants ?? "terminate-after-leader"
|
|
130
|
-
}));
|
|
131
|
-
var NativeCommandResultSchema = z.object({
|
|
132
|
-
exitCode: z.number().int().nullable(),
|
|
133
|
-
signal: z.string().nullable(),
|
|
134
|
-
descendantsStopped: z.boolean(),
|
|
135
|
-
stdout: z.instanceof(Uint8Array),
|
|
136
|
-
stderr: z.instanceof(Uint8Array)
|
|
137
|
-
});
|
|
138
|
-
var NativeCommandLimitReasonSchema = z.enum(["deadline", "output-budget"]);
|
|
139
|
-
|
|
140
|
-
class NativeCommandError2 extends Error {
|
|
141
|
-
code;
|
|
142
|
-
reason;
|
|
143
|
-
constructor(code, message, options) {
|
|
144
|
-
super(message, options);
|
|
145
|
-
this.code = code;
|
|
146
|
-
if (options?.reason !== undefined && code !== "COMMAND_LIMIT")
|
|
147
|
-
throw new TypeError("Only COMMAND_LIMIT accepts a limit reason");
|
|
148
|
-
this.reason = options?.reason === undefined ? undefined : NativeCommandLimitReasonSchema.parse(options.reason);
|
|
149
|
-
this.name = "NativeCommandError";
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
26
|
// src/process/group.ts
|
|
154
27
|
function createCommandDeadline(timeoutMs, reason) {
|
|
155
28
|
assertPositiveSafeInteger("Command deadline", timeoutMs, RangeError);
|
|
@@ -395,176 +268,11 @@ function createCommandLifetime(input) {
|
|
|
395
268
|
}
|
|
396
269
|
|
|
397
270
|
// src/process/launch.ts
|
|
398
|
-
import { Readable as
|
|
271
|
+
import { Readable as Readable2 } from "node:stream";
|
|
399
272
|
|
|
400
273
|
// src/process/owned-child.ts
|
|
401
274
|
import { spawn } from "node:child_process";
|
|
402
|
-
|
|
403
|
-
// src/process/bun-child.ts
|
|
404
|
-
import { EventEmitter } from "node:events";
|
|
405
|
-
import { Readable, Writable } from "node:stream";
|
|
406
|
-
function hostIsBun() {
|
|
407
|
-
return typeof Bun !== "undefined";
|
|
408
|
-
}
|
|
409
|
-
function readableOf(source) {
|
|
410
|
-
const reader = source.getReader();
|
|
411
|
-
let reading = false;
|
|
412
|
-
return new Readable({
|
|
413
|
-
read() {
|
|
414
|
-
if (reading)
|
|
415
|
-
return;
|
|
416
|
-
reading = true;
|
|
417
|
-
reader.read().then(({ done, value }) => {
|
|
418
|
-
reading = false;
|
|
419
|
-
if (done)
|
|
420
|
-
this.push(null);
|
|
421
|
-
else
|
|
422
|
-
this.push(value);
|
|
423
|
-
}, (error) => {
|
|
424
|
-
reading = false;
|
|
425
|
-
this.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
426
|
-
});
|
|
427
|
-
},
|
|
428
|
-
destroy(error, done) {
|
|
429
|
-
reader.cancel().then(() => done(error), () => done(error));
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
var REPORTED_SPAWN_CODES = new Set(["ENOENT", "EACCES", "EAGAIN", "EMFILE", "ENFILE"]);
|
|
434
|
-
function reportedSpawnFailure(cause) {
|
|
435
|
-
return typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string" && REPORTED_SPAWN_CODES.has(cause.code);
|
|
436
|
-
}
|
|
437
|
-
var waitClosed = (stream) => stream.closed ? Promise.resolve() : new Promise((resolve) => stream.once("close", () => resolve()));
|
|
438
|
-
function defineBunChildren() {
|
|
439
|
-
|
|
440
|
-
class BunProcess extends EventEmitter {
|
|
441
|
-
pipes;
|
|
442
|
-
pid;
|
|
443
|
-
exitCode = null;
|
|
444
|
-
signalCode = null;
|
|
445
|
-
#subprocess;
|
|
446
|
-
constructor(pipes) {
|
|
447
|
-
super();
|
|
448
|
-
this.pipes = pipes;
|
|
449
|
-
}
|
|
450
|
-
watch(subprocess) {
|
|
451
|
-
this.pid = subprocess.pid;
|
|
452
|
-
this.#subprocess = subprocess;
|
|
453
|
-
subprocess.exited.then(() => {
|
|
454
|
-
this.exitCode = subprocess.exitCode;
|
|
455
|
-
this.signalCode = subprocess.signalCode;
|
|
456
|
-
this.emit("exit", this.exitCode, this.signalCode);
|
|
457
|
-
for (const pipe of this.pipes)
|
|
458
|
-
if (pipe.readableFlowing === null)
|
|
459
|
-
pipe.resume();
|
|
460
|
-
this.closeAfterPipes();
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
fail(cause) {
|
|
464
|
-
for (const pipe of this.pipes)
|
|
465
|
-
pipe.resume();
|
|
466
|
-
setImmediate(() => {
|
|
467
|
-
this.emit("error", cause);
|
|
468
|
-
this.closeAfterPipes();
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
kill(signal) {
|
|
472
|
-
const subprocess = this.#subprocess;
|
|
473
|
-
if (!subprocess || this.exitCode !== null || this.signalCode !== null)
|
|
474
|
-
return false;
|
|
475
|
-
try {
|
|
476
|
-
subprocess.kill(signal);
|
|
477
|
-
return true;
|
|
478
|
-
} catch {
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
closeAfterPipes() {
|
|
483
|
-
Promise.all(this.pipes.map(waitClosed)).then(() => this.emit("close", this.exitCode, this.signalCode));
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
class BunChild extends BunProcess {
|
|
488
|
-
stdin;
|
|
489
|
-
stdout;
|
|
490
|
-
stderr;
|
|
491
|
-
constructor(stdin, stdout, stderr) {
|
|
492
|
-
super([stdout, stderr]);
|
|
493
|
-
this.stdin = stdin;
|
|
494
|
-
this.stdout = stdout;
|
|
495
|
-
this.stderr = stderr;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return { BunProcess, BunChild };
|
|
499
|
-
}
|
|
500
|
-
var bunChildren;
|
|
501
|
-
function children() {
|
|
502
|
-
bunChildren ??= defineBunChildren();
|
|
503
|
-
return bunChildren;
|
|
504
|
-
}
|
|
505
|
-
function sinkWritable(sink) {
|
|
506
|
-
let ended = false;
|
|
507
|
-
const end = () => {
|
|
508
|
-
if (ended)
|
|
509
|
-
return Promise.resolve(0);
|
|
510
|
-
ended = true;
|
|
511
|
-
return Promise.resolve().then(() => sink.end());
|
|
512
|
-
};
|
|
513
|
-
return new Writable({
|
|
514
|
-
write(chunk, _encoding, done) {
|
|
515
|
-
Promise.resolve().then(() => sink.write(chunk)).then(() => done(), done);
|
|
516
|
-
},
|
|
517
|
-
final(done) {
|
|
518
|
-
end().then(() => done(), done);
|
|
519
|
-
},
|
|
520
|
-
destroy(error, done) {
|
|
521
|
-
end().then(() => done(error), () => done(error));
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
function spawnBunChild(input) {
|
|
526
|
-
const { BunChild } = children();
|
|
527
|
-
try {
|
|
528
|
-
const subprocess = Bun.spawn([input.executable, ...input.args], {
|
|
529
|
-
cwd: input.cwd,
|
|
530
|
-
env: input.env,
|
|
531
|
-
detached: input.group,
|
|
532
|
-
stdin: "pipe",
|
|
533
|
-
stdout: "pipe",
|
|
534
|
-
stderr: "pipe"
|
|
535
|
-
});
|
|
536
|
-
const child = new BunChild(sinkWritable(subprocess.stdin), readableOf(subprocess.stdout), readableOf(subprocess.stderr));
|
|
537
|
-
child.watch(subprocess);
|
|
538
|
-
return child;
|
|
539
|
-
} catch (cause) {
|
|
540
|
-
if (!reportedSpawnFailure(cause))
|
|
541
|
-
throw cause;
|
|
542
|
-
const child = new BunChild(new Writable({ write: (_chunk, _encoding, done) => done() }), Readable.from([]), Readable.from([]));
|
|
543
|
-
child.fail(cause);
|
|
544
|
-
return child;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
function spawnBunInheritedChild(input) {
|
|
548
|
-
const { BunProcess } = children();
|
|
549
|
-
const child = new BunProcess([]);
|
|
550
|
-
try {
|
|
551
|
-
child.watch(Bun.spawn([input.executable, ...input.args], {
|
|
552
|
-
cwd: input.cwd,
|
|
553
|
-
env: input.env,
|
|
554
|
-
detached: input.group,
|
|
555
|
-
stdin: "inherit",
|
|
556
|
-
stdout: "inherit",
|
|
557
|
-
stderr: "inherit"
|
|
558
|
-
}));
|
|
559
|
-
} catch (cause) {
|
|
560
|
-
if (!reportedSpawnFailure(cause))
|
|
561
|
-
throw cause;
|
|
562
|
-
child.fail(cause);
|
|
563
|
-
}
|
|
564
|
-
return child;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// src/process/owned-child.ts
|
|
275
|
+
import { Duplex } from "node:stream";
|
|
568
276
|
var groups = new WeakSet;
|
|
569
277
|
function spawnOwnedCommand(input) {
|
|
570
278
|
const child = hostIsBun() ? spawnBunChild(input) : nodeChild(input);
|
|
@@ -573,12 +281,27 @@ function spawnOwnedCommand(input) {
|
|
|
573
281
|
return child;
|
|
574
282
|
}
|
|
575
283
|
function nodeChild(input) {
|
|
576
|
-
|
|
284
|
+
if (input.ownerLoss === undefined)
|
|
285
|
+
return spawn(input.executable, [...input.args], {
|
|
286
|
+
cwd: input.cwd,
|
|
287
|
+
env: input.env,
|
|
288
|
+
detached: input.group,
|
|
289
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
290
|
+
});
|
|
291
|
+
const [executable, ...args] = ownerLossGuardCommand(input.ownerLoss, input.executable, input.args);
|
|
292
|
+
const child = spawn(executable, args, {
|
|
577
293
|
cwd: input.cwd,
|
|
578
294
|
env: input.env,
|
|
579
295
|
detached: input.group,
|
|
580
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
296
|
+
stdio: ["pipe", "pipe", "pipe", "pipe"]
|
|
581
297
|
});
|
|
298
|
+
const control = child.stdio[3];
|
|
299
|
+
if (!(control instanceof Duplex)) {
|
|
300
|
+
abortOwnerLossLaunch(child.pid);
|
|
301
|
+
throw new Error("Owner-loss guard supplied no control pipe");
|
|
302
|
+
}
|
|
303
|
+
attachOwnerLossControl(child, control);
|
|
304
|
+
return child;
|
|
582
305
|
}
|
|
583
306
|
var INHERITED_OUTPUT = {
|
|
584
307
|
destroyed: true,
|
|
@@ -614,12 +337,31 @@ class InheritedChild {
|
|
|
614
337
|
}
|
|
615
338
|
}
|
|
616
339
|
function spawnInheritedCommand(input) {
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
340
|
+
const observable = hostIsBun() ? spawnBunInheritedChild(input) : (() => {
|
|
341
|
+
if (input.ownerLoss === undefined)
|
|
342
|
+
return spawn(input.executable, [...input.args], {
|
|
343
|
+
cwd: input.cwd,
|
|
344
|
+
env: input.env,
|
|
345
|
+
detached: input.group,
|
|
346
|
+
stdio: "inherit"
|
|
347
|
+
});
|
|
348
|
+
const [executable, ...args] = ownerLossGuardCommand(input.ownerLoss, input.executable, input.args);
|
|
349
|
+
const spawned = spawn(executable, args, {
|
|
350
|
+
cwd: input.cwd,
|
|
351
|
+
env: input.env,
|
|
352
|
+
detached: input.group,
|
|
353
|
+
stdio: ["inherit", "inherit", "inherit", "pipe"]
|
|
354
|
+
});
|
|
355
|
+
const control = spawned.stdio[3];
|
|
356
|
+
if (!(control instanceof Duplex)) {
|
|
357
|
+
abortOwnerLossLaunch(spawned.pid);
|
|
358
|
+
throw new Error("Owner-loss guard supplied no control pipe");
|
|
359
|
+
}
|
|
360
|
+
attachOwnerLossControl(spawned, control);
|
|
361
|
+
return spawned;
|
|
362
|
+
})();
|
|
363
|
+
const child = new InheritedChild(observable);
|
|
364
|
+
transferOwnerLossControl(observable, child);
|
|
623
365
|
if (input.group)
|
|
624
366
|
groups.add(child);
|
|
625
367
|
return child;
|
|
@@ -629,7 +371,7 @@ function ownsCommandGroup(child) {
|
|
|
629
371
|
}
|
|
630
372
|
|
|
631
373
|
// src/process/transport.ts
|
|
632
|
-
import { Readable
|
|
374
|
+
import { Readable, Writable } from "node:stream";
|
|
633
375
|
function createCommandTransport(child) {
|
|
634
376
|
const leader = Promise.withResolvers();
|
|
635
377
|
const closed = Promise.withResolvers();
|
|
@@ -686,17 +428,17 @@ function createCommandTransport(child) {
|
|
|
686
428
|
stderr = child.stderr;
|
|
687
429
|
destructors.set("stderr", () => stderr?.destroy());
|
|
688
430
|
const input = "stdin" in child ? child.stdin : undefined;
|
|
689
|
-
if (input instanceof
|
|
431
|
+
if (input instanceof Writable) {
|
|
690
432
|
stdin = input;
|
|
691
433
|
pendingHandles.add("stdin");
|
|
692
434
|
destructors.set("stdin", () => input.destroy());
|
|
693
435
|
pipe("stdin", input);
|
|
694
436
|
}
|
|
695
|
-
if (stdout instanceof
|
|
437
|
+
if (stdout instanceof Readable)
|
|
696
438
|
pipe("stdout", stdout);
|
|
697
|
-
if (stderr instanceof
|
|
439
|
+
if (stderr instanceof Readable)
|
|
698
440
|
pipe("stderr", stderr);
|
|
699
|
-
if (stdout instanceof
|
|
441
|
+
if (stdout instanceof Readable && stderr instanceof Readable)
|
|
700
442
|
release("launcher");
|
|
701
443
|
return { stdout, stderr };
|
|
702
444
|
},
|
|
@@ -714,7 +456,7 @@ function createCommandTransport(child) {
|
|
|
714
456
|
child.stdout.destroy();
|
|
715
457
|
else if (name === "stderr")
|
|
716
458
|
child.stderr.destroy();
|
|
717
|
-
else if ("stdin" in child && child.stdin instanceof
|
|
459
|
+
else if ("stdin" in child && child.stdin instanceof Writable)
|
|
718
460
|
child.stdin.destroy();
|
|
719
461
|
} catch (cause) {
|
|
720
462
|
failures.push(cause);
|
|
@@ -747,7 +489,7 @@ function nativeCommandOwner(child) {
|
|
|
747
489
|
return owners.get(child);
|
|
748
490
|
}
|
|
749
491
|
function outputStream(source, closed, maxBufferedBytes) {
|
|
750
|
-
if (source instanceof
|
|
492
|
+
if (source instanceof Readable2)
|
|
751
493
|
return {
|
|
752
494
|
bytes: source,
|
|
753
495
|
destroy() {
|
|
@@ -830,7 +572,8 @@ function launchNativeCommand({
|
|
|
830
572
|
args: options.args,
|
|
831
573
|
cwd: options.cwd,
|
|
832
574
|
env: options.envPolicy === "ambient" ? { ...process.env, ...options.env } : options.env ?? {},
|
|
833
|
-
group: options.group === "own" && (driver?.group ?? true)
|
|
575
|
+
group: options.group === "own" && (driver?.group ?? true),
|
|
576
|
+
ownerLoss: options.ownerLoss === "terminate" ? { entry: ownerLossGuardEntry(), signalTarget: options.stop.target } : undefined
|
|
834
577
|
};
|
|
835
578
|
let inherited;
|
|
836
579
|
if (!launch) {
|
|
@@ -859,10 +602,12 @@ function launchNativeCommand({
|
|
|
859
602
|
...transport,
|
|
860
603
|
stdout: stdout.bytes,
|
|
861
604
|
stderr: stderr.bytes,
|
|
862
|
-
start() {
|
|
605
|
+
async start() {
|
|
863
606
|
if (native)
|
|
864
607
|
onSpawn?.(native);
|
|
608
|
+
const pid = await awaitCommandStart(child);
|
|
865
609
|
stdin?.end(options.stdin);
|
|
610
|
+
return pid;
|
|
866
611
|
}
|
|
867
612
|
};
|
|
868
613
|
}
|
|
@@ -1021,6 +766,10 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
1021
766
|
killOn?.throwIfAborted();
|
|
1022
767
|
if (process.platform === "win32" && driver?.group !== false)
|
|
1023
768
|
throw new NativeCommandError2("COMMAND_UNAVAILABLE", "POSIX process groups are required");
|
|
769
|
+
if (options.ownerLoss === "terminate" && !ownerLossAvailable(process.platform))
|
|
770
|
+
throw new NativeCommandError2("COMMAND_UNAVAILABLE", "ownerLoss: 'terminate' is available on Linux and Darwin");
|
|
771
|
+
if (options.ownerLoss === "terminate" && (driver?.launch !== undefined || driver?.group === false))
|
|
772
|
+
throw new NativeCommandError2("COMMAND_UNAVAILABLE", "ownerLoss: 'terminate' requires the package-owned process-group launcher");
|
|
1024
773
|
const { cleanupTimeoutMs, timeoutMs } = nativeCommandBudgets(options, driver);
|
|
1025
774
|
const controller = new AbortController;
|
|
1026
775
|
const abort = () => controller.abort(options.signal?.reason);
|
|
@@ -1126,8 +875,7 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
1126
875
|
};
|
|
1127
876
|
const run = (async () => {
|
|
1128
877
|
try {
|
|
1129
|
-
launched.start();
|
|
1130
|
-
const pid = acquired?.child.pid;
|
|
878
|
+
const pid = await raceAbort(launched.start(), controller.signal);
|
|
1131
879
|
if (pid !== undefined)
|
|
1132
880
|
options.onLeaderStarted?.({ pid });
|
|
1133
881
|
const drains = Promise.all([
|
|
@@ -1166,4 +914,4 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
1166
914
|
return owner;
|
|
1167
915
|
}
|
|
1168
916
|
|
|
1169
|
-
export {
|
|
917
|
+
export { CommandStopped, spawnOwnedCommand, nativeCommandOwner, terminateOwnedGroup, startNativeCommand };
|