stitchkit 0.105.0 → 0.105.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/entrypoints/agent-runtime/coding-tools.js +3 -3
  3. package/dist/entrypoints/agent-runtime/sandbox.js +3 -3
  4. package/dist/entrypoints/application/directory-inbox.js +3 -3
  5. package/dist/entrypoints/cli/publish.js +4 -4
  6. package/dist/entrypoints/cli.js +3 -3
  7. package/dist/entrypoints/files/packaging.js +10 -4
  8. package/dist/entrypoints/files.js +3 -3
  9. package/dist/entrypoints/process.js +1 -1
  10. package/dist/entrypoints/server.js +3 -3
  11. package/dist/entrypoints/telegram.js +2 -2
  12. package/dist/entrypoints/tools.js +3 -3
  13. package/dist/files/native-packaging.d.ts.map +1 -1
  14. package/dist/{index-2rc6jd9n.js → index-1ptj98e0.js} +1 -1
  15. package/dist/{index-g2vfg7ak.js → index-5c26631f.js} +33 -10
  16. package/dist/{index-ef8gdaq0.js → index-cazts87s.js} +1 -1
  17. package/dist/{index-szybbezc.js → index-drs41arz.js} +1 -1
  18. package/dist/{index-cq59agna.js → index-j4zh647a.js} +9 -1
  19. package/dist/{index-wqmvtnkp.js → index-w8etpm8j.js} +1 -1
  20. package/dist/{index-ma963wz7.js → index-y7k2cdgv.js} +2 -2
  21. package/dist/internal/atomic-staging.d.ts +2 -1
  22. package/dist/internal/atomic-staging.d.ts.map +1 -1
  23. package/dist/process/cleanup.d.ts +1 -1
  24. package/dist/process/cleanup.d.ts.map +1 -1
  25. package/dist/process/command-lifetime.d.ts +2 -1
  26. package/dist/process/command-lifetime.d.ts.map +1 -1
  27. package/dist/process/command-owner.d.ts +1 -0
  28. package/dist/process/command-owner.d.ts.map +1 -1
  29. package/dist/process/contract.d.ts +3 -2
  30. package/dist/process/contract.d.ts.map +1 -1
  31. package/dist/process/group.d.ts +4 -2
  32. package/dist/process/group.d.ts.map +1 -1
  33. package/dist/process/terminate.d.ts +3 -2
  34. package/dist/process/terminate.d.ts.map +1 -1
  35. package/llms/files-packaging.txt +2 -0
  36. package/llms/files.txt +1 -1
  37. package/llms/process.txt +45 -11
  38. package/llms-full.txt +48 -12
  39. package/llms.txt +2 -2
  40. package/package.json +1 -1
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.105.1] - 2026-10-06
19
+
20
+ ### Added
21
+
22
+ - `stitchkit/process` — `NativeCommandResult.descendantsStopped` reports that the group still had
23
+ members after the leader exited and that `descendants: 'terminate-after-leader'` stopped them,
24
+ so a helper ended this way is visible in the result instead of only missing afterwards.
25
+ - `stitchkit/files/packaging` — `assetPath` accepts `[hash]`, replaced by the first 16 hex digits
26
+ of the addon's published SHA256, so two installed versions get different companion names
27
+ (`addons/darwin-arm64-[hash].node`); the loader requires exactly that name.
28
+
29
+ ### Fixed
30
+
31
+ - `stitchkit/files` — `sweepAtomicStaging` returns `[]` for a `directory` that does not exist yet
32
+ instead of throwing `ENOENT`; any other listing failure still throws.
33
+ - `stitchkit/process` — an option `runNativeCommand` does not know is refused by name, and the
34
+ removed `killGraceMs` names its replacement, `stop: { target: 'group', graceMs }`: a spread
35
+ object can carry a key the type never checked.
36
+
37
+ ### Documentation
38
+
39
+ - `stitchkit/process` — the 0.105.0 note that a descendant leaving the group with `setsid` is
40
+ unaffected holds only when it has left before the leader exits: `setsid helper &` and
41
+ `nohup helper &` race the leader and are stopped, `setsid -f helper` survives. The native IO
42
+ guide shows the measured forms, how a guardian or worker that holds a child until it exits
43
+ maps onto `runNativeCommand`, and why a child inheriting the terminal stays a plain spawn.
44
+ - `stitchkit/files/packaging` — calling `createNativePackaging` has to stay in the build step;
45
+ importing the leaf from a module that is also bundled is side-effect free.
46
+
18
47
  ## [0.105.0] - 2026-10-05
19
48
 
20
49
  ### ⚠️ Breaking changes
@@ -7,10 +7,10 @@ import {
7
7
  AgentCodingToolAuthorizationSchema2,
8
8
  AgentCodingToolPathAuthorizationSchema2,
9
9
  createAgentCodingTools2
10
- } from "../../index-ma963wz7.js";
10
+ } from "../../index-y7k2cdgv.js";
11
11
  import"../../index-v5ee8ath.js";
12
- import"../../index-cq59agna.js";
13
- import"../../index-g2vfg7ak.js";
12
+ import"../../index-j4zh647a.js";
13
+ import"../../index-5c26631f.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-ma963wz7.js";
12
+ } from "../../index-y7k2cdgv.js";
13
13
  import"../../index-v5ee8ath.js";
14
- import"../../index-cq59agna.js";
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-g2vfg7ak.js";
22
+ } from "../../index-5c26631f.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-cq59agna.js";
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-2rc6jd9n.js";
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-ef8gdaq0.js";
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-cq59agna.js";
2
+ import"../../index-j4zh647a.js";
3
3
  import {
4
4
  ManagedFileError2,
5
5
  createManagedFileBoundary2
6
- } from "../../index-wqmvtnkp.js";
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-szybbezc.js";
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-ef8gdaq0.js";
29
+ } from "../../index-cazts87s.js";
30
30
  import {
31
31
  AtomicFilePublicationError2,
32
32
  assertDirectoryDurability,
@@ -16,7 +16,7 @@ import {
16
16
  pollUntilDone2,
17
17
  createCli2
18
18
  } from "../index-ksfagmyz.js";
19
- import"../index-cq59agna.js";
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-szybbezc.js";
48
+ } from "../index-drs41arz.js";
49
49
  import {
50
50
  writeFileAtomicSync2
51
- } from "../index-ef8gdaq0.js";
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: RelativePathSchema
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), RelativePathSchema)
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 outputPath = typeof input.assetPath === "string" ? input.assetPath : input.assetPath[architecture];
224
- if (!outputPath)
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-cq59agna.js";
9
+ } from "../index-j4zh647a.js";
10
10
  import {
11
11
  ManagedFileError2,
12
12
  createManagedFileBoundary2
13
- } from "../index-wqmvtnkp.js";
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-ef8gdaq0.js";
27
+ } from "../index-cazts87s.js";
28
28
  import {
29
29
  AtomicFilePublicationError2
30
30
  } from "../index-b56nnscm.js";
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  NativeCommandError2,
9
9
  startNativeCommand
10
- } from "../index-g2vfg7ak.js";
10
+ } from "../index-5c26631f.js";
11
11
  // src/process/command.ts
12
12
  function runNativeCommand(input) {
13
13
  return startNativeCommand(input).result;
@@ -1,10 +1,10 @@
1
- import"../index-cq59agna.js";
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-2rc6jd9n.js";
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-ef8gdaq0.js";
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-cq59agna.js";
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-ef8gdaq0.js";
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-cq59agna.js";
88
+ import"../index-j4zh647a.js";
89
89
  import {
90
90
  ManagedFileError2
91
- } from "../index-wqmvtnkp.js";
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-ef8gdaq0.js";
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":"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"}
1
+ {"version":3,"file":"native-packaging.d.ts","sourceRoot":"","sources":["../../src/files/native-packaging.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import {
6
6
  } from "./index-m616f0yz.js";
7
7
  import {
8
8
  writeAtomicFileData
9
- } from "./index-ef8gdaq0.js";
9
+ } from "./index-cazts87s.js";
10
10
  import {
11
11
  publishAtomicFile
12
12
  } from "./index-b56nnscm.js";
@@ -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([]),
@@ -48,6 +58,8 @@ var NativeCommandOptionsSchema = z.strictObject({
48
58
  }),
49
59
  descendants: z.enum(["terminate-after-leader", "leave"]).default("terminate-after-leader"),
50
60
  cleanupTimeoutMs: z.number().int().positive().max(30000).default(COMMAND_CLEANUP_TIMEOUT_MS)
61
+ }, {
62
+ error: (issue) => issue.code === "unrecognized_keys" ? unknownOptionMessage(issue.keys) : undefined
51
63
  }).check((ctx) => {
52
64
  const input = ctx.value;
53
65
  if (!input.signal && input.timeoutMs === undefined)
@@ -72,6 +84,7 @@ var NativeCommandOptionsSchema = z.strictObject({
72
84
  var NativeCommandResultSchema = z.object({
73
85
  exitCode: z.number().int().nullable(),
74
86
  signal: z.string().nullable(),
87
+ descendantsStopped: z.boolean(),
75
88
  stdout: z.instanceof(Uint8Array),
76
89
  stderr: z.instanceof(Uint8Array)
77
90
  });
@@ -158,12 +171,10 @@ async function signalUntilSettled(pid, signal, deadline) {
158
171
  async function stopCommandGroup(stop) {
159
172
  const { pid, policy } = stop;
160
173
  const deadline = performance.now() + policy.graceMs + stop.cleanupTimeoutMs;
161
- if (stop.force || policy.killOn?.aborted) {
162
- await signalUntilSettled(pid, "SIGKILL", deadline);
163
- return;
164
- }
174
+ if (stop.force || policy.killOn?.aborted)
175
+ return signalUntilSettled(pid, "SIGKILL", deadline);
165
176
  if (!await signalUntilSettled(pid, policy.signal, deadline))
166
- return;
177
+ return false;
167
178
  const until = performance.now() + policy.graceMs;
168
179
  let interval = 10;
169
180
  let present = exists(pid);
@@ -176,14 +187,17 @@ async function stopCommandGroup(stop) {
176
187
  }
177
188
  if (present)
178
189
  await signalUntilSettled(pid, "SIGKILL", deadline);
190
+ return true;
179
191
  }
180
192
  async function stopCommandLeader(stop) {
181
193
  const { pid, policy, leader } = stop;
182
194
  const deadline = performance.now() + policy.graceMs + stop.cleanupTimeoutMs;
183
195
  const running = () => leader.exitCode === null && leader.signalCode === null;
196
+ let signalled = false;
184
197
  if (!stop.force && !policy.killOn?.aborted && pid !== undefined && running()) {
185
198
  try {
186
199
  process.kill(pid, policy.signal);
200
+ signalled = true;
187
201
  } catch (error) {
188
202
  if (!isErrno(error, "ESRCH"))
189
203
  throw error;
@@ -198,7 +212,7 @@ async function stopCommandLeader(stop) {
198
212
  policy.killOn?.removeEventListener("abort", end);
199
213
  grace.abort();
200
214
  }
201
- await signalUntilSettled(pid, "SIGKILL", deadline);
215
+ return await signalUntilSettled(pid, "SIGKILL", deadline) || signalled;
202
216
  }
203
217
  function commandCleanupError(cause) {
204
218
  return new NativeCommandError2("COMMAND_CLEANUP", "Command cleanup did not complete", {
@@ -616,8 +630,11 @@ function signalCommandChild(child, input) {
616
630
  return input.policy.target === "leader" ? stopCommandLeader({ ...stop, leader: child, leaderExit: input.leaderExit }) : stopCommandGroup(stop);
617
631
  }
618
632
  return Promise.resolve().then(() => {
619
- if (child.exitCode === null && child.signalCode === null && !child.kill("SIGKILL"))
633
+ if (child.exitCode !== null || child.signalCode !== null)
634
+ return false;
635
+ if (!child.kill("SIGKILL"))
620
636
  throw new Error("Command leader termination was refused");
637
+ return true;
621
638
  });
622
639
  }
623
640
  async function terminateOwnedGroup(child, closed, cleanupTimeoutMs) {
@@ -677,7 +694,7 @@ function startNativeCommand(input, onSpawn, driver) {
677
694
  });
678
695
  const signalOwned = (force = driver?.force) => {
679
696
  if (!acquired)
680
- return Promise.resolve();
697
+ return Promise.resolve(false);
681
698
  return signalCommandChild(acquired.child, {
682
699
  group: driver?.group !== false,
683
700
  policy: options.stop,
@@ -757,6 +774,7 @@ function startNativeCommand(input, onSpawn, driver) {
757
774
  }
758
775
  }
759
776
  };
777
+ let descendantsStopped = false;
760
778
  const settleLeader = async (event) => {
761
779
  await settle(event);
762
780
  if (controller.signal.aborted)
@@ -764,7 +782,7 @@ function startNativeCommand(input, onSpawn, driver) {
764
782
  if (options.descendants !== "terminate-after-leader" || event.kind !== "exit")
765
783
  return;
766
784
  try {
767
- await signalOwned();
785
+ descendantsStopped = await signalOwned();
768
786
  } catch (cause) {
769
787
  throw commandCleanupError(cause);
770
788
  }
@@ -780,7 +798,12 @@ function startNativeCommand(input, onSpawn, driver) {
780
798
  const status = await raceAbort(closed, controller.signal);
781
799
  if (spawnFailure !== undefined)
782
800
  throw spawnFailure;
783
- return { ...status, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr) };
801
+ return {
802
+ ...status,
803
+ descendantsStopped,
804
+ stdout: Buffer.concat(stdout),
805
+ stderr: Buffer.concat(stderr)
806
+ };
784
807
  } catch (error) {
785
808
  controller.abort(error);
786
809
  await cleanupNativeCommand({
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  stagingPath
3
- } from "./index-cq59agna.js";
3
+ } from "./index-j4zh647a.js";
4
4
  import {
5
5
  closeAfter,
6
6
  closeAfterSync,
@@ -11,7 +11,7 @@ import {
11
11
  } from "./index-b0kddvzw.js";
12
12
  import {
13
13
  writeFileAtomicSync2
14
- } from "./index-ef8gdaq0.js";
14
+ } from "./index-cazts87s.js";
15
15
  import {
16
16
  assertPositiveSafeInteger
17
17
  } from "./index-0s4xebbg.js";
@@ -29,7 +29,15 @@ async function sweepAtomicStaging2(options) {
29
29
  signal?.throwIfAborted();
30
30
  const cutoff = Date.now() - olderThanMs;
31
31
  const removed = [];
32
- for (const name of await readdir(directory)) {
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;
@@ -15,7 +15,7 @@ import {
15
15
  import {
16
16
  writeAllBytes,
17
17
  writeAtomicFileStaged
18
- } from "./index-ef8gdaq0.js";
18
+ } from "./index-cazts87s.js";
19
19
  import {
20
20
  AtomicFilePublicationError2
21
21
  } from "./index-b56nnscm.js";
@@ -7,12 +7,12 @@ import {
7
7
  } from "./index-v5ee8ath.js";
8
8
  import {
9
9
  stagingName
10
- } from "./index-cq59agna.js";
10
+ } from "./index-j4zh647a.js";
11
11
  import {
12
12
  NativeCommandError2,
13
13
  CommandStopped,
14
14
  startNativeCommand
15
- } from "./index-g2vfg7ak.js";
15
+ } from "./index-5c26631f.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;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,MAAM,EAAE,CAAC,CAsBnB"}
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<void>;
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,IAAI,CAAC,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB"}
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<void>;
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,IAAI,CAAC,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;IAKG,MAAM;;;;;;IACA,SAAS;IAoBT,IAAI;EAKb"}
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"}
@@ -6,6 +6,7 @@ export declare function startNativeCommand(input: NativeCommandOptions, onSpawn?
6
6
  result: Promise<{
7
7
  exitCode: number | null;
8
8
  signal: string | null;
9
+ descendantsStopped: boolean;
9
10
  stdout: Uint8Array<ArrayBuffer>;
10
11
  stderr: Uint8Array<ArrayBuffer>;
11
12
  }>;
@@ -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;;;;;;;;;EAyLnC"}
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;;;;;;;;;;EA+LnC"}
@@ -89,12 +89,13 @@ export type ParsedNativeCommandOptions = z.output<typeof NativeCommandOptionsSch
89
89
  declare const NativeCommandResultSchema: z.ZodObject<{
90
90
  exitCode: z.ZodNullable<z.ZodNumber>;
91
91
  signal: z.ZodNullable<z.ZodString>;
92
+ descendantsStopped: z.ZodBoolean;
92
93
  stdout: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
93
94
  stderr: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
94
95
  }, z.core.$strip>;
95
96
  /**
96
- * Exit code, signal and captured stdout and stderr bytes of a finished command; the buffers
97
- * stay empty unless `capture` was set.
97
+ * Exit code, signal, whether descendants were stopped after the leader exited, and captured
98
+ * stdout and stderr bytes of a finished command; the buffers stay empty unless `capture` was set.
98
99
  */
99
100
  export type NativeCommandResult = z.infer<typeof NativeCommandResultSchema>;
100
101
  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;AAE3F,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;gDAoBtB,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;;;;;;;;;;;;;;;;;;;;;;kBAuCvF,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;;;;;iBAK7B,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"}
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;;;;;;;;;;;;;;;;;;;;;;kBA+CzF,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"}
@@ -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<void>;
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<void>;
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;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB5E;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;;;;;;;GAOG;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,IAAI,CAAC,CAoBf;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"}
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<void>;
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;;;;GAIG;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,IAAI,CAAC,CAgBf;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"}
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"}
@@ -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 leaves the group on its own with `setsid` is
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. Descendants that left the group with `setsid` stay out of reach. |
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. A real daemon detaches into its own session (`setsid`, as `daemon(3)` does), is no member
260
- of the group and is unaffected. Declare `'leave'` only for a helper that has to outlive the
261
- command while staying in its group:
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,17 @@ 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. Importing one leaf does not make
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 reads the
302
+ exit code in `onLeaderSettled`, before the pipes drain, and stderr through `onOutput` or `capture`
303
+ with `maxOutputBytes`, and `cleanupTimeoutMs` bounds the drain (`COMMAND_CLEANUP` when a holder
304
+ keeps the pipe past it). A worker whose whole group must stop on abort is the default
305
+ `target: 'group'`. A child that inherits the caller's terminal is outside this capability: every
306
+ command runs in a group of its own, and a background group that reads the terminal is stopped
307
+ by the kernel (`SIGTTIN`), so an interactive re-execution stays a plain spawn in the caller's
308
+ group. Importing one leaf does not make
283
309
  installing the whole npm package smaller; choose a dependency after measuring packed size and its
284
310
  dependency closure.
285
311
 
@@ -407,7 +433,11 @@ package was built. A ready asset carries those verified `bytes` with the publish
407
433
  `sha256`; its Bun-compatible plugin integrates the same bytes with the same lazy native loader.
408
434
  Each call reads and hashes the selected addons, so call it once per build and reuse the result.
409
435
  The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
410
- It must run unbundled from its installed package, never from an application's runtime artifact.
436
+ Calling it must happen in a process that runs it from its installed package: it locates the
437
+ package's `native-assets.json` beside its own file and throws `Native packaging must run from its
438
+ installed Stitchkit package` anywhere else, such as inside an application's runtime artifact.
439
+ Importing the leaf is side-effect free (it loads no addon and reads no file), so a module that
440
+ both builds and is bundled may import it; only the call has to stay in the build step.
411
441
 
412
442
  ```ts
413
443
  import { createNativePackaging } from 'stitchkit/files/packaging'
@@ -434,7 +464,11 @@ for (const asset of native.assets) {
434
464
  ```
435
465
 
436
466
  `entryPath` and `assetPath` are relative paths inside the application's output root, without
437
- absolute paths, traversal or overlapping file/directory paths. `entryPath` is fixed, without
467
+ absolute paths, traversal or overlapping file/directory paths. `assetPath` may contain `[hash]`,
468
+ which becomes the first 16 hex digits of that addon's published SHA256: the digest is known before
469
+ the build, so `addons/darwin-arm64-[hash].node` gives two installed versions of the package
470
+ different companion names, and the loader requires exactly that name. It is the only template an
471
+ addon path accepts. `entryPath` is fixed, without
438
472
  Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
439
473
  and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
440
474
  Choose any application layout; the framework does not prescribe an app directory or installer.
@@ -536,9 +570,9 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
536
570
  | Export | Kind | Purpose |
537
571
  |---|---|---|
538
572
  | `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'`) |
573
+ | `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'`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
540
574
  | `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 |
575
+ | `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
576
  | `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
577
 
544
578
  | `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 leaves the group on its own with `setsid` is
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. Descendants that left the group with `setsid` stay out of reach. |
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. A real daemon detaches into its own session (`setsid`, as `daemon(3)` does), is no member
8402
- of the group and is unaffected. Declare `'leave'` only for a helper that has to outlive the
8403
- command while staying in its group:
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,17 @@ 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. Importing one leaf does not make
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 reads the
8444
+ exit code in `onLeaderSettled`, before the pipes drain, and stderr through `onOutput` or `capture`
8445
+ with `maxOutputBytes`, and `cleanupTimeoutMs` bounds the drain (`COMMAND_CLEANUP` when a holder
8446
+ keeps the pipe past it). A worker whose whole group must stop on abort is the default
8447
+ `target: 'group'`. A child that inherits the caller's terminal is outside this capability: every
8448
+ command runs in a group of its own, and a background group that reads the terminal is stopped
8449
+ by the kernel (`SIGTTIN`), so an interactive re-execution stays a plain spawn in the caller's
8450
+ group. Importing one leaf does not make
8425
8451
  installing the whole npm package smaller; choose a dependency after measuring packed size and its
8426
8452
  dependency closure.
8427
8453
 
@@ -8549,7 +8575,11 @@ package was built. A ready asset carries those verified `bytes` with the publish
8549
8575
  `sha256`; its Bun-compatible plugin integrates the same bytes with the same lazy native loader.
8550
8576
  Each call reads and hashes the selected addons, so call it once per build and reuse the result.
8551
8577
  The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
8552
- It must run unbundled from its installed package, never from an application's runtime artifact.
8578
+ Calling it must happen in a process that runs it from its installed package: it locates the
8579
+ package's `native-assets.json` beside its own file and throws `Native packaging must run from its
8580
+ installed Stitchkit package` anywhere else, such as inside an application's runtime artifact.
8581
+ Importing the leaf is side-effect free (it loads no addon and reads no file), so a module that
8582
+ both builds and is bundled may import it; only the call has to stay in the build step.
8553
8583
 
8554
8584
  ```ts
8555
8585
  import { createNativePackaging } from 'stitchkit/files/packaging'
@@ -8576,7 +8606,11 @@ for (const asset of native.assets) {
8576
8606
  ```
8577
8607
 
8578
8608
  `entryPath` and `assetPath` are relative paths inside the application's output root, without
8579
- absolute paths, traversal or overlapping file/directory paths. `entryPath` is fixed, without
8609
+ absolute paths, traversal or overlapping file/directory paths. `assetPath` may contain `[hash]`,
8610
+ which becomes the first 16 hex digits of that addon's published SHA256: the digest is known before
8611
+ the build, so `addons/darwin-arm64-[hash].node` gives two installed versions of the package
8612
+ different companion names, and the loader requires exactly that name. It is the only template an
8613
+ addon path accepts. `entryPath` is fixed, without
8580
8614
  Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
8581
8615
  and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
8582
8616
  Choose any application layout; the framework does not prescribe an app directory or installer.
@@ -22039,7 +22073,7 @@ available from `stitchkit/contract`.
22039
22073
  | `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
22040
22074
  | `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
22075
  | `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 |
22076
+ | `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
22077
  | `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
22078
  | `ExclusiveLockError` | class | `LOCK_TIMEOUT` naming the resource and its holder, or `LOCK_ABORTED` with the signal's reason |
22045
22079
  | `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 +22642,9 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
22608
22642
  | Export | Kind | Purpose |
22609
22643
  |---|---|---|
22610
22644
  | `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'`) |
22645
+ | `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'`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
22612
22646
  | `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 |
22647
+ | `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
22648
  | `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
22649
 
22616
22650
  | `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 +22660,8 @@ It resolves the installed package's target/version, reads each selected addon on
22626
22660
  against the `size` and `sha256` published in the package's `native-assets.json` (`formatVersion: 2`,
22627
22661
  written when Stitchkit is built). Call it once per build and reuse the result.
22628
22662
  `platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs;
22663
+ `assetPath` accepts one template, `[hash]`, replaced by the first 16 hex digits of the addon's
22664
+ published SHA256;
22629
22665
  `platform` is the closed set `'darwin'`, and another name is a type error and a schema refusal.
22630
22666
  The result is `ready` with verified assets and a structural Bun plugin, or `unsupported` /
22631
22667
  `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) 32.8 KB
60
- - **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 2.7 KB
59
+ - **stitchkit/process**: [llms/process.txt](llms/process.txt) 35.6 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.105.0",
3
+ "version": "0.105.1",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",