stitchkit 0.105.0 → 0.105.2
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 +44 -0
- package/dist/entrypoints/agent-runtime/coding-tools.js +3 -3
- package/dist/entrypoints/agent-runtime/sandbox.js +3 -3
- package/dist/entrypoints/application/directory-inbox.js +3 -3
- package/dist/entrypoints/cli/publish.js +4 -4
- package/dist/entrypoints/cli.js +3 -3
- package/dist/entrypoints/files/packaging.js +10 -4
- package/dist/entrypoints/files.js +3 -3
- package/dist/entrypoints/process.js +1 -1
- package/dist/entrypoints/server.js +3 -3
- package/dist/entrypoints/telegram.js +2 -2
- package/dist/entrypoints/tools.js +3 -3
- package/dist/files/native-packaging.d.ts.map +1 -1
- package/dist/{index-2rc6jd9n.js → index-1ptj98e0.js} +1 -1
- package/dist/{index-g2vfg7ak.js → index-3s86a7kf.js} +39 -11
- package/dist/{index-ef8gdaq0.js → index-cazts87s.js} +1 -1
- package/dist/{index-szybbezc.js → index-drs41arz.js} +1 -1
- package/dist/{index-cq59agna.js → index-j4zh647a.js} +9 -1
- package/dist/{index-ma963wz7.js → index-sevbwsnn.js} +2 -2
- package/dist/{index-wqmvtnkp.js → index-w8etpm8j.js} +1 -1
- package/dist/internal/atomic-staging.d.ts +2 -1
- package/dist/internal/atomic-staging.d.ts.map +1 -1
- package/dist/process/cleanup.d.ts +1 -1
- package/dist/process/cleanup.d.ts.map +1 -1
- package/dist/process/command-lifetime.d.ts +2 -1
- package/dist/process/command-lifetime.d.ts.map +1 -1
- package/dist/process/command-owner.d.ts +1 -0
- package/dist/process/command-owner.d.ts.map +1 -1
- package/dist/process/contract.d.ts +9 -2
- package/dist/process/contract.d.ts.map +1 -1
- package/dist/process/group.d.ts +4 -2
- package/dist/process/group.d.ts.map +1 -1
- package/dist/process/terminate.d.ts +3 -2
- package/dist/process/terminate.d.ts.map +1 -1
- package/llms/files-packaging.txt +2 -0
- package/llms/files.txt +1 -1
- package/llms/process.txt +49 -11
- package/llms-full.txt +52 -12
- package/llms.txt +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,50 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.105.2] - 2026-10-06
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `stitchkit/process` — `runNativeCommand` takes `onLeaderStarted({ pid })`, called once right
|
|
23
|
+
after the leader exists (a throw stops the command with that error), and `drainTimeoutMs`,
|
|
24
|
+
which bounds how long the output pipes may stay open after the leader exited: a holder that
|
|
25
|
+
left the group with `setsid` otherwise keeps the command waiting until `timeoutMs` or the
|
|
26
|
+
signal, and with the bound it ends with `COMMAND_CLEANUP`.
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
- `stitchkit/process` — the native IO guide no longer says that `cleanupTimeoutMs` bounds the drain
|
|
31
|
+
after the leader exited; it bounds the wait after a stop, and `drainTimeoutMs` bounds the drain.
|
|
32
|
+
|
|
33
|
+
## [0.105.1] - 2026-10-06
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `stitchkit/process` — `NativeCommandResult.descendantsStopped` reports that the group still had
|
|
38
|
+
members after the leader exited and that `descendants: 'terminate-after-leader'` stopped them,
|
|
39
|
+
so a helper ended this way is visible in the result instead of only missing afterwards.
|
|
40
|
+
- `stitchkit/files/packaging` — `assetPath` accepts `[hash]`, replaced by the first 16 hex digits
|
|
41
|
+
of the addon's published SHA256, so two installed versions get different companion names
|
|
42
|
+
(`addons/darwin-arm64-[hash].node`); the loader requires exactly that name.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- `stitchkit/files` — `sweepAtomicStaging` returns `[]` for a `directory` that does not exist yet
|
|
47
|
+
instead of throwing `ENOENT`; any other listing failure still throws.
|
|
48
|
+
- `stitchkit/process` — an option `runNativeCommand` does not know is refused by name, and the
|
|
49
|
+
removed `killGraceMs` names its replacement, `stop: { target: 'group', graceMs }`: a spread
|
|
50
|
+
object can carry a key the type never checked.
|
|
51
|
+
|
|
52
|
+
### Documentation
|
|
53
|
+
|
|
54
|
+
- `stitchkit/process` — the 0.105.0 note that a descendant leaving the group with `setsid` is
|
|
55
|
+
unaffected holds only when it has left before the leader exits: `setsid helper &` and
|
|
56
|
+
`nohup helper &` race the leader and are stopped, `setsid -f helper` survives. The native IO
|
|
57
|
+
guide shows the measured forms, how a guardian or worker that holds a child until it exits
|
|
58
|
+
maps onto `runNativeCommand`, and why a child inheriting the terminal stays a plain spawn.
|
|
59
|
+
- `stitchkit/files/packaging` — calling `createNativePackaging` has to stay in the build step;
|
|
60
|
+
importing the leaf from a module that is also bundled is side-effect free.
|
|
61
|
+
|
|
18
62
|
## [0.105.0] - 2026-10-05
|
|
19
63
|
|
|
20
64
|
### ⚠️ Breaking changes
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
AgentCodingToolAuthorizationSchema2,
|
|
8
8
|
AgentCodingToolPathAuthorizationSchema2,
|
|
9
9
|
createAgentCodingTools2
|
|
10
|
-
} from "../../index-
|
|
10
|
+
} from "../../index-sevbwsnn.js";
|
|
11
11
|
import"../../index-v5ee8ath.js";
|
|
12
|
-
import"../../index-
|
|
13
|
-
import"../../index-
|
|
12
|
+
import"../../index-j4zh647a.js";
|
|
13
|
+
import"../../index-3s86a7kf.js";
|
|
14
14
|
import"../../index-8ks9b85e.js";
|
|
15
15
|
import"../../index-gxm9rc97.js";
|
|
16
16
|
import"../../index-yvfk3y36.js";
|
|
@@ -9,9 +9,9 @@ import"../../index-r393tnfp.js";
|
|
|
9
9
|
import"../../index-rpzewktb.js";
|
|
10
10
|
import {
|
|
11
11
|
createAgentCodingTools2
|
|
12
|
-
} from "../../index-
|
|
12
|
+
} from "../../index-sevbwsnn.js";
|
|
13
13
|
import"../../index-v5ee8ath.js";
|
|
14
|
-
import"../../index-
|
|
14
|
+
import"../../index-j4zh647a.js";
|
|
15
15
|
import {
|
|
16
16
|
COMMAND_CLEANUP_TIMEOUT_MS,
|
|
17
17
|
NativeCommandError2,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
nativeCommandOwner,
|
|
20
20
|
terminateOwnedGroup,
|
|
21
21
|
startNativeCommand
|
|
22
|
-
} from "../../index-
|
|
22
|
+
} from "../../index-3s86a7kf.js";
|
|
23
23
|
import"../../index-gxm9rc97.js";
|
|
24
24
|
import {
|
|
25
25
|
MAX_TIMER_MS
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
renewEntry,
|
|
11
11
|
forgetMissing
|
|
12
12
|
} from "../../index-msfpyy2f.js";
|
|
13
|
-
import"../../index-
|
|
13
|
+
import"../../index-j4zh647a.js";
|
|
14
14
|
import {
|
|
15
15
|
backoffDelay
|
|
16
16
|
} from "../../index-g6a3na27.js";
|
|
@@ -19,7 +19,7 @@ import"../../index-azx12jgv.js";
|
|
|
19
19
|
import"../../index-v5ee8ath.js";
|
|
20
20
|
import {
|
|
21
21
|
createFileStateStore2
|
|
22
|
-
} from "../../index-
|
|
22
|
+
} from "../../index-1ptj98e0.js";
|
|
23
23
|
import {
|
|
24
24
|
defineManagedResource2
|
|
25
25
|
} from "../../index-50ejfz75.js";
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
} from "../../index-m616f0yz.js";
|
|
38
38
|
import {
|
|
39
39
|
writeAtomicFileData
|
|
40
|
-
} from "../../index-
|
|
40
|
+
} from "../../index-cazts87s.js";
|
|
41
41
|
import {
|
|
42
42
|
closeAfter,
|
|
43
43
|
publishAtomicFile
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import"../../index-xqhede6x.js";
|
|
2
|
-
import"../../index-
|
|
2
|
+
import"../../index-j4zh647a.js";
|
|
3
3
|
import {
|
|
4
4
|
ManagedFileError2,
|
|
5
5
|
createManagedFileBoundary2
|
|
6
|
-
} from "../../index-
|
|
6
|
+
} from "../../index-w8etpm8j.js";
|
|
7
7
|
import"../../index-dsnjzb62.js";
|
|
8
8
|
import"../../index-azx12jgv.js";
|
|
9
9
|
import"../../index-v5ee8ath.js";
|
|
@@ -20,13 +20,13 @@ import {
|
|
|
20
20
|
gzipCliAsset,
|
|
21
21
|
decodeCliAsset,
|
|
22
22
|
compareCliVersions2
|
|
23
|
-
} from "../../index-
|
|
23
|
+
} from "../../index-drs41arz.js";
|
|
24
24
|
import {
|
|
25
25
|
withExclusiveLock2
|
|
26
26
|
} from "../../index-4vsby8n8.js";
|
|
27
27
|
import {
|
|
28
28
|
writeFileAtomic2
|
|
29
|
-
} from "../../index-
|
|
29
|
+
} from "../../index-cazts87s.js";
|
|
30
30
|
import {
|
|
31
31
|
AtomicFilePublicationError2,
|
|
32
32
|
assertDirectoryDurability,
|
package/dist/entrypoints/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
pollUntilDone2,
|
|
17
17
|
createCli2
|
|
18
18
|
} from "../index-ksfagmyz.js";
|
|
19
|
-
import"../index-
|
|
19
|
+
import"../index-j4zh647a.js";
|
|
20
20
|
import {
|
|
21
21
|
AppError2
|
|
22
22
|
} from "../index-j9gxbztg.js";
|
|
@@ -45,10 +45,10 @@ import {
|
|
|
45
45
|
checkCliUpdate2,
|
|
46
46
|
applyCliUpdate2,
|
|
47
47
|
rollbackCliUpdate2
|
|
48
|
-
} from "../index-
|
|
48
|
+
} from "../index-drs41arz.js";
|
|
49
49
|
import {
|
|
50
50
|
writeFileAtomicSync2
|
|
51
|
-
} from "../index-
|
|
51
|
+
} from "../index-cazts87s.js";
|
|
52
52
|
import"../index-pz3y6b6g.js";
|
|
53
53
|
import"../index-yvfk3y36.js";
|
|
54
54
|
import {
|
|
@@ -65,17 +65,20 @@ var CommonOptions = {
|
|
|
65
65
|
platform: PlatformSchema,
|
|
66
66
|
entryPath: RelativePathSchema.refine((value) => !value.includes("[") && !value.includes("]"), "Expected a fixed entry path without Bun naming templates")
|
|
67
67
|
};
|
|
68
|
+
var ASSET_HASH_TOKEN = "[hash]";
|
|
69
|
+
var ASSET_HASH_LENGTH = 16;
|
|
70
|
+
var AssetPathSchema = RelativePathSchema.refine((value) => !/[[\]]/.test(value.replaceAll(ASSET_HASH_TOKEN, "")), "Expected an addon path whose only template is [hash]");
|
|
68
71
|
var SingleOptionsSchema = z2.object({
|
|
69
72
|
...CommonOptions,
|
|
70
73
|
architecture: z2.string().min(1),
|
|
71
74
|
delivery: z2.enum(["companion", "embedded"]),
|
|
72
|
-
assetPath:
|
|
75
|
+
assetPath: AssetPathSchema
|
|
73
76
|
});
|
|
74
77
|
var MultipleOptionsSchema = z2.object({
|
|
75
78
|
...CommonOptions,
|
|
76
79
|
architecture: z2.array(z2.string().min(1)).refine((targets) => targets.length > 0),
|
|
77
80
|
delivery: z2.literal("companion"),
|
|
78
|
-
assetPath: z2.record(z2.string().min(1),
|
|
81
|
+
assetPath: z2.record(z2.string().min(1), AssetPathSchema)
|
|
79
82
|
});
|
|
80
83
|
var OptionsSchema = z2.union([SingleOptionsSchema, MultipleOptionsSchema]).check((ctx) => {
|
|
81
84
|
const input = ctx.value;
|
|
@@ -220,13 +223,16 @@ function createNativePackaging(options) {
|
|
|
220
223
|
const specifiers = {};
|
|
221
224
|
const embedded = new Map;
|
|
222
225
|
for (const architecture of architectures) {
|
|
223
|
-
const
|
|
224
|
-
if (!
|
|
226
|
+
const template = typeof input.assetPath === "string" ? input.assetPath : input.assetPath[architecture];
|
|
227
|
+
if (!template)
|
|
225
228
|
throw new Error("Declared architecture has no output path");
|
|
226
229
|
const verified = readVerifiedAsset(root, published.assets[architecture], input.platform, architecture);
|
|
227
230
|
if ("refusal" in verified)
|
|
228
231
|
return verified.refusal;
|
|
229
232
|
const { sourcePath, bytes, size, sha256 } = verified;
|
|
233
|
+
const outputPath = template.replaceAll(ASSET_HASH_TOKEN, sha256.slice(0, ASSET_HASH_LENGTH));
|
|
234
|
+
if (assets.some((asset) => asset.outputPath === outputPath))
|
|
235
|
+
throw new Error("Addons must have distinct output paths");
|
|
230
236
|
if (input.delivery === "embedded")
|
|
231
237
|
embedded.set(sourcePath, new Uint8Array(bytes));
|
|
232
238
|
const outputSpecifier = relative(dirname(input.entryPath), outputPath).split("\\").join("/");
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
stagingPath,
|
|
7
7
|
isAtomicStagingName2,
|
|
8
8
|
sweepAtomicStaging2
|
|
9
|
-
} from "../index-
|
|
9
|
+
} from "../index-j4zh647a.js";
|
|
10
10
|
import {
|
|
11
11
|
ManagedFileError2,
|
|
12
12
|
createManagedFileBoundary2
|
|
13
|
-
} from "../index-
|
|
13
|
+
} from "../index-w8etpm8j.js";
|
|
14
14
|
import {
|
|
15
15
|
AppError2
|
|
16
16
|
} from "../index-j9gxbztg.js";
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
writeFileAtomic2,
|
|
26
26
|
writeFileAtomicSync2
|
|
27
|
-
} from "../index-
|
|
27
|
+
} from "../index-cazts87s.js";
|
|
28
28
|
import {
|
|
29
29
|
AtomicFilePublicationError2
|
|
30
30
|
} from "../index-b56nnscm.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import"../index-
|
|
1
|
+
import"../index-j4zh647a.js";
|
|
2
2
|
import"../index-dsnjzb62.js";
|
|
3
3
|
import"../index-azx12jgv.js";
|
|
4
4
|
import"../index-v5ee8ath.js";
|
|
5
5
|
import {
|
|
6
6
|
createFileStateStore2
|
|
7
|
-
} from "../index-
|
|
7
|
+
} from "../index-1ptj98e0.js";
|
|
8
8
|
import {
|
|
9
9
|
AppError2,
|
|
10
10
|
notFound2,
|
|
@@ -96,7 +96,7 @@ import {
|
|
|
96
96
|
isWithinDir2,
|
|
97
97
|
realPathWithinDir
|
|
98
98
|
} from "../index-5cc6r990.js";
|
|
99
|
-
import"../index-
|
|
99
|
+
import"../index-cazts87s.js";
|
|
100
100
|
import {
|
|
101
101
|
DEFAULT_CORS_ALLOW_HEADERS2,
|
|
102
102
|
DEFAULT_CORS_EXPOSE_HEADERS2,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TELEGRAM_SEND_FAILURE_REASONS2,
|
|
9
9
|
classifyTelegramSendFailure2
|
|
10
10
|
} from "../index-e05cjgtf.js";
|
|
11
|
-
import"../index-
|
|
11
|
+
import"../index-j4zh647a.js";
|
|
12
12
|
import {
|
|
13
13
|
attemptExclusiveLock
|
|
14
14
|
} from "../index-dsnjzb62.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "../index-b0kddvzw.js";
|
|
24
24
|
import {
|
|
25
25
|
writeFileAtomic2
|
|
26
|
-
} from "../index-
|
|
26
|
+
} from "../index-cazts87s.js";
|
|
27
27
|
import {
|
|
28
28
|
MAX_TIMER_MS,
|
|
29
29
|
sleep
|
|
@@ -85,10 +85,10 @@ import {
|
|
|
85
85
|
ManagedFilePathSchema2,
|
|
86
86
|
ManagedFileRefSchema2
|
|
87
87
|
} from "../index-xqhede6x.js";
|
|
88
|
-
import"../index-
|
|
88
|
+
import"../index-j4zh647a.js";
|
|
89
89
|
import {
|
|
90
90
|
ManagedFileError2
|
|
91
|
-
} from "../index-
|
|
91
|
+
} from "../index-w8etpm8j.js";
|
|
92
92
|
import {
|
|
93
93
|
fetchGuarded,
|
|
94
94
|
fetchPinnedDocument,
|
|
@@ -102,7 +102,7 @@ import {
|
|
|
102
102
|
contractOnlyService,
|
|
103
103
|
mediaTypeEssence
|
|
104
104
|
} from "../index-cmzvhhtd.js";
|
|
105
|
-
import"../index-
|
|
105
|
+
import"../index-cazts87s.js";
|
|
106
106
|
import {
|
|
107
107
|
collectToolSurface
|
|
108
108
|
} from "../index-pz3y6b6g.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-packaging.d.ts","sourceRoot":"","sources":["../../src/files/native-packaging.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,QAAA,MAAM,kBAAkB;;;EAA2B,CAAC;AACpD,mGAAmG;AACnG,QAAA,MAAM,cAAc;;EAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"native-packaging.d.ts","sourceRoot":"","sources":["../../src/files/native-packaging.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,QAAA,MAAM,kBAAkB;;;EAA2B,CAAC;AACpD,mGAAmG;AACnG,QAAA,MAAM,cAAc;;EAAqB,CAAC;AAe1C,QAAA,MAAM,mBAAmB;;;;;;;;;;;iBAKvB,CAAC;AACH,QAAA,MAAM,qBAAqB;;;;;;;;iBAOzB,CAAC;AAsCH,QAAA,MAAM,WAAW;;;;;iBAKf,CAAC;AACH,QAAA,MAAM,iBAAiB;;;;;;;;;iBAA2D,CAAC;AAKnF,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAuBjB,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,OAAO,GAAG,KAAK,IAAI,QAAQ,SAAS,IAAI,GACxF,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GACrC,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACxC;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,OAAO,GAAG,KAAK,IAAI,QAAQ,SAAS,IAAI,GACtF,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GACjC,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhC;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,KAAK,EAAE;QACb,MAAM,EAAE;YACN,SAAS,CAAC,EAAE,OAAO,CAAC;YACpB,MAAM,CAAC,EAAE,MAAM,GAAG;gBAAE,KAAK,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YACrC,WAAW,EAAE,MAAM,EAAE,CAAC;SACvB,CAAC;QACF,MAAM,EAAE,CACN,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,EAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE;YACf,IAAI,EAAE,MAAM,CAAC;SACd,KACG;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,IAAI,CAAA;SAAE,GAClC;YAAE,QAAQ,EAAE,UAAU,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GACxC,SAAS,KACV,OAAO,CAAC;QACb,SAAS,EAAE,CACT,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,EAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE;YACf,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,EAAE,MAAM,CAAC;SAClB,KAAK;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,GAAG,SAAS,KAClD,OAAO,CAAC;KACd,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,CAAC,QAAQ,SAAS,OAAO,GAAG,KAAK,IAC9D,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAC7B;IACE,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;IACzC,YAAY,EAAE,QAAQ,SAAS,IAAI,GAC/B,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,EAAE,GACpC,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;IACzC,MAAM,EAAE,qBAAqB,CAAC;CAC/B,CAAC;AAsFN;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAAC;AAC9F,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,CAAC,IAAI,CAAC,GACpC,qBAAqB,CAAC,IAAI,CAAC,CAAC;AAC/B,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC,GACvC,qBAAqB,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -27,6 +27,16 @@ var NativeCommandStopPolicySchema = z.strictObject({
|
|
|
27
27
|
graceMs: z.number().int().nonnegative().max(MAX_COMMAND_STOP_GRACE_MS),
|
|
28
28
|
killOn: z.custom((v) => v instanceof AbortSignal).optional()
|
|
29
29
|
});
|
|
30
|
+
var REMOVED_NATIVE_COMMAND_OPTIONS = {
|
|
31
|
+
killGraceMs: "stop: { target: 'group', graceMs } (removed in 0.105.0)"
|
|
32
|
+
};
|
|
33
|
+
function unknownOptionMessage(keys) {
|
|
34
|
+
return keys.map((key) => {
|
|
35
|
+
const name = String(key);
|
|
36
|
+
const replacement = Object.hasOwn(REMOVED_NATIVE_COMMAND_OPTIONS, name) ? REMOVED_NATIVE_COMMAND_OPTIONS[name] : undefined;
|
|
37
|
+
return replacement === undefined ? `Unknown runNativeCommand option ${name}` : `Unknown runNativeCommand option ${name}; use ${replacement}`;
|
|
38
|
+
}).join("; ");
|
|
39
|
+
}
|
|
30
40
|
var NativeCommandOptionsSchema = z.strictObject({
|
|
31
41
|
executable: z.string().min(1),
|
|
32
42
|
args: z.array(z.string()).default([]),
|
|
@@ -41,6 +51,8 @@ var NativeCommandOptionsSchema = z.strictObject({
|
|
|
41
51
|
maxStdinBytes: z.number().int().positive().default(1024 * 1024),
|
|
42
52
|
onOutput: z.custom((v) => typeof v === "function").optional(),
|
|
43
53
|
onLeaderSettled: z.custom((v) => typeof v === "function").optional(),
|
|
54
|
+
onLeaderStarted: z.custom((v) => typeof v === "function").optional(),
|
|
55
|
+
drainTimeoutMs: z.number().int().positive().max(MAX_TIMER_MS).optional(),
|
|
44
56
|
stop: NativeCommandStopPolicySchema.default({
|
|
45
57
|
target: "group",
|
|
46
58
|
signal: "SIGTERM",
|
|
@@ -48,6 +60,8 @@ var NativeCommandOptionsSchema = z.strictObject({
|
|
|
48
60
|
}),
|
|
49
61
|
descendants: z.enum(["terminate-after-leader", "leave"]).default("terminate-after-leader"),
|
|
50
62
|
cleanupTimeoutMs: z.number().int().positive().max(30000).default(COMMAND_CLEANUP_TIMEOUT_MS)
|
|
63
|
+
}, {
|
|
64
|
+
error: (issue) => issue.code === "unrecognized_keys" ? unknownOptionMessage(issue.keys) : undefined
|
|
51
65
|
}).check((ctx) => {
|
|
52
66
|
const input = ctx.value;
|
|
53
67
|
if (!input.signal && input.timeoutMs === undefined)
|
|
@@ -72,6 +86,7 @@ var NativeCommandOptionsSchema = z.strictObject({
|
|
|
72
86
|
var NativeCommandResultSchema = z.object({
|
|
73
87
|
exitCode: z.number().int().nullable(),
|
|
74
88
|
signal: z.string().nullable(),
|
|
89
|
+
descendantsStopped: z.boolean(),
|
|
75
90
|
stdout: z.instanceof(Uint8Array),
|
|
76
91
|
stderr: z.instanceof(Uint8Array)
|
|
77
92
|
});
|
|
@@ -158,12 +173,10 @@ async function signalUntilSettled(pid, signal, deadline) {
|
|
|
158
173
|
async function stopCommandGroup(stop) {
|
|
159
174
|
const { pid, policy } = stop;
|
|
160
175
|
const deadline = performance.now() + policy.graceMs + stop.cleanupTimeoutMs;
|
|
161
|
-
if (stop.force || policy.killOn?.aborted)
|
|
162
|
-
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
176
|
+
if (stop.force || policy.killOn?.aborted)
|
|
177
|
+
return signalUntilSettled(pid, "SIGKILL", deadline);
|
|
165
178
|
if (!await signalUntilSettled(pid, policy.signal, deadline))
|
|
166
|
-
return;
|
|
179
|
+
return false;
|
|
167
180
|
const until = performance.now() + policy.graceMs;
|
|
168
181
|
let interval = 10;
|
|
169
182
|
let present = exists(pid);
|
|
@@ -176,14 +189,17 @@ async function stopCommandGroup(stop) {
|
|
|
176
189
|
}
|
|
177
190
|
if (present)
|
|
178
191
|
await signalUntilSettled(pid, "SIGKILL", deadline);
|
|
192
|
+
return true;
|
|
179
193
|
}
|
|
180
194
|
async function stopCommandLeader(stop) {
|
|
181
195
|
const { pid, policy, leader } = stop;
|
|
182
196
|
const deadline = performance.now() + policy.graceMs + stop.cleanupTimeoutMs;
|
|
183
197
|
const running = () => leader.exitCode === null && leader.signalCode === null;
|
|
198
|
+
let signalled = false;
|
|
184
199
|
if (!stop.force && !policy.killOn?.aborted && pid !== undefined && running()) {
|
|
185
200
|
try {
|
|
186
201
|
process.kill(pid, policy.signal);
|
|
202
|
+
signalled = true;
|
|
187
203
|
} catch (error) {
|
|
188
204
|
if (!isErrno(error, "ESRCH"))
|
|
189
205
|
throw error;
|
|
@@ -198,7 +214,7 @@ async function stopCommandLeader(stop) {
|
|
|
198
214
|
policy.killOn?.removeEventListener("abort", end);
|
|
199
215
|
grace.abort();
|
|
200
216
|
}
|
|
201
|
-
await signalUntilSettled(pid, "SIGKILL", deadline);
|
|
217
|
+
return await signalUntilSettled(pid, "SIGKILL", deadline) || signalled;
|
|
202
218
|
}
|
|
203
219
|
function commandCleanupError(cause) {
|
|
204
220
|
return new NativeCommandError2("COMMAND_CLEANUP", "Command cleanup did not complete", {
|
|
@@ -616,8 +632,11 @@ function signalCommandChild(child, input) {
|
|
|
616
632
|
return input.policy.target === "leader" ? stopCommandLeader({ ...stop, leader: child, leaderExit: input.leaderExit }) : stopCommandGroup(stop);
|
|
617
633
|
}
|
|
618
634
|
return Promise.resolve().then(() => {
|
|
619
|
-
if (child.exitCode
|
|
635
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
636
|
+
return false;
|
|
637
|
+
if (!child.kill("SIGKILL"))
|
|
620
638
|
throw new Error("Command leader termination was refused");
|
|
639
|
+
return true;
|
|
621
640
|
});
|
|
622
641
|
}
|
|
623
642
|
async function terminateOwnedGroup(child, closed, cleanupTimeoutMs) {
|
|
@@ -677,7 +696,7 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
677
696
|
});
|
|
678
697
|
const signalOwned = (force = driver?.force) => {
|
|
679
698
|
if (!acquired)
|
|
680
|
-
return Promise.resolve();
|
|
699
|
+
return Promise.resolve(false);
|
|
681
700
|
return signalCommandChild(acquired.child, {
|
|
682
701
|
group: driver?.group !== false,
|
|
683
702
|
policy: options.stop,
|
|
@@ -731,7 +750,7 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
731
750
|
const stderr = [];
|
|
732
751
|
let total = 0;
|
|
733
752
|
const { stdout: output, stderr: errorOutput, leader, closed } = launched;
|
|
734
|
-
const cancelCloseDeadline = createLeaderCloseDeadline(leader, driver?.closeTimeoutMs, controller);
|
|
753
|
+
const cancelCloseDeadline = createLeaderCloseDeadline(leader, driver?.closeTimeoutMs ?? options.drainTimeoutMs, controller);
|
|
735
754
|
const drain = async (stream, channel, captured) => {
|
|
736
755
|
for await (const chunk of stream) {
|
|
737
756
|
if (controller.signal.aborted)
|
|
@@ -757,6 +776,7 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
757
776
|
}
|
|
758
777
|
}
|
|
759
778
|
};
|
|
779
|
+
let descendantsStopped = false;
|
|
760
780
|
const settleLeader = async (event) => {
|
|
761
781
|
await settle(event);
|
|
762
782
|
if (controller.signal.aborted)
|
|
@@ -764,7 +784,7 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
764
784
|
if (options.descendants !== "terminate-after-leader" || event.kind !== "exit")
|
|
765
785
|
return;
|
|
766
786
|
try {
|
|
767
|
-
await signalOwned();
|
|
787
|
+
descendantsStopped = await signalOwned();
|
|
768
788
|
} catch (cause) {
|
|
769
789
|
throw commandCleanupError(cause);
|
|
770
790
|
}
|
|
@@ -772,6 +792,9 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
772
792
|
const run = (async () => {
|
|
773
793
|
try {
|
|
774
794
|
launched.start();
|
|
795
|
+
const pid = acquired?.child.pid;
|
|
796
|
+
if (pid !== undefined)
|
|
797
|
+
options.onLeaderStarted?.({ pid });
|
|
775
798
|
const drains = Promise.all([
|
|
776
799
|
drain(output, "stdout", stdout),
|
|
777
800
|
drain(errorOutput, "stderr", stderr)
|
|
@@ -780,7 +803,12 @@ function startNativeCommand(input, onSpawn, driver) {
|
|
|
780
803
|
const status = await raceAbort(closed, controller.signal);
|
|
781
804
|
if (spawnFailure !== undefined)
|
|
782
805
|
throw spawnFailure;
|
|
783
|
-
return {
|
|
806
|
+
return {
|
|
807
|
+
...status,
|
|
808
|
+
descendantsStopped,
|
|
809
|
+
stdout: Buffer.concat(stdout),
|
|
810
|
+
stderr: Buffer.concat(stderr)
|
|
811
|
+
};
|
|
784
812
|
} catch (error) {
|
|
785
813
|
controller.abort(error);
|
|
786
814
|
await cleanupNativeCommand({
|
|
@@ -29,7 +29,15 @@ async function sweepAtomicStaging2(options) {
|
|
|
29
29
|
signal?.throwIfAborted();
|
|
30
30
|
const cutoff = Date.now() - olderThanMs;
|
|
31
31
|
const removed = [];
|
|
32
|
-
|
|
32
|
+
let names;
|
|
33
|
+
try {
|
|
34
|
+
names = await readdir(directory);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (isMissing(error))
|
|
37
|
+
return removed;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
for (const name of names) {
|
|
33
41
|
signal?.throwIfAborted();
|
|
34
42
|
if (!isAtomicStagingName2(name))
|
|
35
43
|
continue;
|
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
} from "./index-v5ee8ath.js";
|
|
8
8
|
import {
|
|
9
9
|
stagingName
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-j4zh647a.js";
|
|
11
11
|
import {
|
|
12
12
|
NativeCommandError2,
|
|
13
13
|
CommandStopped,
|
|
14
14
|
startNativeCommand
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-3s86a7kf.js";
|
|
16
16
|
import {
|
|
17
17
|
probeAgentProcessSandboxOutcome,
|
|
18
18
|
missingSandboxRestrictions2,
|
|
@@ -40,7 +40,8 @@ export interface SweepAtomicStagingOptions {
|
|
|
40
40
|
* Remove the abandoned staging files of atomic writes from one directory and return their
|
|
41
41
|
* names. Only regular files matching {@link isAtomicStagingName} and older than `olderThanMs`
|
|
42
42
|
* are removed; a symlink, a directory or any other entry is left alone, and nothing is
|
|
43
|
-
* followed or recursed into.
|
|
43
|
+
* followed or recursed into. A `directory` that does not exist holds nothing to sweep and
|
|
44
|
+
* yields `[]`; any other listing failure (a file in its place, a denied read) throws.
|
|
44
45
|
*/
|
|
45
46
|
export declare function sweepAtomicStaging(options: SweepAtomicStagingOptions): Promise<string[]>;
|
|
46
47
|
//# sourceMappingURL=atomic-staging.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-staging.d.ts","sourceRoot":"","sources":["../../src/internal/atomic-staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAaH,mGAAmG;AACnG,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,6CAA6C;AAC7C,MAAM,WAAW,yBAAyB;IACxC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAQD
|
|
1
|
+
{"version":3,"file":"atomic-staging.d.ts","sourceRoot":"","sources":["../../src/internal/atomic-staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAaH,mGAAmG;AACnG,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,6CAA6C;AAC7C,MAAM,WAAW,yBAAyB;IACxC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAQD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,MAAM,EAAE,CAAC,CA6BnB"}
|
|
@@ -11,7 +11,7 @@ export declare function cleanupNativeCommand(input: {
|
|
|
11
11
|
transport?: NativeCommandTransport;
|
|
12
12
|
settle: (event: NativeCommandSettlement) => Promise<void>;
|
|
13
13
|
event?: NativeCommandSettlement;
|
|
14
|
-
signal: () => Promise<
|
|
14
|
+
signal: () => Promise<unknown>;
|
|
15
15
|
timeoutMs: number;
|
|
16
16
|
}): Promise<void>;
|
|
17
17
|
//# sourceMappingURL=cleanup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/process/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,MAAM,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/process/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,MAAM,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB"}
|
|
@@ -8,12 +8,13 @@ export declare function createCommandLifetime(input: {
|
|
|
8
8
|
result: Promise<NativeCommandResult>;
|
|
9
9
|
controller: AbortController;
|
|
10
10
|
finished: () => boolean;
|
|
11
|
-
signal: () => Promise<
|
|
11
|
+
signal: () => Promise<unknown>;
|
|
12
12
|
cleanupTimeoutMs: number;
|
|
13
13
|
}): {
|
|
14
14
|
result: Promise<{
|
|
15
15
|
exitCode: number | null;
|
|
16
16
|
signal: string | null;
|
|
17
|
+
descendantsStopped: boolean;
|
|
17
18
|
stdout: Uint8Array<ArrayBuffer>;
|
|
18
19
|
stderr: Uint8Array<ArrayBuffer>;
|
|
19
20
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-lifetime.d.ts","sourceRoot":"","sources":["../../src/process/command-lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAG1E,gFAAgF;AAChF,qBAAa,cAAe,SAAQ,YAAY;IAC9C,cAEC;CACF;AAED,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACrC,UAAU,EAAE,eAAe,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"command-lifetime.d.ts","sourceRoot":"","sources":["../../src/process/command-lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAG1E,gFAAgF;AAChF,qBAAa,cAAe,SAAQ,YAAY;IAC9C,cAEC;CACF;AAED,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACrC,UAAU,EAAE,eAAe,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;IAKG,MAAM;;;;;;;IACA,SAAS;IAoBT,IAAI;EAKb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-owner.d.ts","sourceRoot":"","sources":["../../src/process/command-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAIzE,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,KAAK,yBAAyB,EAG/B,MAAM,UAAU,CAAC;AAMlB,wFAAwF;AACxF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,IAAI,EACzD,MAAM,CAAC,EAAE,yBAAyB
|
|
1
|
+
{"version":3,"file":"command-owner.d.ts","sourceRoot":"","sources":["../../src/process/command-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAIzE,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,KAAK,yBAAyB,EAG/B,MAAM,UAAU,CAAC;AAMlB,wFAAwF;AACxF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,IAAI,EACzD,MAAM,CAAC,EAAE,yBAAyB;;;;;;;;;;EAiMnC"}
|
|
@@ -58,6 +58,12 @@ export declare const NativeCommandOptionsSchema: z.ZodObject<{
|
|
|
58
58
|
maxStdinBytes: z.ZodDefault<z.ZodNumber>;
|
|
59
59
|
onOutput: z.ZodOptional<z.ZodCustom<(bytes: Uint8Array, channel: 'stdout' | 'stderr', signal: AbortSignal) => void | Promise<void>, (bytes: Uint8Array, channel: 'stdout' | 'stderr', signal: AbortSignal) => void | Promise<void>>>;
|
|
60
60
|
onLeaderSettled: z.ZodOptional<z.ZodCustom<(event: NativeCommandSettlement, signal: AbortSignal) => void | Promise<void>, (event: NativeCommandSettlement, signal: AbortSignal) => void | Promise<void>>>;
|
|
61
|
+
onLeaderStarted: z.ZodOptional<z.ZodCustom<(event: {
|
|
62
|
+
pid: number;
|
|
63
|
+
}) => void, (event: {
|
|
64
|
+
pid: number;
|
|
65
|
+
}) => void>>;
|
|
66
|
+
drainTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
61
67
|
stop: z.ZodDefault<z.ZodObject<{
|
|
62
68
|
target: z.ZodEnum<{
|
|
63
69
|
group: "group";
|
|
@@ -89,12 +95,13 @@ export type ParsedNativeCommandOptions = z.output<typeof NativeCommandOptionsSch
|
|
|
89
95
|
declare const NativeCommandResultSchema: z.ZodObject<{
|
|
90
96
|
exitCode: z.ZodNullable<z.ZodNumber>;
|
|
91
97
|
signal: z.ZodNullable<z.ZodString>;
|
|
98
|
+
descendantsStopped: z.ZodBoolean;
|
|
92
99
|
stdout: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
|
|
93
100
|
stderr: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
|
|
94
101
|
}, z.core.$strip>;
|
|
95
102
|
/**
|
|
96
|
-
* Exit code, signal
|
|
97
|
-
* stay empty unless `capture` was set.
|
|
103
|
+
* Exit code, signal, whether descendants were stopped after the leader exited, and captured
|
|
104
|
+
* stdout and stderr bytes of a finished command; the buffers stay empty unless `capture` was set.
|
|
98
105
|
*/
|
|
99
106
|
export type NativeCommandResult = z.infer<typeof NativeCommandResultSchema>;
|
|
100
107
|
declare const NativeCommandLimitReasonSchema: z.ZodEnum<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/process/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,6BAA6B;;;;;;;2BAOjC,CAAC;AACH;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;;GAEG;AACH,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,UAAY,CAAC;AAEnD,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;kBAOjC,CAAC;AACH;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACpF,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/process/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,6BAA6B;;;;;;;2BAOjC,CAAC;AACH;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;;GAEG;AACH,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,UAAY,CAAC;AAEnD,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;kBAOjC,CAAC;AACH;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACpF,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAqB3F,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;gDAqBpB,UAAU,WACR,QAAQ,GAAG,QAAQ,UACpB,WAAW,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;uDAKX,uBAAuB,UAAU,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;uDAMrE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;kBAgD5C,CAAC;AACL;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,0BAA0B,CAAC,CAAC;AACrF,QAAA,MAAM,yBAAyB;;;;;;iBAU7B,CAAC;AACH;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,QAAA,MAAM,8BAA8B;;;EAAwC,CAAC;AAE7E;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;aAezB,IAAI,EAAE,eAAe,GAAG,qBAAqB,GAAG,iBAAiB;IAdnF,2GAA2G;IAC3G,SAAgB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,GAAG,SAAS,CAAC;IAEnF,YACE,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,GAAG;QAAE,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAA;KAAE,EACpF;IACF,YACE,IAAI,EAAE,eAAe,GAAG,qBAAqB,GAAG,iBAAiB,EACjE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,GAAG;QAAE,MAAM,CAAC,EAAE,KAAK,CAAA;KAAE,EAC3C;CAgBH"}
|
package/dist/process/group.d.ts
CHANGED
|
@@ -23,8 +23,9 @@ export interface CommandGroupStop {
|
|
|
23
23
|
* A group still visible after the grace (including members holding a pipe after
|
|
24
24
|
* the leader exited, or Darwin zombies awaiting the reaper) is killed. The probe
|
|
25
25
|
* backs off to 250 ms, so a grace of an hour costs a few thousand probes, not millions.
|
|
26
|
+
* Resolves `true` when the group still had a member to receive the first signal.
|
|
26
27
|
*/
|
|
27
|
-
export declare function stopCommandGroup(stop: CommandGroupStop): Promise<
|
|
28
|
+
export declare function stopCommandGroup(stop: CommandGroupStop): Promise<boolean>;
|
|
28
29
|
/** The leader of a stop with `target: 'leader'`: its exit state and the promise of its exit. */
|
|
29
30
|
export interface CommandLeader {
|
|
30
31
|
readonly pid?: number;
|
|
@@ -38,11 +39,12 @@ export interface CommandLeader {
|
|
|
38
39
|
* The leader's exit ends the grace at once: the rest of the group are processes the leader
|
|
39
40
|
* left behind, and nothing in them was asked to cooperate. A leader that has already
|
|
40
41
|
* exited is not signalled again, because its pid may be reused once it was reaped.
|
|
42
|
+
* Resolves `true` when the leader or a member of its group received a signal.
|
|
41
43
|
*/
|
|
42
44
|
export declare function stopCommandLeader(stop: CommandGroupStop & {
|
|
43
45
|
leader: CommandLeader;
|
|
44
46
|
leaderExit: Promise<unknown>;
|
|
45
|
-
}): Promise<
|
|
47
|
+
}): Promise<boolean>;
|
|
46
48
|
export declare function commandCleanupError(cause: unknown): NativeCommandError;
|
|
47
49
|
export declare function waitForCommandClose<T>(closed: Promise<T>, timeoutMs: number): Promise<T>;
|
|
48
50
|
//# sourceMappingURL=group.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../src/process/group.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,KAAK,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAEpF,+FAA+F;AAC/F,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IAYpE,MAAM;IACN,MAAM;EAIT;AAeD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CASpF;AAmCD,sGAAsG;AACtG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,6BAA6B,CAAC;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED
|
|
1
|
+
{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../src/process/group.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,KAAK,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAEpF,+FAA+F;AAC/F,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IAYpE,MAAM;IACN,MAAM;EAIT;AAeD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CASpF;AAmCD,sGAAsG;AACtG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,6BAA6B,CAAC;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmB/E;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,gBAAgB,GAAG;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,GAC/E,OAAO,CAAC,OAAO,CAAC,CAsBlB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAItE;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAMxF"}
|
|
@@ -7,7 +7,8 @@ interface SignalableChild extends CommandLeader {
|
|
|
7
7
|
/**
|
|
8
8
|
* Deliver the stop signals to `child` by its stop policy. A child this package spawned as a
|
|
9
9
|
* group leader is stopped as a group (`target: 'group'`) or leader first (`'leader'`); any
|
|
10
|
-
* other child is only a leader, killed while it still runs.
|
|
10
|
+
* other child is only a leader, killed while it still runs. Resolves `true` when a signal
|
|
11
|
+
* reached a process.
|
|
11
12
|
*/
|
|
12
13
|
export declare function signalCommandChild(child: SignalableChild, input: {
|
|
13
14
|
group: boolean;
|
|
@@ -16,7 +17,7 @@ export declare function signalCommandChild(child: SignalableChild, input: {
|
|
|
16
17
|
leaderExit: Promise<unknown>;
|
|
17
18
|
cleanupTimeoutMs: number;
|
|
18
19
|
force: boolean;
|
|
19
|
-
}): Promise<
|
|
20
|
+
}): Promise<boolean>;
|
|
20
21
|
interface ClosableChild extends SignalableChild {
|
|
21
22
|
readonly stdin: {
|
|
22
23
|
destroy(): unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminate.d.ts","sourceRoot":"","sources":["../../src/process/terminate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EACL,KAAK,aAAa,EAKnB,MAAM,SAAS,CAAC;AAGjB,mFAAmF;AACnF,UAAU,eAAgB,SAAQ,aAAa;IAC7C,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED
|
|
1
|
+
{"version":3,"file":"terminate.d.ts","sourceRoot":"","sources":["../../src/process/terminate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EACL,KAAK,aAAa,EAKnB,MAAM,SAAS,CAAC;AAGjB,mFAAmF;AACnF,UAAU,eAAgB,SAAQ,aAAa;IAC7C,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE;IACL,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,6BAA6B,CAAC;IACtC,mFAAmF;IACnF,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;CAChB,GACA,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED,UAAU,aAAc,SAAQ,eAAe;IAC7C,QAAQ,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,OAAO,CAAA;KAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE;QAAE,OAAO,IAAI,OAAO,CAAA;KAAE,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE;QAAE,OAAO,IAAI,OAAO,CAAA;KAAE,CAAC;CACzC;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,EACxB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
package/llms/files-packaging.txt
CHANGED
|
@@ -20,6 +20,8 @@ It resolves the installed package's target/version, reads each selected addon on
|
|
|
20
20
|
against the `size` and `sha256` published in the package's `native-assets.json` (`formatVersion: 2`,
|
|
21
21
|
written when Stitchkit is built). Call it once per build and reuse the result.
|
|
22
22
|
`platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs;
|
|
23
|
+
`assetPath` accepts one template, `[hash]`, replaced by the first 16 hex digits of the addon's
|
|
24
|
+
published SHA256;
|
|
23
25
|
`platform` is the closed set `'darwin'`, and another name is a type error and a schema refusal.
|
|
24
26
|
The result is `ready` with verified assets and a structural Bun plugin, or `unsupported` /
|
|
25
27
|
`missing` / `mismatch` with the safe code `NATIVE_TARGET_UNSUPPORTED`, `NATIVE_ASSET_MISSING` or
|
package/llms/files.txt
CHANGED
|
@@ -31,7 +31,7 @@ available from `stitchkit/contract`.
|
|
|
31
31
|
| `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
|
|
32
32
|
| `writeFileAtomic` / `writeFileAtomicSync` / `WriteFileAtomicOptions` | function / _type_ | replace a file atomically: a random staging name created exclusively (never through a planted link), the mode set on the descriptor before the file is visible (default `0o600`, not masked by the umask), `fsync`, rename; a failure leaves the target and no staging file. The asynchronous form keeps the event loop running. A process killed before publication leaves its staging file; sweeping it is the caller's job |
|
|
33
33
|
| `isAtomicStagingName` | function | true for a staging file name of an atomic write of this library (`.stitchkit-<24 lowercase hex>.tmp`, also used by the managed writer and the chunk spool); the form is a stable contract, and changing it is a breaking change ([ADR 0242](../decisions/0242-atomic-staging-name-is-a-public-contract.md)) |
|
|
34
|
-
| `sweepAtomicStaging` / `SweepAtomicStagingOptions` | function / _type_ | remove abandoned staging files from one `directory`: only regular files matching `isAtomicStagingName` and older than the required `olderThanMs`, never following a symlink or entering a subdirectory; stops on `signal`; returns the removed names |
|
|
34
|
+
| `sweepAtomicStaging` / `SweepAtomicStagingOptions` | function / _type_ | remove abandoned staging files from one `directory`: only regular files matching `isAtomicStagingName` and older than the required `olderThanMs`, never following a symlink or entering a subdirectory; stops on `signal`; returns the removed names, `[]` for a `directory` that does not exist |
|
|
35
35
|
| `withExclusiveLock` / `ExclusiveLockOptions` / `ExclusiveLock` / `ExclusiveLockOwner` | function / _type_ | run work under an exclusive lock between processes — a file recording its owner (pid, host, machine identity, time and nullable kernel process identity); waits up to `timeoutMs` (default 10 s) and stops on `signal`; a dead owner on this machine is taken over, a live, slow or foreign one never is; the lock is published with its owner already recorded (temporary file plus hard link), so a stalled holder is never displaced; a legacy empty lock file is taken by age only when `ownerlessGraceMs` is a number (default `null`: never; an abandoned empty reclaim guard is taken after 5 000 ms unless a value is set); exact `mode` is independent of umask; shared readers need a common group and traversable directories |
|
|
36
36
|
| `ExclusiveLockError` | class | `LOCK_TIMEOUT` naming the resource and its holder, or `LOCK_ABORTED` with the signal's reason |
|
|
37
37
|
| `createChunkSpool` | function | the server half of a chunked upload: parts on disk under `owner` + client-minted `uploadId`, each with a size+sha256 receipt; idempotent `open` and `put`, the first writer of a part wins across processes, `assemble` in order once every part is in, `sweep` of untouched uploads — [guide](../guide/client.md#chunked-uploads) |
|
package/llms/process.txt
CHANGED
|
@@ -68,7 +68,8 @@ The bytes are staged beside the target under `.stitchkit-<24 lowercase hex>.tmp`
|
|
|
68
68
|
`writeFileAtomic`, `writeFileAtomicSync`, the managed writer and the chunk spool. A process killed
|
|
69
69
|
between the staging and the publication (SIGKILL, OOM, power loss) leaves that file behind, and no
|
|
70
70
|
later write removes it: a random name alone cannot tell an abandoned write from one in flight.
|
|
71
|
-
Removing it is the caller's responsibility
|
|
71
|
+
Removing it is the caller's responsibility. A directory that does not exist yet has nothing to
|
|
72
|
+
sweep and yields `[]`; a file standing where the directory should be, or a denied listing, throws:
|
|
72
73
|
|
|
73
74
|
```ts
|
|
74
75
|
import { isAtomicStagingName, sweepAtomicStaging } from 'stitchkit/files'
|
|
@@ -197,7 +198,7 @@ permanent refusal is kept in the cause. The close of every owned pipe and the le
|
|
|
197
198
|
observed separately, including the absence of a shared `child.close` during teardown. The caller or
|
|
198
199
|
sink cause is kept; a cleanup failure is a `NativeCommandError` with `COMMAND_CLEANUP` and an
|
|
199
200
|
`AggregateError` cause. Even an exited parent does not exclude a helper that holds a pipe. Windows
|
|
200
|
-
refuses with `COMMAND_UNAVAILABLE`; a descendant that
|
|
201
|
+
refuses with `COMMAND_UNAVAILABLE`; a descendant that has left the group on its own with `setsid` is
|
|
201
202
|
outside this capability. This is one-shot execution, not a PTY, a supervisor or a daemon host.
|
|
202
203
|
|
|
203
204
|
### Stopping a command: the `stop` policy
|
|
@@ -252,13 +253,28 @@ successfully:
|
|
|
252
253
|
|
|
253
254
|
| `descendants` | After the leader exits |
|
|
254
255
|
|---|---|
|
|
255
|
-
| `'terminate-after-leader'` (the default) | The group is stopped right after `onLeaderSettled` returns and before the pipes drain: with `target: 'group'` by the same signal, grace, KILL sequence as cancellation; with `target: 'leader'` by KILL at once, since the leader has already exited.
|
|
256
|
+
| `'terminate-after-leader'` (the default) | The group is stopped right after `onLeaderSettled` returns and before the pipes drain: with `target: 'group'` by the same signal, grace, KILL sequence as cancellation; with `target: 'leader'` by KILL at once, since the leader has already exited. A descendant that had already left the group with `setsid` when the leader exited stays out of reach. |
|
|
256
257
|
| `'leave'` | Nothing. A helper started with its stdio detached keeps running. One that inherited stdout or stderr keeps the pipes open, and the command then waits for it until its deadline or abort. |
|
|
257
258
|
|
|
258
259
|
Omitting the option means `'terminate-after-leader'`: a command does not leak the helpers it
|
|
259
|
-
started.
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
started. The result's `descendantsStopped` is `true` when the group still had members after the
|
|
261
|
+
leader exited and they were stopped, so a helper that was ended this way is visible to the caller
|
|
262
|
+
rather than only missing afterwards; under `'leave'` it is always `false`.
|
|
263
|
+
|
|
264
|
+
A real daemon detaches into its own session (`setsid`, as `daemon(3)` does), is no member of the
|
|
265
|
+
group and is unaffected, provided it has left the group **before the leader exits**. Leaving is
|
|
266
|
+
the daemon's own system call, and the group is stopped as soon as the leader's exit is observed,
|
|
267
|
+
so a helper started in the background races the leader:
|
|
268
|
+
|
|
269
|
+
| launched by the leader as | after the leader exits |
|
|
270
|
+
|---|---|
|
|
271
|
+
| `setsid -f helper` (returns once the new session exists) | survives |
|
|
272
|
+
| `setsid helper &`, `(setsid helper &)`, `nohup helper &` | stopped: the leader usually exits before the background job has left the group |
|
|
273
|
+
| any of these under `descendants: 'leave'` | survives |
|
|
274
|
+
|
|
275
|
+
Start a daemon so that the session exists before the leader returns (`setsid -f`, or the
|
|
276
|
+
daemon's own double fork followed by the leader waiting for it), or declare `'leave'`. Declare
|
|
277
|
+
`'leave'` only for a helper that has to outlive the command while staying in its group:
|
|
262
278
|
|
|
263
279
|
```ts
|
|
264
280
|
await runNativeCommand({
|
|
@@ -279,7 +295,21 @@ regular-file read becomes `files.read`; JSON and schema parsing, receipt conflic
|
|
|
279
295
|
and application locks stay with the application. A spawn, drain, capture and cancellation wrapper
|
|
280
296
|
becomes `runNativeCommand`; shell policy, command admission, logging, redaction, the bytes-to-text
|
|
281
297
|
decoder and the operator sink stay with the application. Long signal-only commands stay streaming;
|
|
282
|
-
bounded tar or capture commands get their own explicit limits.
|
|
298
|
+
bounded tar or capture commands get their own explicit limits.
|
|
299
|
+
|
|
300
|
+
A child the caller holds until it exits is the same call. A guardian that forwards its own SIGTERM
|
|
301
|
+
passes an abort signal and `stop: { target: 'leader', signal: 'SIGTERM', graceMs }`. It records the
|
|
302
|
+
pid in `onLeaderStarted({ pid })`, which runs once right after the leader exists (a throw stops the
|
|
303
|
+
command with that error), reads the exit code and signal in `onLeaderSettled` before the pipes
|
|
304
|
+
drain, and reads stderr through `onOutput` or `capture` with `maxOutputBytes`. `drainTimeoutMs`
|
|
305
|
+
bounds how long the output pipes may stay open after the leader exited: a holder that left the
|
|
306
|
+
group with `setsid` keeps them open, and without the option the command waits for it until
|
|
307
|
+
`timeoutMs` or the signal; with it the command ends with `COMMAND_CLEANUP` (a holder inside the
|
|
308
|
+
group is already stopped by `descendants`). A worker whose whole group must stop on abort is the
|
|
309
|
+
default `target: 'group'`. A child that inherits the caller's terminal is outside this
|
|
310
|
+
capability: every command runs in a group of its own, and a background group that reads the
|
|
311
|
+
terminal is stopped by the kernel (`SIGTTIN`), so an interactive re-execution stays a plain
|
|
312
|
+
spawn in the caller's group. Importing one leaf does not make
|
|
283
313
|
installing the whole npm package smaller; choose a dependency after measuring packed size and its
|
|
284
314
|
dependency closure.
|
|
285
315
|
|
|
@@ -407,7 +437,11 @@ package was built. A ready asset carries those verified `bytes` with the publish
|
|
|
407
437
|
`sha256`; its Bun-compatible plugin integrates the same bytes with the same lazy native loader.
|
|
408
438
|
Each call reads and hashes the selected addons, so call it once per build and reuse the result.
|
|
409
439
|
The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
|
|
410
|
-
|
|
440
|
+
Calling it must happen in a process that runs it from its installed package: it locates the
|
|
441
|
+
package's `native-assets.json` beside its own file and throws `Native packaging must run from its
|
|
442
|
+
installed Stitchkit package` anywhere else, such as inside an application's runtime artifact.
|
|
443
|
+
Importing the leaf is side-effect free (it loads no addon and reads no file), so a module that
|
|
444
|
+
both builds and is bundled may import it; only the call has to stay in the build step.
|
|
411
445
|
|
|
412
446
|
```ts
|
|
413
447
|
import { createNativePackaging } from 'stitchkit/files/packaging'
|
|
@@ -434,7 +468,11 @@ for (const asset of native.assets) {
|
|
|
434
468
|
```
|
|
435
469
|
|
|
436
470
|
`entryPath` and `assetPath` are relative paths inside the application's output root, without
|
|
437
|
-
absolute paths, traversal or overlapping file/directory paths. `
|
|
471
|
+
absolute paths, traversal or overlapping file/directory paths. `assetPath` may contain `[hash]`,
|
|
472
|
+
which becomes the first 16 hex digits of that addon's published SHA256: the digest is known before
|
|
473
|
+
the build, so `addons/darwin-arm64-[hash].node` gives two installed versions of the package
|
|
474
|
+
different companion names, and the loader requires exactly that name. It is the only template an
|
|
475
|
+
addon path accepts. `entryPath` is fixed, without
|
|
438
476
|
Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
|
|
439
477
|
and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
|
|
440
478
|
Choose any application layout; the framework does not prescribe an app directory or installer.
|
|
@@ -536,9 +574,9 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
|
|
|
536
574
|
| Export | Kind | Purpose |
|
|
537
575
|
|---|---|---|
|
|
538
576
|
| `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
|
|
539
|
-
| `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`) |
|
|
577
|
+
| `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`); `onLeaderStarted({ pid })` (once, after the leader exists), `drainTimeoutMs` (bound on the pipes staying open after the leader exited, `COMMAND_CLEANUP`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
|
|
540
578
|
| `NativeCommandStopPolicy` | type | how a cancelled command stops: `target` `'group'` (signal every member) or `'leader'` (signal only the leader; its exit ends the grace), `signal` (default `SIGTERM`), `graceMs` (at most one hour) before KILL to the group, and `killOn`, an `AbortSignal` that skips the grace; default `{ target: 'group', signal: 'SIGTERM', graceMs: 100 }` |
|
|
541
|
-
| `NativeCommandResult` | type | observed nullable exit code/signal and captured stdout/stderr bytes |
|
|
579
|
+
| `NativeCommandResult` | type | observed nullable exit code/signal, `descendantsStopped` (members left in the group after the leader exited were stopped) and captured stdout/stderr bytes |
|
|
542
580
|
| `NativeCommandError` | class | COMMAND_LIMIT with machine reason `deadline` or `output-budget`; COMMAND_UNAVAILABLE / COMMAND_CLEANUP have no limit reason. Preserves internal cause and existing constructor; caller-created limits without supplied reason remain unclassified |
|
|
543
581
|
|
|
544
582
|
| `NativeCommandSettlement` | type | observed leader `exit` or terminal `error`, including synchronous native launch failure; `onLeaderSettled` runs once before drain, bounded by cleanupTimeoutMs, with a cancellable settlement signal; schema/pre-abort refusal creates no settlement obligation |
|
package/llms-full.txt
CHANGED
|
@@ -8210,7 +8210,8 @@ The bytes are staged beside the target under `.stitchkit-<24 lowercase hex>.tmp`
|
|
|
8210
8210
|
`writeFileAtomic`, `writeFileAtomicSync`, the managed writer and the chunk spool. A process killed
|
|
8211
8211
|
between the staging and the publication (SIGKILL, OOM, power loss) leaves that file behind, and no
|
|
8212
8212
|
later write removes it: a random name alone cannot tell an abandoned write from one in flight.
|
|
8213
|
-
Removing it is the caller's responsibility
|
|
8213
|
+
Removing it is the caller's responsibility. A directory that does not exist yet has nothing to
|
|
8214
|
+
sweep and yields `[]`; a file standing where the directory should be, or a denied listing, throws:
|
|
8214
8215
|
|
|
8215
8216
|
```ts
|
|
8216
8217
|
import { isAtomicStagingName, sweepAtomicStaging } from 'stitchkit/files'
|
|
@@ -8339,7 +8340,7 @@ permanent refusal is kept in the cause. The close of every owned pipe and the le
|
|
|
8339
8340
|
observed separately, including the absence of a shared `child.close` during teardown. The caller or
|
|
8340
8341
|
sink cause is kept; a cleanup failure is a `NativeCommandError` with `COMMAND_CLEANUP` and an
|
|
8341
8342
|
`AggregateError` cause. Even an exited parent does not exclude a helper that holds a pipe. Windows
|
|
8342
|
-
refuses with `COMMAND_UNAVAILABLE`; a descendant that
|
|
8343
|
+
refuses with `COMMAND_UNAVAILABLE`; a descendant that has left the group on its own with `setsid` is
|
|
8343
8344
|
outside this capability. This is one-shot execution, not a PTY, a supervisor or a daemon host.
|
|
8344
8345
|
|
|
8345
8346
|
### Stopping a command: the `stop` policy
|
|
@@ -8394,13 +8395,28 @@ successfully:
|
|
|
8394
8395
|
|
|
8395
8396
|
| `descendants` | After the leader exits |
|
|
8396
8397
|
|---|---|
|
|
8397
|
-
| `'terminate-after-leader'` (the default) | The group is stopped right after `onLeaderSettled` returns and before the pipes drain: with `target: 'group'` by the same signal, grace, KILL sequence as cancellation; with `target: 'leader'` by KILL at once, since the leader has already exited.
|
|
8398
|
+
| `'terminate-after-leader'` (the default) | The group is stopped right after `onLeaderSettled` returns and before the pipes drain: with `target: 'group'` by the same signal, grace, KILL sequence as cancellation; with `target: 'leader'` by KILL at once, since the leader has already exited. A descendant that had already left the group with `setsid` when the leader exited stays out of reach. |
|
|
8398
8399
|
| `'leave'` | Nothing. A helper started with its stdio detached keeps running. One that inherited stdout or stderr keeps the pipes open, and the command then waits for it until its deadline or abort. |
|
|
8399
8400
|
|
|
8400
8401
|
Omitting the option means `'terminate-after-leader'`: a command does not leak the helpers it
|
|
8401
|
-
started.
|
|
8402
|
-
|
|
8403
|
-
|
|
8402
|
+
started. The result's `descendantsStopped` is `true` when the group still had members after the
|
|
8403
|
+
leader exited and they were stopped, so a helper that was ended this way is visible to the caller
|
|
8404
|
+
rather than only missing afterwards; under `'leave'` it is always `false`.
|
|
8405
|
+
|
|
8406
|
+
A real daemon detaches into its own session (`setsid`, as `daemon(3)` does), is no member of the
|
|
8407
|
+
group and is unaffected, provided it has left the group **before the leader exits**. Leaving is
|
|
8408
|
+
the daemon's own system call, and the group is stopped as soon as the leader's exit is observed,
|
|
8409
|
+
so a helper started in the background races the leader:
|
|
8410
|
+
|
|
8411
|
+
| launched by the leader as | after the leader exits |
|
|
8412
|
+
|---|---|
|
|
8413
|
+
| `setsid -f helper` (returns once the new session exists) | survives |
|
|
8414
|
+
| `setsid helper &`, `(setsid helper &)`, `nohup helper &` | stopped: the leader usually exits before the background job has left the group |
|
|
8415
|
+
| any of these under `descendants: 'leave'` | survives |
|
|
8416
|
+
|
|
8417
|
+
Start a daemon so that the session exists before the leader returns (`setsid -f`, or the
|
|
8418
|
+
daemon's own double fork followed by the leader waiting for it), or declare `'leave'`. Declare
|
|
8419
|
+
`'leave'` only for a helper that has to outlive the command while staying in its group:
|
|
8404
8420
|
|
|
8405
8421
|
```ts
|
|
8406
8422
|
await runNativeCommand({
|
|
@@ -8421,7 +8437,21 @@ regular-file read becomes `files.read`; JSON and schema parsing, receipt conflic
|
|
|
8421
8437
|
and application locks stay with the application. A spawn, drain, capture and cancellation wrapper
|
|
8422
8438
|
becomes `runNativeCommand`; shell policy, command admission, logging, redaction, the bytes-to-text
|
|
8423
8439
|
decoder and the operator sink stay with the application. Long signal-only commands stay streaming;
|
|
8424
|
-
bounded tar or capture commands get their own explicit limits.
|
|
8440
|
+
bounded tar or capture commands get their own explicit limits.
|
|
8441
|
+
|
|
8442
|
+
A child the caller holds until it exits is the same call. A guardian that forwards its own SIGTERM
|
|
8443
|
+
passes an abort signal and `stop: { target: 'leader', signal: 'SIGTERM', graceMs }`. It records the
|
|
8444
|
+
pid in `onLeaderStarted({ pid })`, which runs once right after the leader exists (a throw stops the
|
|
8445
|
+
command with that error), reads the exit code and signal in `onLeaderSettled` before the pipes
|
|
8446
|
+
drain, and reads stderr through `onOutput` or `capture` with `maxOutputBytes`. `drainTimeoutMs`
|
|
8447
|
+
bounds how long the output pipes may stay open after the leader exited: a holder that left the
|
|
8448
|
+
group with `setsid` keeps them open, and without the option the command waits for it until
|
|
8449
|
+
`timeoutMs` or the signal; with it the command ends with `COMMAND_CLEANUP` (a holder inside the
|
|
8450
|
+
group is already stopped by `descendants`). A worker whose whole group must stop on abort is the
|
|
8451
|
+
default `target: 'group'`. A child that inherits the caller's terminal is outside this
|
|
8452
|
+
capability: every command runs in a group of its own, and a background group that reads the
|
|
8453
|
+
terminal is stopped by the kernel (`SIGTTIN`), so an interactive re-execution stays a plain
|
|
8454
|
+
spawn in the caller's group. Importing one leaf does not make
|
|
8425
8455
|
installing the whole npm package smaller; choose a dependency after measuring packed size and its
|
|
8426
8456
|
dependency closure.
|
|
8427
8457
|
|
|
@@ -8549,7 +8579,11 @@ package was built. A ready asset carries those verified `bytes` with the publish
|
|
|
8549
8579
|
`sha256`; its Bun-compatible plugin integrates the same bytes with the same lazy native loader.
|
|
8550
8580
|
Each call reads and hashes the selected addons, so call it once per build and reuse the result.
|
|
8551
8581
|
The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
|
|
8552
|
-
|
|
8582
|
+
Calling it must happen in a process that runs it from its installed package: it locates the
|
|
8583
|
+
package's `native-assets.json` beside its own file and throws `Native packaging must run from its
|
|
8584
|
+
installed Stitchkit package` anywhere else, such as inside an application's runtime artifact.
|
|
8585
|
+
Importing the leaf is side-effect free (it loads no addon and reads no file), so a module that
|
|
8586
|
+
both builds and is bundled may import it; only the call has to stay in the build step.
|
|
8553
8587
|
|
|
8554
8588
|
```ts
|
|
8555
8589
|
import { createNativePackaging } from 'stitchkit/files/packaging'
|
|
@@ -8576,7 +8610,11 @@ for (const asset of native.assets) {
|
|
|
8576
8610
|
```
|
|
8577
8611
|
|
|
8578
8612
|
`entryPath` and `assetPath` are relative paths inside the application's output root, without
|
|
8579
|
-
absolute paths, traversal or overlapping file/directory paths. `
|
|
8613
|
+
absolute paths, traversal or overlapping file/directory paths. `assetPath` may contain `[hash]`,
|
|
8614
|
+
which becomes the first 16 hex digits of that addon's published SHA256: the digest is known before
|
|
8615
|
+
the build, so `addons/darwin-arm64-[hash].node` gives two installed versions of the package
|
|
8616
|
+
different companion names, and the loader requires exactly that name. It is the only template an
|
|
8617
|
+
addon path accepts. `entryPath` is fixed, without
|
|
8580
8618
|
Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
|
|
8581
8619
|
and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
|
|
8582
8620
|
Choose any application layout; the framework does not prescribe an app directory or installer.
|
|
@@ -22039,7 +22077,7 @@ available from `stitchkit/contract`.
|
|
|
22039
22077
|
| `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
|
|
22040
22078
|
| `writeFileAtomic` / `writeFileAtomicSync` / `WriteFileAtomicOptions` | function / _type_ | replace a file atomically: a random staging name created exclusively (never through a planted link), the mode set on the descriptor before the file is visible (default `0o600`, not masked by the umask), `fsync`, rename; a failure leaves the target and no staging file. The asynchronous form keeps the event loop running. A process killed before publication leaves its staging file; sweeping it is the caller's job |
|
|
22041
22079
|
| `isAtomicStagingName` | function | true for a staging file name of an atomic write of this library (`.stitchkit-<24 lowercase hex>.tmp`, also used by the managed writer and the chunk spool); the form is a stable contract, and changing it is a breaking change ([ADR 0242](../decisions/0242-atomic-staging-name-is-a-public-contract.md)) |
|
|
22042
|
-
| `sweepAtomicStaging` / `SweepAtomicStagingOptions` | function / _type_ | remove abandoned staging files from one `directory`: only regular files matching `isAtomicStagingName` and older than the required `olderThanMs`, never following a symlink or entering a subdirectory; stops on `signal`; returns the removed names |
|
|
22080
|
+
| `sweepAtomicStaging` / `SweepAtomicStagingOptions` | function / _type_ | remove abandoned staging files from one `directory`: only regular files matching `isAtomicStagingName` and older than the required `olderThanMs`, never following a symlink or entering a subdirectory; stops on `signal`; returns the removed names, `[]` for a `directory` that does not exist |
|
|
22043
22081
|
| `withExclusiveLock` / `ExclusiveLockOptions` / `ExclusiveLock` / `ExclusiveLockOwner` | function / _type_ | run work under an exclusive lock between processes — a file recording its owner (pid, host, machine identity, time and nullable kernel process identity); waits up to `timeoutMs` (default 10 s) and stops on `signal`; a dead owner on this machine is taken over, a live, slow or foreign one never is; the lock is published with its owner already recorded (temporary file plus hard link), so a stalled holder is never displaced; a legacy empty lock file is taken by age only when `ownerlessGraceMs` is a number (default `null`: never; an abandoned empty reclaim guard is taken after 5 000 ms unless a value is set); exact `mode` is independent of umask; shared readers need a common group and traversable directories |
|
|
22044
22082
|
| `ExclusiveLockError` | class | `LOCK_TIMEOUT` naming the resource and its holder, or `LOCK_ABORTED` with the signal's reason |
|
|
22045
22083
|
| `createChunkSpool` | function | the server half of a chunked upload: parts on disk under `owner` + client-minted `uploadId`, each with a size+sha256 receipt; idempotent `open` and `put`, the first writer of a part wins across processes, `assemble` in order once every part is in, `sweep` of untouched uploads — [guide](../guide/client.md#chunked-uploads) |
|
|
@@ -22608,9 +22646,9 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
|
|
|
22608
22646
|
| Export | Kind | Purpose |
|
|
22609
22647
|
|---|---|---|
|
|
22610
22648
|
| `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
|
|
22611
|
-
| `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`) |
|
|
22649
|
+
| `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`); `onLeaderStarted({ pid })` (once, after the leader exists), `drainTimeoutMs` (bound on the pipes staying open after the leader exited, `COMMAND_CLEANUP`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
|
|
22612
22650
|
| `NativeCommandStopPolicy` | type | how a cancelled command stops: `target` `'group'` (signal every member) or `'leader'` (signal only the leader; its exit ends the grace), `signal` (default `SIGTERM`), `graceMs` (at most one hour) before KILL to the group, and `killOn`, an `AbortSignal` that skips the grace; default `{ target: 'group', signal: 'SIGTERM', graceMs: 100 }` |
|
|
22613
|
-
| `NativeCommandResult` | type | observed nullable exit code/signal and captured stdout/stderr bytes |
|
|
22651
|
+
| `NativeCommandResult` | type | observed nullable exit code/signal, `descendantsStopped` (members left in the group after the leader exited were stopped) and captured stdout/stderr bytes |
|
|
22614
22652
|
| `NativeCommandError` | class | COMMAND_LIMIT with machine reason `deadline` or `output-budget`; COMMAND_UNAVAILABLE / COMMAND_CLEANUP have no limit reason. Preserves internal cause and existing constructor; caller-created limits without supplied reason remain unclassified |
|
|
22615
22653
|
|
|
22616
22654
|
| `NativeCommandSettlement` | type | observed leader `exit` or terminal `error`, including synchronous native launch failure; `onLeaderSettled` runs once before drain, bounded by cleanupTimeoutMs, with a cancellable settlement signal; schema/pre-abort refusal creates no settlement obligation |
|
|
@@ -22626,6 +22664,8 @@ It resolves the installed package's target/version, reads each selected addon on
|
|
|
22626
22664
|
against the `size` and `sha256` published in the package's `native-assets.json` (`formatVersion: 2`,
|
|
22627
22665
|
written when Stitchkit is built). Call it once per build and reuse the result.
|
|
22628
22666
|
`platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs;
|
|
22667
|
+
`assetPath` accepts one template, `[hash]`, replaced by the first 16 hex digits of the addon's
|
|
22668
|
+
published SHA256;
|
|
22629
22669
|
`platform` is the closed set `'darwin'`, and another name is a type error and a schema refusal.
|
|
22630
22670
|
The result is `ready` with verified assets and a structural Bun plugin, or `unsupported` /
|
|
22631
22671
|
`missing` / `mismatch` with the safe code `NATIVE_TARGET_UNSUPPORTED`, `NATIVE_ASSET_MISSING` or
|
package/llms.txt
CHANGED
|
@@ -56,8 +56,8 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
56
56
|
- **stitchkit/node**: [llms/node.txt](llms/node.txt) 3.1 KB
|
|
57
57
|
- **stitchkit/agent-runtime/react**: [llms/agent-runtime-react.txt](llms/agent-runtime-react.txt) 1.0 KB
|
|
58
58
|
- **stitchkit/agent-runtime/realtime**: [llms/agent-runtime-realtime.txt](llms/agent-runtime-realtime.txt) 2.6 KB
|
|
59
|
-
- **stitchkit/process**: [llms/process.txt](llms/process.txt)
|
|
60
|
-
- **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 2.
|
|
59
|
+
- **stitchkit/process**: [llms/process.txt](llms/process.txt) 36.2 KB
|
|
60
|
+
- **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 2.8 KB
|
|
61
61
|
|
|
62
62
|
## Upgrading slices
|
|
63
63
|
- **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 10.3 KB
|
package/package.json
CHANGED