@vibe-agent-toolkit/utils 0.2.0-rc.11 → 0.2.0-rc.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/README.md +111 -9
  2. package/dist/archive/archive.d.ts +129 -0
  3. package/dist/archive/archive.d.ts.map +1 -0
  4. package/dist/archive/archive.js +223 -0
  5. package/dist/archive/archive.js.map +1 -0
  6. package/dist/archive/entries.d.ts +132 -0
  7. package/dist/archive/entries.d.ts.map +1 -0
  8. package/dist/archive/entries.js +223 -0
  9. package/dist/archive/entries.js.map +1 -0
  10. package/dist/archive/limits.d.ts +39 -0
  11. package/dist/archive/limits.d.ts.map +1 -0
  12. package/dist/archive/limits.js +59 -0
  13. package/dist/archive/limits.js.map +1 -0
  14. package/dist/archive/records.d.ts +14 -0
  15. package/dist/archive/records.d.ts.map +1 -0
  16. package/dist/archive/records.js +3 -0
  17. package/dist/archive/records.js.map +1 -0
  18. package/dist/archive/tar-reader.d.ts +18 -0
  19. package/dist/archive/tar-reader.d.ts.map +1 -0
  20. package/dist/archive/tar-reader.js +173 -0
  21. package/dist/archive/tar-reader.js.map +1 -0
  22. package/dist/archive/zip-reader.d.ts +76 -0
  23. package/dist/archive/zip-reader.d.ts.map +1 -0
  24. package/dist/archive/zip-reader.js +376 -0
  25. package/dist/archive/zip-reader.js.map +1 -0
  26. package/dist/archive.d.ts +14 -0
  27. package/dist/archive.d.ts.map +1 -0
  28. package/dist/archive.js +12 -0
  29. package/dist/archive.js.map +1 -0
  30. package/dist/asset-reference.d.ts +17 -1
  31. package/dist/asset-reference.d.ts.map +1 -1
  32. package/dist/asset-reference.js +31 -7
  33. package/dist/asset-reference.js.map +1 -1
  34. package/dist/dirent-kind.d.ts +13 -40
  35. package/dist/dirent-kind.d.ts.map +1 -1
  36. package/dist/dirent-kind.js +21 -69
  37. package/dist/dirent-kind.js.map +1 -1
  38. package/dist/entrypoint.d.ts +2 -2
  39. package/dist/entrypoint.d.ts.map +1 -1
  40. package/dist/entrypoint.js +4 -3
  41. package/dist/entrypoint.js.map +1 -1
  42. package/dist/env-flag.d.ts +2 -0
  43. package/dist/env-flag.d.ts.map +1 -1
  44. package/dist/env-flag.js +2 -0
  45. package/dist/env-flag.js.map +1 -1
  46. package/dist/errors/confirmed-absent.d.ts +41 -0
  47. package/dist/errors/confirmed-absent.d.ts.map +1 -0
  48. package/dist/errors/confirmed-absent.js +43 -0
  49. package/dist/errors/confirmed-absent.js.map +1 -0
  50. package/dist/errors/errno-table.d.ts +177 -0
  51. package/dist/errors/errno-table.d.ts.map +1 -0
  52. package/dist/errors/errno-table.js +343 -0
  53. package/dist/errors/errno-table.js.map +1 -0
  54. package/dist/errors/fs-boundary.d.ts +36 -0
  55. package/dist/errors/fs-boundary.d.ts.map +1 -0
  56. package/dist/errors/fs-boundary.js +121 -0
  57. package/dist/errors/fs-boundary.js.map +1 -0
  58. package/dist/errors/fs-fault.d.ts +176 -0
  59. package/dist/errors/fs-fault.d.ts.map +1 -0
  60. package/dist/errors/fs-fault.js +215 -0
  61. package/dist/errors/fs-fault.js.map +1 -0
  62. package/dist/errors/git-errno.d.ts +13 -0
  63. package/dist/errors/git-errno.d.ts.map +1 -0
  64. package/dist/errors/git-errno.js +45 -0
  65. package/dist/errors/git-errno.js.map +1 -0
  66. package/dist/errors/path-present.d.ts +72 -0
  67. package/dist/errors/path-present.d.ts.map +1 -0
  68. package/dist/errors/path-present.js +110 -0
  69. package/dist/errors/path-present.js.map +1 -0
  70. package/dist/errors/rag-index-empty.d.ts +16 -0
  71. package/dist/errors/rag-index-empty.d.ts.map +1 -0
  72. package/dist/errors/rag-index-empty.js +16 -0
  73. package/dist/errors/rag-index-empty.js.map +1 -0
  74. package/dist/errors/suppressed-faults.d.ts +34 -0
  75. package/dist/errors/suppressed-faults.d.ts.map +1 -0
  76. package/dist/errors/suppressed-faults.js +66 -0
  77. package/dist/errors/suppressed-faults.js.map +1 -0
  78. package/dist/file-crawler.d.ts +13 -18
  79. package/dist/file-crawler.d.ts.map +1 -1
  80. package/dist/file-crawler.js +140 -24
  81. package/dist/file-crawler.js.map +1 -1
  82. package/dist/fs-utils.d.ts +75 -50
  83. package/dist/fs-utils.d.ts.map +1 -1
  84. package/dist/fs-utils.js +148 -158
  85. package/dist/fs-utils.js.map +1 -1
  86. package/dist/fs.d.ts +4 -4
  87. package/dist/fs.d.ts.map +1 -1
  88. package/dist/fs.js +4 -4
  89. package/dist/fs.js.map +1 -1
  90. package/dist/git-snapshot.d.ts +25 -17
  91. package/dist/git-snapshot.d.ts.map +1 -1
  92. package/dist/git-snapshot.js +65 -11
  93. package/dist/git-snapshot.js.map +1 -1
  94. package/dist/git-tracker.d.ts +11 -3
  95. package/dist/git-tracker.d.ts.map +1 -1
  96. package/dist/git-tracker.js +29 -6
  97. package/dist/git-tracker.js.map +1 -1
  98. package/dist/git-utils.d.ts.map +1 -1
  99. package/dist/git-utils.js +7 -35
  100. package/dist/git-utils.js.map +1 -1
  101. package/dist/git.d.ts +1 -1
  102. package/dist/git.d.ts.map +1 -1
  103. package/dist/git.js +1 -1
  104. package/dist/git.js.map +1 -1
  105. package/dist/gitignore-checker.d.ts.map +1 -1
  106. package/dist/gitignore-checker.js +5 -4
  107. package/dist/gitignore-checker.js.map +1 -1
  108. package/dist/in-order.d.ts +93 -0
  109. package/dist/in-order.d.ts.map +1 -0
  110. package/dist/in-order.js +165 -0
  111. package/dist/in-order.js.map +1 -0
  112. package/dist/index.d.ts +20 -4
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +30 -4
  115. package/dist/index.js.map +1 -1
  116. package/dist/kill-process-tree.d.ts +43 -0
  117. package/dist/kill-process-tree.d.ts.map +1 -0
  118. package/dist/kill-process-tree.js +48 -0
  119. package/dist/kill-process-tree.js.map +1 -0
  120. package/dist/link-following.d.ts +115 -0
  121. package/dist/link-following.d.ts.map +1 -0
  122. package/dist/link-following.js +283 -0
  123. package/dist/link-following.js.map +1 -0
  124. package/dist/listing-refusal.d.ts +44 -0
  125. package/dist/listing-refusal.d.ts.map +1 -1
  126. package/dist/listing-refusal.js +79 -2
  127. package/dist/listing-refusal.js.map +1 -1
  128. package/dist/name-fold.d.ts +25 -0
  129. package/dist/name-fold.d.ts.map +1 -0
  130. package/dist/name-fold.js +40 -0
  131. package/dist/name-fold.js.map +1 -0
  132. package/dist/path-containment.d.ts +3 -1
  133. package/dist/path-containment.d.ts.map +1 -1
  134. package/dist/path-containment.js +13 -7
  135. package/dist/path-containment.js.map +1 -1
  136. package/dist/path-utils.d.ts +14 -17
  137. package/dist/path-utils.d.ts.map +1 -1
  138. package/dist/path-utils.js +37 -55
  139. package/dist/path-utils.js.map +1 -1
  140. package/dist/process.d.ts +2 -0
  141. package/dist/process.d.ts.map +1 -1
  142. package/dist/process.js +2 -0
  143. package/dist/process.js.map +1 -1
  144. package/dist/project-utils.d.ts.map +1 -1
  145. package/dist/project-utils.js +8 -9
  146. package/dist/project-utils.js.map +1 -1
  147. package/dist/safe-exec.d.ts +6 -2
  148. package/dist/safe-exec.d.ts.map +1 -1
  149. package/dist/safe-exec.js +1 -1
  150. package/dist/safe-exec.js.map +1 -1
  151. package/dist/skill-targets.d.ts.map +1 -1
  152. package/dist/skill-targets.js +3 -1
  153. package/dist/skill-targets.js.map +1 -1
  154. package/dist/skill-test/spawn-claude.d.ts +0 -15
  155. package/dist/skill-test/spawn-claude.d.ts.map +1 -1
  156. package/dist/skill-test/spawn-claude.js +11 -50
  157. package/dist/skill-test/spawn-claude.js.map +1 -1
  158. package/dist/special-file.d.ts +18 -0
  159. package/dist/special-file.d.ts.map +1 -0
  160. package/dist/special-file.js +21 -0
  161. package/dist/special-file.js.map +1 -0
  162. package/dist/terminating-signals.d.ts +129 -0
  163. package/dist/terminating-signals.d.ts.map +1 -0
  164. package/dist/terminating-signals.js +210 -0
  165. package/dist/terminating-signals.js.map +1 -0
  166. package/dist/test-helpers.d.ts +9 -5
  167. package/dist/test-helpers.d.ts.map +1 -1
  168. package/dist/test-helpers.js +18 -21
  169. package/dist/test-helpers.js.map +1 -1
  170. package/dist/test-scratch-guard.d.ts +34 -0
  171. package/dist/test-scratch-guard.d.ts.map +1 -0
  172. package/dist/test-scratch-guard.js +45 -0
  173. package/dist/test-scratch-guard.js.map +1 -0
  174. package/dist/testing/archive-fixtures.d.ts +147 -0
  175. package/dist/testing/archive-fixtures.d.ts.map +1 -0
  176. package/dist/testing/archive-fixtures.js +449 -0
  177. package/dist/testing/archive-fixtures.js.map +1 -0
  178. package/dist/testing/executables.d.ts +6 -0
  179. package/dist/testing/executables.d.ts.map +1 -1
  180. package/dist/testing/executables.js +20 -9
  181. package/dist/testing/executables.js.map +1 -1
  182. package/dist/testing/fault-fs-preload.d.ts +16 -0
  183. package/dist/testing/fault-fs-preload.d.ts.map +1 -0
  184. package/dist/testing/fault-fs-preload.js +22 -0
  185. package/dist/testing/fault-fs-preload.js.map +1 -0
  186. package/dist/testing/fault-fs.d.ts +115 -0
  187. package/dist/testing/fault-fs.d.ts.map +1 -0
  188. package/dist/testing/fault-fs.js +411 -0
  189. package/dist/testing/fault-fs.js.map +1 -0
  190. package/dist/testing/fault-spec.d.ts +83 -0
  191. package/dist/testing/fault-spec.d.ts.map +1 -0
  192. package/dist/testing/fault-spec.js +132 -0
  193. package/dist/testing/fault-spec.js.map +1 -0
  194. package/dist/testing/hostile-tree.d.ts.map +1 -1
  195. package/dist/testing/hostile-tree.js +3 -4
  196. package/dist/testing/hostile-tree.js.map +1 -1
  197. package/dist/testing/platform-gates.d.ts +8 -0
  198. package/dist/testing/platform-gates.d.ts.map +1 -1
  199. package/dist/testing/platform-gates.js +24 -0
  200. package/dist/testing/platform-gates.js.map +1 -1
  201. package/dist/testing/temp-dir.d.ts +31 -0
  202. package/dist/testing/temp-dir.d.ts.map +1 -1
  203. package/dist/testing/temp-dir.js +63 -0
  204. package/dist/testing/temp-dir.js.map +1 -1
  205. package/dist/testing/tier-timeout.d.ts +34 -0
  206. package/dist/testing/tier-timeout.d.ts.map +1 -0
  207. package/dist/testing/tier-timeout.js +45 -0
  208. package/dist/testing/tier-timeout.js.map +1 -0
  209. package/dist/testing/tree-snapshot.d.ts +71 -0
  210. package/dist/testing/tree-snapshot.d.ts.map +1 -0
  211. package/dist/testing/tree-snapshot.js +163 -0
  212. package/dist/testing/tree-snapshot.js.map +1 -0
  213. package/dist/testing.d.ts +6 -0
  214. package/dist/testing.d.ts.map +1 -1
  215. package/dist/testing.js +8 -1
  216. package/dist/testing.js.map +1 -1
  217. package/dist/text-file.d.ts +14 -1
  218. package/dist/text-file.d.ts.map +1 -1
  219. package/dist/text-file.js +41 -8
  220. package/dist/text-file.js.map +1 -1
  221. package/dist/timing-dump.d.ts.map +1 -1
  222. package/dist/timing-dump.js +2 -10
  223. package/dist/timing-dump.js.map +1 -1
  224. package/dist/tree-change/apply-decisions.d.ts +101 -0
  225. package/dist/tree-change/apply-decisions.d.ts.map +1 -0
  226. package/dist/tree-change/apply-decisions.js +139 -0
  227. package/dist/tree-change/apply-decisions.js.map +1 -0
  228. package/dist/tree-change/apply.d.ts +98 -0
  229. package/dist/tree-change/apply.d.ts.map +1 -0
  230. package/dist/tree-change/apply.js +364 -0
  231. package/dist/tree-change/apply.js.map +1 -0
  232. package/dist/tree-change/copy-decisions.d.ts +58 -0
  233. package/dist/tree-change/copy-decisions.d.ts.map +1 -0
  234. package/dist/tree-change/copy-decisions.js +66 -0
  235. package/dist/tree-change/copy-decisions.js.map +1 -0
  236. package/dist/tree-change/copy-tree.d.ts +125 -0
  237. package/dist/tree-change/copy-tree.d.ts.map +1 -0
  238. package/dist/tree-change/copy-tree.js +279 -0
  239. package/dist/tree-change/copy-tree.js.map +1 -0
  240. package/dist/tree-change/files.d.ts +167 -0
  241. package/dist/tree-change/files.d.ts.map +1 -0
  242. package/dist/tree-change/files.js +423 -0
  243. package/dist/tree-change/files.js.map +1 -0
  244. package/dist/tree-change/followed-walk.d.ts +54 -0
  245. package/dist/tree-change/followed-walk.d.ts.map +1 -0
  246. package/dist/tree-change/followed-walk.js +81 -0
  247. package/dist/tree-change/followed-walk.js.map +1 -0
  248. package/dist/tree-change/host-chain.d.ts +48 -0
  249. package/dist/tree-change/host-chain.d.ts.map +1 -0
  250. package/dist/tree-change/host-chain.js +85 -0
  251. package/dist/tree-change/host-chain.js.map +1 -0
  252. package/dist/tree-change/identity-compare.d.ts +61 -0
  253. package/dist/tree-change/identity-compare.d.ts.map +1 -0
  254. package/dist/tree-change/identity-compare.js +115 -0
  255. package/dist/tree-change/identity-compare.js.map +1 -0
  256. package/dist/tree-change/identity.d.ts +62 -0
  257. package/dist/tree-change/identity.d.ts.map +1 -0
  258. package/dist/tree-change/identity.js +152 -0
  259. package/dist/tree-change/identity.js.map +1 -0
  260. package/dist/tree-change/index.d.ts +24 -0
  261. package/dist/tree-change/index.d.ts.map +1 -0
  262. package/dist/tree-change/index.js +14 -0
  263. package/dist/tree-change/index.js.map +1 -0
  264. package/dist/tree-change/link-chain.d.ts +180 -0
  265. package/dist/tree-change/link-chain.d.ts.map +1 -0
  266. package/dist/tree-change/link-chain.js +377 -0
  267. package/dist/tree-change/link-chain.js.map +1 -0
  268. package/dist/tree-change/link-containment.d.ts +105 -0
  269. package/dist/tree-change/link-containment.d.ts.map +1 -0
  270. package/dist/tree-change/link-containment.js +196 -0
  271. package/dist/tree-change/link-containment.js.map +1 -0
  272. package/dist/tree-change/link-walk.d.ts +49 -0
  273. package/dist/tree-change/link-walk.d.ts.map +1 -0
  274. package/dist/tree-change/link-walk.js +69 -0
  275. package/dist/tree-change/link-walk.js.map +1 -0
  276. package/dist/tree-change/plan.d.ts +206 -0
  277. package/dist/tree-change/plan.d.ts.map +1 -0
  278. package/dist/tree-change/plan.js +320 -0
  279. package/dist/tree-change/plan.js.map +1 -0
  280. package/dist/tree-change/readable-tree.d.ts +44 -0
  281. package/dist/tree-change/readable-tree.d.ts.map +1 -0
  282. package/dist/tree-change/readable-tree.js +52 -0
  283. package/dist/tree-change/readable-tree.js.map +1 -0
  284. package/dist/tree-change/rollback-error.d.ts +29 -0
  285. package/dist/tree-change/rollback-error.d.ts.map +1 -0
  286. package/dist/tree-change/rollback-error.js +31 -0
  287. package/dist/tree-change/rollback-error.js.map +1 -0
  288. package/dist/tree-change/staging-names.d.ts +27 -0
  289. package/dist/tree-change/staging-names.d.ts.map +1 -0
  290. package/dist/tree-change/staging-names.js +40 -0
  291. package/dist/tree-change/staging-names.js.map +1 -0
  292. package/dist/tree-change/tree-walk.d.ts +81 -0
  293. package/dist/tree-change/tree-walk.d.ts.map +1 -0
  294. package/dist/tree-change/tree-walk.js +212 -0
  295. package/dist/tree-change/tree-walk.js.map +1 -0
  296. package/dist/win32-namespace.d.ts +24 -0
  297. package/dist/win32-namespace.d.ts.map +1 -0
  298. package/dist/win32-namespace.js +31 -0
  299. package/dist/win32-namespace.js.map +1 -0
  300. package/dist/yaml/surgical-yaml.d.ts +12 -3
  301. package/dist/yaml/surgical-yaml.d.ts.map +1 -1
  302. package/dist/yaml/surgical-yaml.js +16 -6
  303. package/dist/yaml/surgical-yaml.js.map +1 -1
  304. package/eslint/README.md +12 -4
  305. package/eslint/rules/fs-binding-scan.cjs +278 -0
  306. package/eslint/rules/no-adhoc-errno.cjs +364 -0
  307. package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
  308. package/eslint/rules/no-blind-catch.cjs +1 -1
  309. package/eslint/rules/no-destructive-fs.cjs +359 -0
  310. package/eslint/rules/no-existssync.cjs +70 -0
  311. package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
  312. package/eslint/rules/no-link-following-fs.cjs +68 -0
  313. package/eslint/rules/no-literal-process-exit.cjs +3 -16
  314. package/eslint/rules/no-literal-test-timeout.cjs +214 -0
  315. package/eslint/rules/no-raw-signal-listener.cjs +224 -0
  316. package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
  317. package/package.json +10 -2
  318. package/dist/errors/errno.d.ts +0 -51
  319. package/dist/errors/errno.d.ts.map +0 -1
  320. package/dist/errors/errno.js +0 -112
  321. package/dist/errors/errno.js.map +0 -1
  322. package/eslint/rules/no-fs-promises-cp.cjs +0 -43
@@ -46,21 +46,6 @@ export interface SpawnResult {
46
46
  timedOut: boolean;
47
47
  stalled: boolean;
48
48
  }
49
- /**
50
- * SIGKILL the child AND every process in its group. The child is spawned
51
- * `detached` (POSIX) so it leads a new process group; killing the negated pid
52
- * reaps backgrounded grandchildren (e.g. a skill that runs `nohup … &`) and
53
- * orphaned MCP subprocesses that a direct `child.kill()` would leave running.
54
- *
55
- * - Guards an undefined pid (child never spawned — nothing to kill).
56
- * - Absorbs the throw that means the group is already gone (see
57
- * {@link isProcessGroupGone}); anything else stays loud.
58
- * - Windows has no POSIX process groups: fall back to `taskkill /T /F`, which
59
- * terminates the whole process tree.
60
- */
61
- export declare function killProcessTree(child: {
62
- pid?: number | undefined;
63
- }): void;
64
49
  /**
65
50
  * SIGKILL every currently in-flight `claude` child (and its process tree, via
66
51
  * {@link killProcessTree}) and clear the registry. Idempotent — safe to call
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,EAAE,CAoBlE;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAAG,IAAI,CAkBzE;AAgCD;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC3D;;;;;;;;;OASG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CA8G1F"}
1
+ {"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,EAAE,CAoBlE;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AA4BD;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC3D;;;;;;;;;OASG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CA8G1F"}
@@ -1,5 +1,5 @@
1
- import { spawnSync } from 'node:child_process';
2
1
  import { Readable } from 'node:stream';
2
+ import { killProcessTree } from '../kill-process-tree.js';
3
3
  import { spawnHardened } from '../spawn-hardened.js';
4
4
  /**
5
5
  * Pure assembly of the headless `claude -p` argv (spec §9). The prompt is never
@@ -61,51 +61,12 @@ export function assembleClaudeArgs(opts) {
61
61
  return args;
62
62
  }
63
63
  /**
64
- * SIGKILL the child AND every process in its group. The child is spawned
65
- * `detached` (POSIX) so it leads a new process group; killing the negated pid
66
- * reaps backgrounded grandchildren (e.g. a skill that runs `nohup … &`) and
67
- * orphaned MCP subprocesses that a direct `child.kill()` would leave running.
68
- *
69
- * - Guards an undefined pid (child never spawned — nothing to kill).
70
- * - Absorbs the throw that means the group is already gone (see
71
- * {@link isProcessGroupGone}); anything else stays loud.
72
- * - Windows has no POSIX process groups: fall back to `taskkill /T /F`, which
73
- * terminates the whole process tree.
74
- */
75
- export function killProcessTree(child) {
76
- const { pid } = child;
77
- if (pid === undefined)
78
- return;
79
- try {
80
- if (process.platform === 'win32') {
81
- // eslint-disable-next-line sonarjs/no-os-command-from-path -- taskkill is a fixed Windows system command (no PATH-injection surface); it is the only POSIX-process-group-free way to terminate the child's tree.
82
- spawnSync('taskkill', ['/pid', String(pid), '/T', '/F']);
83
- }
84
- else {
85
- // Negated pid → deliver the signal to the whole process group.
86
- process.kill(-pid, 'SIGKILL');
87
- }
88
- }
89
- catch (error) {
90
- // A reaper that says nothing about a tree it did not reap is the
91
- // orphan-billing defect the registry exists to prevent, so only "already
92
- // gone" is absorbed. A bug here — the one other thing `process.kill` on our
93
- // own child's group can throw — stays loud.
94
- if (!isProcessGroupGone(error))
95
- throw error;
96
- }
97
- }
98
- /**
99
- * Whether `process.kill(-pid)` failed because the group is already gone.
100
- *
101
- * `ESRCH`: no such process group. `EPERM`: the group id has been recycled to a
102
- * process this uid may not signal — which, for a group WE created and would
103
- * have permission over, also means every process of ours in it has exited.
104
- * Both are the outcome the kill was after.
64
+ * How a `claude` child is killed: with every process in its group. It is spawned `detached` (POSIX),
65
+ * so it leads a new process group, and the group kill reaps backgrounded grandchildren (a skill that
66
+ * runs `nohup … &`) and orphaned MCP subprocesses that a direct `child.kill()` would leave running;
67
+ * on Windows `taskkill /T /F` takes the tree.
105
68
  */
106
- function isProcessGroupGone(error) {
107
- return error instanceof Error && 'code' in error && (error.code === 'ESRCH' || error.code === 'EPERM');
108
- }
69
+ const CLAUDE_TREE = { groupLeader: true, throughChild: false };
109
70
  /**
110
71
  * Registry of currently in-flight `claude` child processes, keyed by the
111
72
  * `ChildProcess` object itself. A child is registered immediately after
@@ -131,7 +92,7 @@ const activeClaudeChildren = new Set();
131
92
  */
132
93
  export function killAllActiveClaudeChildren() {
133
94
  for (const child of activeClaudeChildren) {
134
- killProcessTree(child);
95
+ killProcessTree(child, CLAUDE_TREE);
135
96
  }
136
97
  activeClaudeChildren.clear();
137
98
  }
@@ -193,7 +154,7 @@ export async function spawnHeadlessClaude(opts) {
193
154
  const promptStream = Readable.from([opts.prompt], { objectMode: false });
194
155
  const wallTimer = setTimeout(() => {
195
156
  timedOut = true;
196
- killProcessTree(child);
157
+ killProcessTree(child, CLAUDE_TREE);
197
158
  }, opts.timeoutMs);
198
159
  // Stall watchdog: reset on every stdout/stderr chunk; fire if silent for stallMs.
199
160
  let stallTimer;
@@ -204,7 +165,7 @@ export async function spawnHeadlessClaude(opts) {
204
165
  clearTimeout(stallTimer);
205
166
  stallTimer = setTimeout(() => {
206
167
  stalled = true;
207
- killProcessTree(child);
168
+ killProcessTree(child, CLAUDE_TREE);
208
169
  }, stallMs);
209
170
  };
210
171
  resetStallTimer();
@@ -226,7 +187,7 @@ export async function spawnHeadlessClaude(opts) {
226
187
  };
227
188
  promptStream.on('error', err => {
228
189
  clearAllTimers();
229
- killProcessTree(child);
190
+ killProcessTree(child, CLAUDE_TREE);
230
191
  reject(err);
231
192
  });
232
193
  // Swallow EPIPE: if the child exits before consuming all stdin, the write
@@ -242,7 +203,7 @@ export async function spawnHeadlessClaude(opts) {
242
203
  // ever fires while the process is actually alive we would otherwise drop it
243
204
  // from the reap set without killing it (the exact orphan this file guards
244
205
  // against). killProcessTree no-ops on an undefined pid (never-spawned case).
245
- child.on('error', err => { clearAllTimers(); killProcessTree(child); reject(err); });
206
+ child.on('error', err => { clearAllTimers(); killProcessTree(child, CLAUDE_TREE); reject(err); });
246
207
  child.on('close', code => { clearAllTimers(); resolve({ status: code ?? -1, timedOut, stalled }); });
247
208
  });
248
209
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The one special-file policy: what a read does with an entry that is not a
3
+ * regular file. Internal, and off the barrel: `openForReading` and the
4
+ * tree-change walk both judge by it, so a named pipe is refused the same way
5
+ * whether one file is read or a whole tree is.
6
+ */
7
+ /**
8
+ * Refuse, before reading a byte, something that is not a regular file: a named
9
+ * pipe, socket or device has no content to read, only a stream that may never
10
+ * end. It is an `EFTYPE` errno ("inappropriate file type"), so every caller's
11
+ * own environmental-refusal convention codes it. A directory is left to the
12
+ * read, which raises the OS's own `EISDIR`.
13
+ */
14
+ export declare function refuseSpecialFile(stats: {
15
+ isFile(): boolean;
16
+ isDirectory(): boolean;
17
+ }, filePath: string): void;
18
+ //# sourceMappingURL=special-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-file.d.ts","sourceRoot":"","sources":["../src/special-file.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,MAAM,IAAI,OAAO,CAAC;IAAC,WAAW,IAAI,OAAO,CAAA;CAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAM9G"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The one special-file policy: what a read does with an entry that is not a
3
+ * regular file. Internal, and off the barrel: `openForReading` and the
4
+ * tree-change walk both judge by it, so a named pipe is refused the same way
5
+ * whether one file is read or a whole tree is.
6
+ */
7
+ /** An OS errno (BSD's "inappropriate file type"), not a VAT code: the shape every errno predicate reads. */
8
+ const NOT_A_REGULAR_FILE_ERRNO = 'EFTYPE';
9
+ /**
10
+ * Refuse, before reading a byte, something that is not a regular file: a named
11
+ * pipe, socket or device has no content to read, only a stream that may never
12
+ * end. It is an `EFTYPE` errno ("inappropriate file type"), so every caller's
13
+ * own environmental-refusal convention codes it. A directory is left to the
14
+ * read, which raises the OS's own `EISDIR`.
15
+ */
16
+ export function refuseSpecialFile(stats, filePath) {
17
+ if (stats.isFile() || stats.isDirectory())
18
+ return;
19
+ throw Object.assign(new Error(`${NOT_A_REGULAR_FILE_ERRNO}: not a regular file (a named pipe, socket or device), refused unread: ${filePath}`), { code: NOT_A_REGULAR_FILE_ERRNO, path: filePath, syscall: 'open' });
20
+ }
21
+ //# sourceMappingURL=special-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-file.js","sourceRoot":"","sources":["../src/special-file.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4GAA4G;AAC5G,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAoD,EAAE,QAAgB;IACtG,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE;QAAE,OAAO;IAClD,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CAAC,GAAG,wBAAwB,0EAA0E,QAAQ,EAAE,CAAC,EAC1H,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CACpE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * The signals a process must not die of before it has cleaned up, the ONE way a handler for them is
3
+ * installed ({@link handleTerminatingSignals}), and how the process then dies of the signal itself.
4
+ *
5
+ * 🚨 Every terminating-signal handler in VAT's product code goes through
6
+ * {@link handleTerminatingSignals}; `local/no-raw-signal-listener` refuses any other way of adding
7
+ * or removing a listener for one (its README lists every shape it catches). The class it closes: a handler
8
+ * that removed itself on the first signal and started an ASYNCHRONOUS cleanup, so a second signal —
9
+ * which a terminal Ctrl-C under the `vat` wrapper delivers (the terminal signals the whole
10
+ * foreground group, and the wrapper forwards) — met the default disposition and killed the process
11
+ * half-way through that cleanup, leaking the clone or the lock it was removing.
12
+ */
13
+ /**
14
+ * The signals that end a process by default disposition — `kill <pid>`, `kill -INT <pid>`,
15
+ * `timeout(1)`, a CI runner stopping or cancelling a step, a closed terminal, Ctrl-C, `Ctrl-\`,
16
+ * Ctrl-Break.
17
+ *
18
+ * SIGQUIT is here because its default disposition terminates exactly as the others do (plus a core
19
+ * dump where the system allows one). SIGBREAK is Windows' Ctrl-Break, which Node delivers there and
20
+ * which ends a process with no handler. Each exists on one platform only: a listener for the other
21
+ * platform's registers an ordinary event that never fires, which is harmless — so ONE list serves
22
+ * both. SIGKILL is not here because nothing can handle it.
23
+ *
24
+ * ⚠️ On Windows only SIGINT, SIGBREAK and SIGHUP are ever DELIVERED, and only as console events
25
+ * (Ctrl-C, Ctrl-Break, the console closing) — which the console sends to EVERY process attached to
26
+ * it. An outside kill there (`taskkill /F`, `process.kill(pid, <any>)`, `child.kill()`) is
27
+ * `TerminateProcess`: no handler runs at all.
28
+ */
29
+ export declare const TERMINATING_SIGNALS: readonly ["SIGTERM", "SIGHUP", "SIGINT", "SIGQUIT", "SIGBREAK"];
30
+ /** One of {@link TERMINATING_SIGNALS}. */
31
+ export type TerminatingSignal = (typeof TERMINATING_SIGNALS)[number];
32
+ /**
33
+ * Whether `signal` is one of {@link TERMINATING_SIGNALS}.
34
+ *
35
+ * @param signal - A signal name, as a child's `exit` reports it
36
+ * @returns True for a terminating signal
37
+ */
38
+ export declare function isTerminatingSignal(signal: string): signal is TerminatingSignal;
39
+ /**
40
+ * How long after the first terminating signal a repeat is still the SAME interruption, and is
41
+ * swallowed rather than acted on.
42
+ *
43
+ * A terminal's Ctrl-C under the `vat` wrapper arrives twice — the foreground group's, and the
44
+ * wrapper's forward — milliseconds apart. A repeat later than this is an operator (or a supervisor)
45
+ * asking again because the first did not end the process soon enough: it ends the process at once,
46
+ * whatever cleanup is still running.
47
+ */
48
+ export declare const REPEAT_SIGNAL_WINDOW_MS = 500;
49
+ /**
50
+ * End this process OF `signal`, as its default disposition would have — so a shell and a CI runner
51
+ * read the status they expect (128 + n) — with an exit-code fallback.
52
+ *
53
+ * The {@link handleTerminatingSignals} listener is removed first, so the raise reaches the default
54
+ * disposition rather than a guard still swallowing repeats. Only that listener: nothing else in VAT
55
+ * installs one (`local/no-raw-signal-listener`), and a host's own — a test runner's — is not this
56
+ * module's to strip. A process one of those keeps alive takes the fallback below.
57
+ *
58
+ * The wait is synchronous so nothing else runs meanwhile: output published after the operator asked
59
+ * this process to end would read as the run's verdict. A process still here after
60
+ * {@link RERAISE_GRACE_MS} survived its own raise (PID 1) and exits `fallbackExitCode`. Windows is
61
+ * never sent the raise: it cannot SEND most of these signals, and the ones it can terminate without
62
+ * a signal status to preserve — so on Windows this always exits `fallbackExitCode`.
63
+ *
64
+ * @param signal - The signal this process received
65
+ * @param fallbackExitCode - The exit code when the raise cannot end this process
66
+ */
67
+ export declare function reRaiseSignal(signal: NodeJS.Signals, fallbackExitCode: number): never;
68
+ /**
69
+ * What a guard undoes before the process ends: given the signal, and an `AbortSignal` that aborts
70
+ * — synchronously, just before the process ends — when a later repeat forces the ending while this
71
+ * cleanup is still running. Most cleanups have nothing to do then; one that started something which
72
+ * must not outlive this process (the `vat` wrapper's CLI) kills it there.
73
+ */
74
+ export type TerminatingSignalCleanup = (signal: TerminatingSignal, forced: AbortSignal) => unknown;
75
+ /** What {@link handleTerminatingSignals} hands back. */
76
+ export interface TerminatingSignalGuard {
77
+ /**
78
+ * The guarded work is over without a signal: stop guarding it.
79
+ *
80
+ * Before any signal arrived, this removes the guard — and the process's listeners with the last
81
+ * one. Once a signal HAS arrived it changes nothing: the process still ends of that signal when
82
+ * every cleanup has settled (or a later repeat forces it). A caller that must end the process its
83
+ * own way meanwhile does so with {@link reRaiseSignal} or `process.exit`, both of which pass every
84
+ * guard by.
85
+ *
86
+ * @returns True when the guard was removed; false when a signal is already ending the process
87
+ */
88
+ release(): boolean;
89
+ /**
90
+ * {@link release} for work that would go on to report or exit its own way: resolves once the
91
+ * guard is removed — and NEVER settles when a signal is already ending the process, so that work
92
+ * neither publishes nor exits under it. Call it only once this guard's own cleanup can no longer
93
+ * be in progress (the caller has run or awaited it), so the ending it yields to is not waiting
94
+ * on the caller.
95
+ *
96
+ * @returns A promise that resolves once the guard is removed
97
+ */
98
+ releaseOrAwaitEnding(): Promise<void>;
99
+ }
100
+ /**
101
+ * Guard this process's terminating signals while `cleanup` has something to undo: on the FIRST
102
+ * {@link TERMINATING_SIGNALS} member, run it — and every other live guard's — then die of that
103
+ * signal.
104
+ *
105
+ * - **A repeat within {@link REPEAT_SIGNAL_WINDOW_MS} is swallowed.** A terminal Ctrl-C under the
106
+ * `vat` wrapper arrives twice (the group's, and the wrapper's forward); the second must not meet
107
+ * the default disposition half-way through an asynchronous cleanup.
108
+ * - **A repeat after it ends the process at once**, of the repeat, the cleanups abandoned (each
109
+ * cleanup's `forced` signal aborts first): a second Ctrl-C or kill still ends a cleanup that hung.
110
+ * - **Synchronous or asynchronous.** A `cleanup` that returns a promise is awaited, whichever way it
111
+ * settles — it reports its own failures — and a ref'd timer holds the process open meanwhile, so
112
+ * a promise that holds nothing open cannot let it exit 0. When none returned one, the process
113
+ * ends synchronously, inside the signal's own turn.
114
+ * - **The ending** is {@link reRaiseSignal}: on POSIX the process dies of the signal (128 + n); on
115
+ * Windows, or where the raise cannot kill it (PID 1), it exits `fallbackExitCode`.
116
+ * - **Guards compose.** One listener serves every live guard, so two guards in one process both
117
+ * clean up before it ends; the first one installed decides `fallbackExitCode`. A guard installed
118
+ * while an ending is in progress runs its cleanup at once, and the ending waits for it too.
119
+ *
120
+ * @param options - The guard
121
+ * @param options.cleanup - What to undo before the process ends
122
+ * @param options.fallbackExitCode - The exit code where the signal cannot end the process
123
+ * @returns The guard, to release once the guarded work is over
124
+ */
125
+ export declare function handleTerminatingSignals(options: {
126
+ cleanup: TerminatingSignalCleanup;
127
+ fallbackExitCode: number;
128
+ }): TerminatingSignalGuard;
129
+ //# sourceMappingURL=terminating-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminating-signals.d.ts","sourceRoot":"","sources":["../src/terminating-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,iEAAkE,CAAC;AAEnG,0CAA0C;AAC1C,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,iBAAiB,CAE/E;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAa3C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,GAAG,KAAK,CAOrF;AAED;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC;AAwGnG,wDAAwD;AACxD,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;OAUG;IACH,OAAO,IAAI,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,wBAAwB,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GAAG,sBAAsB,CAiBzB"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * The signals a process must not die of before it has cleaned up, the ONE way a handler for them is
3
+ * installed ({@link handleTerminatingSignals}), and how the process then dies of the signal itself.
4
+ *
5
+ * 🚨 Every terminating-signal handler in VAT's product code goes through
6
+ * {@link handleTerminatingSignals}; `local/no-raw-signal-listener` refuses any other way of adding
7
+ * or removing a listener for one (its README lists every shape it catches). The class it closes: a handler
8
+ * that removed itself on the first signal and started an ASYNCHRONOUS cleanup, so a second signal —
9
+ * which a terminal Ctrl-C under the `vat` wrapper delivers (the terminal signals the whole
10
+ * foreground group, and the wrapper forwards) — met the default disposition and killed the process
11
+ * half-way through that cleanup, leaking the clone or the lock it was removing.
12
+ */
13
+ /**
14
+ * The signals that end a process by default disposition — `kill <pid>`, `kill -INT <pid>`,
15
+ * `timeout(1)`, a CI runner stopping or cancelling a step, a closed terminal, Ctrl-C, `Ctrl-\`,
16
+ * Ctrl-Break.
17
+ *
18
+ * SIGQUIT is here because its default disposition terminates exactly as the others do (plus a core
19
+ * dump where the system allows one). SIGBREAK is Windows' Ctrl-Break, which Node delivers there and
20
+ * which ends a process with no handler. Each exists on one platform only: a listener for the other
21
+ * platform's registers an ordinary event that never fires, which is harmless — so ONE list serves
22
+ * both. SIGKILL is not here because nothing can handle it.
23
+ *
24
+ * ⚠️ On Windows only SIGINT, SIGBREAK and SIGHUP are ever DELIVERED, and only as console events
25
+ * (Ctrl-C, Ctrl-Break, the console closing) — which the console sends to EVERY process attached to
26
+ * it. An outside kill there (`taskkill /F`, `process.kill(pid, <any>)`, `child.kill()`) is
27
+ * `TerminateProcess`: no handler runs at all.
28
+ */
29
+ export const TERMINATING_SIGNALS = ['SIGTERM', 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGBREAK'];
30
+ /**
31
+ * Whether `signal` is one of {@link TERMINATING_SIGNALS}.
32
+ *
33
+ * @param signal - A signal name, as a child's `exit` reports it
34
+ * @returns True for a terminating signal
35
+ */
36
+ export function isTerminatingSignal(signal) {
37
+ return TERMINATING_SIGNALS.includes(signal);
38
+ }
39
+ /**
40
+ * How long after the first terminating signal a repeat is still the SAME interruption, and is
41
+ * swallowed rather than acted on.
42
+ *
43
+ * A terminal's Ctrl-C under the `vat` wrapper arrives twice — the foreground group's, and the
44
+ * wrapper's forward — milliseconds apart. A repeat later than this is an operator (or a supervisor)
45
+ * asking again because the first did not end the process soon enough: it ends the process at once,
46
+ * whatever cleanup is still running.
47
+ */
48
+ export const REPEAT_SIGNAL_WINDOW_MS = 500;
49
+ /**
50
+ * How long a re-raised signal is given to end this process before it ends itself instead.
51
+ *
52
+ * A process that SURVIVES its own re-raise is one whose default disposition for the signal does
53
+ * nothing — PID 1 in a container is the common case — and it must still end.
54
+ */
55
+ const RERAISE_GRACE_MS = 1000;
56
+ /** The longest interval a Node timer accepts: the hold below never fires, it only holds. */
57
+ const HOLD_INTERVAL_MS = 2 ** 31 - 1;
58
+ /**
59
+ * End this process OF `signal`, as its default disposition would have — so a shell and a CI runner
60
+ * read the status they expect (128 + n) — with an exit-code fallback.
61
+ *
62
+ * The {@link handleTerminatingSignals} listener is removed first, so the raise reaches the default
63
+ * disposition rather than a guard still swallowing repeats. Only that listener: nothing else in VAT
64
+ * installs one (`local/no-raw-signal-listener`), and a host's own — a test runner's — is not this
65
+ * module's to strip. A process one of those keeps alive takes the fallback below.
66
+ *
67
+ * The wait is synchronous so nothing else runs meanwhile: output published after the operator asked
68
+ * this process to end would read as the run's verdict. A process still here after
69
+ * {@link RERAISE_GRACE_MS} survived its own raise (PID 1) and exits `fallbackExitCode`. Windows is
70
+ * never sent the raise: it cannot SEND most of these signals, and the ones it can terminate without
71
+ * a signal status to preserve — so on Windows this always exits `fallbackExitCode`.
72
+ *
73
+ * @param signal - The signal this process received
74
+ * @param fallbackExitCode - The exit code when the raise cannot end this process
75
+ */
76
+ export function reRaiseSignal(signal, fallbackExitCode) {
77
+ unlisten();
78
+ if (process.platform !== 'win32') {
79
+ process.kill(process.pid, signal);
80
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, RERAISE_GRACE_MS);
81
+ }
82
+ process.exit(fallbackExitCode);
83
+ }
84
+ /** Every guard not yet released, in installation order. */
85
+ const registrations = new Set();
86
+ /** The ending in progress, if a signal has arrived — and the reason a repeat is not a new one. */
87
+ let ending;
88
+ /** Whether `value` is a promise (or any thenable) to wait for. */
89
+ function isThenable(value) {
90
+ return typeof value?.then === 'function';
91
+ }
92
+ /**
93
+ * Run one cleanup inside `current`. A synchronous throw becomes a rejection, so it cannot stop the
94
+ * others — or the ending: a cleanup reports its own failures, and this only makes sure the process
95
+ * still ends. A promise it returns is waited for, whichever way it settles: the ending finishes
96
+ * once the last one has.
97
+ */
98
+ function joinEnding(current, cleanup) {
99
+ let outcome;
100
+ try {
101
+ outcome = cleanup(current.signal, current.forced.signal);
102
+ }
103
+ catch (error) {
104
+ outcome = Promise.reject(error instanceof Error ? error : new Error(String(error)));
105
+ }
106
+ if (!isThenable(outcome))
107
+ return;
108
+ current.pending += 1;
109
+ const settled = () => {
110
+ current.pending -= 1;
111
+ if (current.pending === 0 && ending === current)
112
+ finishEnding(current, current.signal);
113
+ };
114
+ outcome.then(settled, settled);
115
+ }
116
+ /** End `current`'s ending: let go of everything this module holds, then die of `signal`. */
117
+ function finishEnding(current, signal) {
118
+ clearInterval(current.hold);
119
+ registrations.clear();
120
+ ending = undefined;
121
+ reRaiseSignal(signal, current.fallbackExitCode);
122
+ }
123
+ /** The one listener every guard shares, on every {@link TERMINATING_SIGNALS} member. */
124
+ function onTerminatingSignal(signal) {
125
+ const current = ending;
126
+ if (current !== undefined) {
127
+ // The same interruption, delivered twice (a group Ctrl-C and the wrapper's forward): swallowed,
128
+ // so it cannot cut the cleanups short.
129
+ if (performance.now() - current.at < REPEAT_SIGNAL_WINDOW_MS)
130
+ return;
131
+ // Asked again: the cleanups are abandoned, and the process ends now, of the repeat.
132
+ current.forced.abort();
133
+ finishEnding(current, signal);
134
+ return;
135
+ }
136
+ const [first] = registrations;
137
+ if (first === undefined)
138
+ return;
139
+ const started = {
140
+ signal,
141
+ at: performance.now(),
142
+ fallbackExitCode: first.fallbackExitCode,
143
+ forced: new AbortController(),
144
+ hold: setInterval(() => undefined, HOLD_INTERVAL_MS),
145
+ pending: 0,
146
+ };
147
+ ending = started;
148
+ for (const registration of registrations)
149
+ joinEnding(started, registration.cleanup);
150
+ // Synchronous cleanups end the process synchronously: nothing else runs meanwhile, so nothing can
151
+ // publish a verdict after the operator asked this process to stop.
152
+ if (started.pending === 0)
153
+ finishEnding(started, signal);
154
+ }
155
+ function listen() {
156
+ for (const signal of TERMINATING_SIGNALS)
157
+ process.on(signal, onTerminatingSignal);
158
+ }
159
+ function unlisten() {
160
+ for (const signal of TERMINATING_SIGNALS)
161
+ process.off(signal, onTerminatingSignal);
162
+ }
163
+ /**
164
+ * Guard this process's terminating signals while `cleanup` has something to undo: on the FIRST
165
+ * {@link TERMINATING_SIGNALS} member, run it — and every other live guard's — then die of that
166
+ * signal.
167
+ *
168
+ * - **A repeat within {@link REPEAT_SIGNAL_WINDOW_MS} is swallowed.** A terminal Ctrl-C under the
169
+ * `vat` wrapper arrives twice (the group's, and the wrapper's forward); the second must not meet
170
+ * the default disposition half-way through an asynchronous cleanup.
171
+ * - **A repeat after it ends the process at once**, of the repeat, the cleanups abandoned (each
172
+ * cleanup's `forced` signal aborts first): a second Ctrl-C or kill still ends a cleanup that hung.
173
+ * - **Synchronous or asynchronous.** A `cleanup` that returns a promise is awaited, whichever way it
174
+ * settles — it reports its own failures — and a ref'd timer holds the process open meanwhile, so
175
+ * a promise that holds nothing open cannot let it exit 0. When none returned one, the process
176
+ * ends synchronously, inside the signal's own turn.
177
+ * - **The ending** is {@link reRaiseSignal}: on POSIX the process dies of the signal (128 + n); on
178
+ * Windows, or where the raise cannot kill it (PID 1), it exits `fallbackExitCode`.
179
+ * - **Guards compose.** One listener serves every live guard, so two guards in one process both
180
+ * clean up before it ends; the first one installed decides `fallbackExitCode`. A guard installed
181
+ * while an ending is in progress runs its cleanup at once, and the ending waits for it too.
182
+ *
183
+ * @param options - The guard
184
+ * @param options.cleanup - What to undo before the process ends
185
+ * @param options.fallbackExitCode - The exit code where the signal cannot end the process
186
+ * @returns The guard, to release once the guarded work is over
187
+ */
188
+ export function handleTerminatingSignals(options) {
189
+ const registration = { cleanup: options.cleanup, fallbackExitCode: options.fallbackExitCode };
190
+ if (ending === undefined) {
191
+ if (registrations.size === 0)
192
+ listen();
193
+ registrations.add(registration);
194
+ }
195
+ else {
196
+ joinEnding(ending, registration.cleanup);
197
+ }
198
+ const release = () => {
199
+ if (ending !== undefined)
200
+ return false;
201
+ if (registrations.delete(registration) && registrations.size === 0)
202
+ unlisten();
203
+ return true;
204
+ };
205
+ return {
206
+ release,
207
+ releaseOrAwaitEnding: () => (release() ? Promise.resolve() : new Promise(() => { })),
208
+ };
209
+ }
210
+ //# sourceMappingURL=terminating-signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminating-signals.js","sourceRoot":"","sources":["../src/terminating-signals.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,7 @@ export type SymlinkCapability = {
26
26
  *
27
27
  * Because the answer is memoized for the whole process, what reads as "no"
28
28
  * matters more than usual: a `null` here silently `skip()`s every symlink test
29
- * for the rest of the run. So ONLY {@link SYMLINK_UNSUPPORTED_ERRNOS} is a
29
+ * for the rest of the run. So ONLY {@link isSymlinkUnsupportedError} is a
30
30
  * no. A tmpdir that is unwritable or missing, or a bug, is not an answer about
31
31
  * symlinks at all and stays loud rather than becoming a process-wide skip for
32
32
  * a reason nothing reported.
@@ -106,13 +106,14 @@ export declare const INHERITED_GIT_ENV: readonly ["GIT_ALTERNATE_OBJECT_DIRECTOR
106
106
  export declare function detachGitEnv(): () => void;
107
107
  /**
108
108
  * The errno-shaped error a refused `fs` call throws: a message, the `code`,
109
- * and the `syscall`, exactly as Node shapes one.
109
+ * the `syscall` and the `path`, exactly as Node shapes one — a caller that names
110
+ * the refused file from `path` (a tree probe naming the nested file) reads it.
110
111
  */
111
112
  export declare function errnoError(code: string, syscall: string, target: string): NodeJS.ErrnoException;
112
113
  /** The sync `node:fs` calls a refusal can be injected into. */
113
- export type RefusableSyncFsMethod = 'readdirSync' | 'readFileSync' | 'statSync' | 'lstatSync' | 'realpathSync' | 'renameSync' | 'rmSync' | 'unlinkSync';
114
+ export type RefusableSyncFsMethod = 'cpSync' | 'openSync' | 'readdirSync' | 'readFileSync' | 'statSync' | 'lstatSync' | 'realpathSync' | 'renameSync' | 'rmSync' | 'unlinkSync' | 'writeFileSync';
114
115
  /** The `node:fs/promises` calls a refusal can be injected into. */
115
- export type RefusableAsyncFsMethod = 'readdir' | 'readFile' | 'stat' | 'lstat' | 'access';
116
+ export type RefusableAsyncFsMethod = 'readdir' | 'readFile' | 'stat' | 'lstat' | 'access' | 'copyFile' | 'mkdir' | 'writeFile';
116
117
  /**
117
118
  * Make `fs[method]` throw `code` for exactly `targetPath` until the returned
118
119
  * restore is called; every other path, and every other method, stays real.
@@ -131,8 +132,11 @@ export declare function refuseSyncFs(method: RefusableSyncFsMethod, targetPath:
131
132
  /**
132
133
  * `fs/promises[method]` rejects with `code` for exactly `targetPath` until the
133
134
  * returned restore is called; every other path, and every other method, is real.
135
+ * `beforeRefusing`, when given, runs first on each refused call — a write that
136
+ * fails partway (a full disk) leaves a truncated file behind, and a test of the
137
+ * cleanup needs that file to exist.
134
138
  */
135
- export declare function refuseAsyncFs(method: RefusableAsyncFsMethod, targetPath: string, code: string): () => void;
139
+ export declare function refuseAsyncFs(method: RefusableAsyncFsMethod, targetPath: string, code: string, beforeRefusing?: () => void): () => void;
136
140
  /**
137
141
  * Run `body` while `fs[method]` throws `code` for exactly `targetPath`; the
138
142
  * patch is lifted however `body` exits. See {@link refuseSyncFs}.
@@ -1 +1 @@
1
- {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,sBAAsB,EAAE,OAAO,MAAM,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAe5E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,GAAG,IAAI,CAwB5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GACjC,IAAI,CAEN;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GACjC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,yUAgBpB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,IAAI,MAAM,IAAI,CAoBzC;AAGD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAE/F;AAED,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,UAAU,GACV,WAAW,GACX,cAAc,GACd,YAAY,GACZ,QAAQ,GACR,YAAY,CAAC;AAEjB,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAiB1F;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAQxG;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAQ1G;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,MAAM,EAAE,qBAAqB,EAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAEZ"}
1
+ {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,sBAAsB,EAAE,OAAO,MAAM,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAI5E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,GAAG,IAAI,CAwB5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GACjC,IAAI,CAEN;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GACjC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,yUAgBpB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,IAAI,MAAM,IAAI,CAoBzC;AAGD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAE/F;AAED,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,GACR,UAAU,GACV,aAAa,GACb,cAAc,GACd,UAAU,GACV,WAAW,GACX,cAAc,GACd,YAAY,GACZ,QAAQ,GACR,YAAY,GACZ,eAAe,CAAC;AAEpB,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,CAAC;AAiB/H;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAQxG;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,sBAAsB,EAC9B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,cAAc,CAAC,EAAE,MAAM,IAAI,GAC1B,MAAM,IAAI,CAWZ;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,MAAM,EAAE,qBAAqB,EAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAEZ"}