@jerryan/just-bash 3.6.0 → 3.7.0

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 (243) hide show
  1. package/CHANGELOG.md +349 -316
  2. package/LICENSE +201 -201
  3. package/README.md +827 -827
  4. package/dist/agent-sandbox.d.ts +5 -0
  5. package/dist/bin/chunks/chmod-G2BCCQGF.js +3 -0
  6. package/dist/bin/chunks/{chunk-B2GCIQI2.js → chunk-2UKTHDUM.js} +1 -1
  7. package/dist/bin/{shell/chunks/chunk-7WBCA2BB.js → chunks/chunk-3DWQCLNP.js} +1 -1
  8. package/dist/bin/chunks/chunk-66UHJFN6.js +11 -0
  9. package/dist/bin/{shell/chunks/chunk-67XYNGRV.js → chunks/chunk-6O4Z6BQT.js} +1 -1
  10. package/dist/bin/chunks/{chunk-QFTZKD5O.js → chunk-6TUILMPB.js} +1 -1
  11. package/dist/bin/chunks/{chunk-M7SIPZZZ.js → chunk-7KMOGI3N.js} +1 -1
  12. package/dist/bin/{shell/chunks/chunk-6ESRNSO7.js → chunks/chunk-ADI36B7X.js} +1 -1
  13. package/dist/bin/chunks/chunk-CN4JKMTR.js +8 -0
  14. package/dist/bin/{shell/chunks/chunk-DZAFUADN.js → chunks/chunk-E26NQEH2.js} +1 -1
  15. package/dist/bin/chunks/chunk-E54Z6VIE.js +12 -0
  16. package/dist/bin/chunks/chunk-F2PYTUMR.js +9 -0
  17. package/dist/bin/chunks/chunk-FO7XJH3J.js +13 -0
  18. package/dist/bin/chunks/chunk-HWK7KFGB.js +3 -0
  19. package/dist/bin/{shell/chunks/chunk-LE2XKKGQ.js → chunks/chunk-HYLF56CC.js} +1 -1
  20. package/dist/bin/chunks/chunk-KNTDFP7X.js +12 -0
  21. package/dist/bin/{shell/chunks/chunk-MF3GVFLC.js → chunks/chunk-L345YZ6R.js} +1 -1
  22. package/dist/bin/chunks/chunk-NBTZL3VS.js +3 -0
  23. package/dist/bin/chunks/chunk-OHFB3M3X.js +3 -0
  24. package/dist/bin/{shell/chunks/chunk-HQ63MFHG.js → chunks/chunk-PEUGMGJJ.js} +1 -1
  25. package/dist/bin/chunks/chunk-QKJAAJ7J.js +3 -0
  26. package/dist/bin/{shell/chunks/chunk-JELR4642.js → chunks/chunk-R76AK3KD.js} +12 -12
  27. package/dist/bin/chunks/{chunk-NLU5LALY.js → chunk-RRECHBUS.js} +1 -1
  28. package/dist/bin/chunks/{chunk-ZMGLTHSU.js → chunk-THFQI5LV.js} +1 -1
  29. package/dist/bin/chunks/{chunk-CL7IYCGG.js → chunk-U7YR5CDD.js} +1 -1
  30. package/dist/bin/chunks/cp-TMDR6USI.js +3 -0
  31. package/dist/bin/chunks/du-UKFUX7KM.js +3 -0
  32. package/dist/bin/chunks/find-W2RZWTOF.js +3 -0
  33. package/dist/bin/chunks/{flag-coverage-C7PB2MO7.js → flag-coverage-QJ5ZQKCF.js} +1 -1
  34. package/dist/bin/{shell/chunks/grep-CVU5V66H.js → chunks/grep-IHDO3L72.js} +1 -1
  35. package/dist/bin/chunks/gzip-WUK63QLL.js +3 -0
  36. package/dist/bin/{shell/chunks/js-exec-DJQLXWCS.js → chunks/js-exec-BE37X4L2.js} +7 -7
  37. package/dist/bin/chunks/js-exec-worker.js +296 -116
  38. package/dist/bin/chunks/ln-XVC53673.js +3 -0
  39. package/dist/bin/chunks/ls-EROTXGTC.js +3 -0
  40. package/dist/bin/chunks/mkdir-YJ3SOVRP.js +3 -0
  41. package/dist/bin/chunks/mv-HDNKXPO6.js +3 -0
  42. package/dist/bin/{shell/chunks/python3-JCJHSX4Z.js → chunks/python3-TJ6DCPIA.js} +1 -1
  43. package/dist/bin/chunks/readlink-ZUIECKQC.js +3 -0
  44. package/dist/bin/chunks/rg-ODJAYIKX.js +3 -0
  45. package/dist/bin/chunks/rm-B4LVIG7A.js +3 -0
  46. package/dist/bin/chunks/rmdir-W5NBOJ7E.js +3 -0
  47. package/dist/bin/{shell/chunks/sleep-DVFE2GSF.js → chunks/sleep-MOJQBZMY.js} +1 -1
  48. package/dist/bin/chunks/split-TVJYLTIJ.js +3 -0
  49. package/dist/bin/chunks/sqlite3-5BGAK7VX.js +3 -0
  50. package/dist/bin/chunks/{timeout-YGFHQJKX.js → timeout-RQOWSWYG.js} +1 -1
  51. package/dist/bin/chunks/worker.js +233 -108
  52. package/dist/bin/just-bash.js +381 -376
  53. package/dist/bin/shell/chunks/chmod-G2BCCQGF.js +3 -0
  54. package/dist/bin/shell/chunks/{chunk-B2GCIQI2.js → chunk-2UKTHDUM.js} +1 -1
  55. package/dist/bin/{chunks/chunk-7WBCA2BB.js → shell/chunks/chunk-3DWQCLNP.js} +1 -1
  56. package/dist/bin/shell/chunks/chunk-66UHJFN6.js +11 -0
  57. package/dist/bin/{chunks/chunk-67XYNGRV.js → shell/chunks/chunk-6O4Z6BQT.js} +1 -1
  58. package/dist/bin/shell/chunks/{chunk-QFTZKD5O.js → chunk-6TUILMPB.js} +1 -1
  59. package/dist/bin/shell/chunks/{chunk-M7SIPZZZ.js → chunk-7KMOGI3N.js} +1 -1
  60. package/dist/bin/{chunks/chunk-6ESRNSO7.js → shell/chunks/chunk-ADI36B7X.js} +1 -1
  61. package/dist/bin/shell/chunks/chunk-CN4JKMTR.js +8 -0
  62. package/dist/bin/{chunks/chunk-DZAFUADN.js → shell/chunks/chunk-E26NQEH2.js} +1 -1
  63. package/dist/bin/shell/chunks/chunk-E54Z6VIE.js +12 -0
  64. package/dist/bin/shell/chunks/chunk-F2PYTUMR.js +9 -0
  65. package/dist/bin/shell/chunks/chunk-FO7XJH3J.js +13 -0
  66. package/dist/bin/shell/chunks/chunk-HWK7KFGB.js +3 -0
  67. package/dist/bin/{chunks/chunk-LE2XKKGQ.js → shell/chunks/chunk-HYLF56CC.js} +1 -1
  68. package/dist/bin/shell/chunks/chunk-KNTDFP7X.js +12 -0
  69. package/dist/bin/{chunks/chunk-MF3GVFLC.js → shell/chunks/chunk-L345YZ6R.js} +1 -1
  70. package/dist/bin/shell/chunks/chunk-NBTZL3VS.js +3 -0
  71. package/dist/bin/shell/chunks/chunk-OHFB3M3X.js +3 -0
  72. package/dist/bin/{chunks/chunk-HQ63MFHG.js → shell/chunks/chunk-PEUGMGJJ.js} +1 -1
  73. package/dist/bin/shell/chunks/chunk-QKJAAJ7J.js +3 -0
  74. package/dist/bin/{chunks/chunk-JELR4642.js → shell/chunks/chunk-R76AK3KD.js} +12 -12
  75. package/dist/bin/shell/chunks/{chunk-NLU5LALY.js → chunk-RRECHBUS.js} +1 -1
  76. package/dist/bin/shell/chunks/{chunk-ZMGLTHSU.js → chunk-THFQI5LV.js} +1 -1
  77. package/dist/bin/shell/chunks/{chunk-CL7IYCGG.js → chunk-U7YR5CDD.js} +1 -1
  78. package/dist/bin/shell/chunks/cp-TMDR6USI.js +3 -0
  79. package/dist/bin/shell/chunks/du-UKFUX7KM.js +3 -0
  80. package/dist/bin/shell/chunks/find-W2RZWTOF.js +3 -0
  81. package/dist/bin/shell/chunks/{flag-coverage-C7PB2MO7.js → flag-coverage-QJ5ZQKCF.js} +1 -1
  82. package/dist/bin/{chunks/grep-CVU5V66H.js → shell/chunks/grep-IHDO3L72.js} +1 -1
  83. package/dist/bin/shell/chunks/gzip-WUK63QLL.js +3 -0
  84. package/dist/bin/{chunks/js-exec-DJQLXWCS.js → shell/chunks/js-exec-BE37X4L2.js} +7 -7
  85. package/dist/bin/shell/chunks/ln-XVC53673.js +3 -0
  86. package/dist/bin/shell/chunks/ls-EROTXGTC.js +3 -0
  87. package/dist/bin/shell/chunks/mkdir-YJ3SOVRP.js +3 -0
  88. package/dist/bin/shell/chunks/mv-HDNKXPO6.js +3 -0
  89. package/dist/bin/{chunks/python3-JCJHSX4Z.js → shell/chunks/python3-TJ6DCPIA.js} +1 -1
  90. package/dist/bin/shell/chunks/readlink-ZUIECKQC.js +3 -0
  91. package/dist/bin/shell/chunks/rg-ODJAYIKX.js +3 -0
  92. package/dist/bin/shell/chunks/rm-B4LVIG7A.js +3 -0
  93. package/dist/bin/shell/chunks/rmdir-W5NBOJ7E.js +3 -0
  94. package/dist/bin/{chunks/sleep-DVFE2GSF.js → shell/chunks/sleep-MOJQBZMY.js} +1 -1
  95. package/dist/bin/shell/chunks/split-TVJYLTIJ.js +3 -0
  96. package/dist/bin/shell/chunks/sqlite3-5BGAK7VX.js +3 -0
  97. package/dist/bin/shell/chunks/{timeout-YGFHQJKX.js → timeout-RQOWSWYG.js} +1 -1
  98. package/dist/bin/shell/shell.js +385 -380
  99. package/dist/bundle/browser.js +643 -638
  100. package/dist/bundle/chunks/chmod-XZNKTJHL.js +2 -0
  101. package/dist/bundle/chunks/{chunk-XVSR5HHH.js → chunk-2T7PBFL4.js} +1 -1
  102. package/dist/bundle/chunks/chunk-3BMWRGU2.js +10 -0
  103. package/dist/bundle/chunks/chunk-3CIDHL5V.js +11 -0
  104. package/dist/bundle/chunks/chunk-4CC3OZPU.js +2 -0
  105. package/dist/bundle/chunks/{chunk-FIMF2TLE.js → chunk-5DMO6LFU.js} +12 -12
  106. package/dist/bundle/chunks/{chunk-N3A4JVFJ.js → chunk-6IA4DXG2.js} +1 -1
  107. package/dist/bundle/chunks/chunk-A257L6QO.js +7 -0
  108. package/dist/bundle/chunks/{chunk-6DDRR56B.js → chunk-ACVD6PQF.js} +1 -1
  109. package/dist/bundle/chunks/chunk-BS6EJR2R.js +11 -0
  110. package/dist/bundle/chunks/chunk-D5FHVW27.js +2 -0
  111. package/dist/bundle/chunks/{chunk-24NLAR7R.js → chunk-E3ABCXE4.js} +1 -1
  112. package/dist/bundle/chunks/{chunk-FSJ6TTCC.js → chunk-HMM3NGUJ.js} +1 -1
  113. package/dist/bundle/chunks/chunk-IYJLVOVE.js +12 -0
  114. package/dist/bundle/chunks/chunk-LRFR25IP.js +8 -0
  115. package/dist/bundle/chunks/{chunk-NBNI7G66.js → chunk-OPYVVD5P.js} +1 -1
  116. package/dist/bundle/chunks/{chunk-F6XU52K3.js → chunk-OR544SGH.js} +1 -1
  117. package/dist/bundle/chunks/{chunk-RPKJJFGZ.js → chunk-PMBXJ3OQ.js} +1 -1
  118. package/dist/bundle/chunks/chunk-PS2RJNZZ.js +2 -0
  119. package/dist/bundle/chunks/{chunk-UC76ZOWM.js → chunk-SBOTR4JQ.js} +1 -1
  120. package/dist/bundle/chunks/chunk-T5ZEIOJ3.js +2 -0
  121. package/dist/bundle/chunks/{chunk-DMWNBCXJ.js → chunk-TLPMB3FO.js} +1 -1
  122. package/dist/bundle/chunks/{chunk-XV2V56NQ.js → chunk-YFFU74KY.js} +1 -1
  123. package/dist/bundle/chunks/{chunk-DAKEIWT3.js → chunk-YNYC232D.js} +1 -1
  124. package/dist/bundle/chunks/{chunk-ZXFELXJH.js → chunk-YUOBBH7P.js} +1 -1
  125. package/dist/bundle/chunks/cp-NWITRTH3.js +2 -0
  126. package/dist/bundle/chunks/du-IFLWEM6M.js +2 -0
  127. package/dist/bundle/chunks/find-EW6OHRPJ.js +2 -0
  128. package/dist/bundle/chunks/{flag-coverage-XTIQGK65.js → flag-coverage-PIF5QRO7.js} +1 -1
  129. package/dist/bundle/chunks/{grep-4AYF3A5J.js → grep-QDFNKAUW.js} +1 -1
  130. package/dist/bundle/chunks/gzip-HBDKZBIP.js +2 -0
  131. package/dist/bundle/chunks/{js-exec-XKF2B7C3.js → js-exec-IREGAOMH.js} +7 -7
  132. package/dist/bundle/chunks/js-exec-worker.js +296 -116
  133. package/dist/bundle/chunks/ln-CEM7GDB5.js +2 -0
  134. package/dist/bundle/chunks/ls-WJ7EIFVM.js +2 -0
  135. package/dist/bundle/chunks/mkdir-Z6MMWH3D.js +2 -0
  136. package/dist/bundle/chunks/mv-E2NFLGRQ.js +2 -0
  137. package/dist/bundle/chunks/{python3-F5S5TYGS.js → python3-W4UMQJAQ.js} +1 -1
  138. package/dist/bundle/chunks/readlink-XCFO7YD2.js +2 -0
  139. package/dist/bundle/chunks/rg-J7XDRV3B.js +2 -0
  140. package/dist/bundle/chunks/rm-IGPLE632.js +2 -0
  141. package/dist/bundle/chunks/rmdir-UMUDM2L2.js +2 -0
  142. package/dist/bundle/chunks/{sleep-PPRBTSVR.js → sleep-25QUPVDK.js} +1 -1
  143. package/dist/bundle/chunks/split-D4HPO4G5.js +2 -0
  144. package/dist/bundle/chunks/sqlite3-3HL2ECCQ.js +2 -0
  145. package/dist/bundle/chunks/{timeout-C6WDYPLV.js → timeout-37GM7IIL.js} +1 -1
  146. package/dist/bundle/chunks/worker.js +233 -108
  147. package/dist/bundle/index.cjs +975 -970
  148. package/dist/bundle/index.js +357 -352
  149. package/dist/commands/js-exec/js-exec.d.ts +4 -0
  150. package/dist/commands/worker-bridge/bridge-handler.d.ts +9 -3
  151. package/dist/commands/worker-bridge/protocol.d.ts +72 -21
  152. package/dist/commands/worker-bridge/sync-backend.d.ts +3 -0
  153. package/dist/fs/fs-error.d.ts +43 -0
  154. package/dist/fs/interface.d.ts +13 -0
  155. package/dist/fs/mountable-fs/mountable-fs.d.ts +20 -0
  156. package/dist/fs/overlay-fs/apply.d.ts +15 -1
  157. package/dist/fs/overlay-fs/index.d.ts +1 -0
  158. package/dist/fs/overlay-fs/overlay-fs.d.ts +31 -0
  159. package/dist/fs/overlay-fs/overlay-tree.d.ts +39 -0
  160. package/dist/fs/path-utils.d.ts +5 -0
  161. package/dist/fs/vfs-template.d.ts +74 -0
  162. package/dist/index.d.cts +3 -1
  163. package/dist/index.d.ts +3 -1
  164. package/dist/interpreter/builtin-dispatch.d.ts +10 -0
  165. package/dist/interpreter/builtin-manifest.d.ts +273 -0
  166. package/dist/interpreter/builtins/help.d.ts +6 -0
  167. package/dist/interpreter/builtins/shopt.d.ts +3 -0
  168. package/dist/interpreter/helpers/shell-constants.d.ts +0 -20
  169. package/dist/timers.d.ts +9 -0
  170. package/package.json +135 -136
  171. package/vendor/cpython-emscripten/python.cjs +2 -2
  172. package/dist/bin/chunks/chmod-WYYTXGVB.js +0 -3
  173. package/dist/bin/chunks/chunk-43E7AR6A.js +0 -3
  174. package/dist/bin/chunks/chunk-6LDOIBU7.js +0 -9
  175. package/dist/bin/chunks/chunk-AI7JUA46.js +0 -13
  176. package/dist/bin/chunks/chunk-GQKQEK5N.js +0 -11
  177. package/dist/bin/chunks/chunk-JQM3DKCH.js +0 -12
  178. package/dist/bin/chunks/chunk-OLEQNRKX.js +0 -8
  179. package/dist/bin/chunks/chunk-PPVF2N6C.js +0 -3
  180. package/dist/bin/chunks/chunk-WEHA3JFD.js +0 -3
  181. package/dist/bin/chunks/chunk-YQEKKZTU.js +0 -12
  182. package/dist/bin/chunks/cp-OMVKM35F.js +0 -3
  183. package/dist/bin/chunks/du-LUFPVLNW.js +0 -3
  184. package/dist/bin/chunks/find-AWH2KOY7.js +0 -3
  185. package/dist/bin/chunks/gzip-FDPA6EKG.js +0 -3
  186. package/dist/bin/chunks/ln-B35EKC2Q.js +0 -3
  187. package/dist/bin/chunks/ls-Q6ZZ6SQ2.js +0 -3
  188. package/dist/bin/chunks/mkdir-74BP5NTG.js +0 -3
  189. package/dist/bin/chunks/mv-76LAHXFR.js +0 -3
  190. package/dist/bin/chunks/readlink-EHXJNW62.js +0 -3
  191. package/dist/bin/chunks/rg-PXNKNIMY.js +0 -3
  192. package/dist/bin/chunks/rm-TF2NJQIA.js +0 -3
  193. package/dist/bin/chunks/rmdir-7ZIVFZJX.js +0 -3
  194. package/dist/bin/chunks/split-ZNNNHKSP.js +0 -3
  195. package/dist/bin/chunks/sqlite3-KP42NNIY.js +0 -3
  196. package/dist/bin/shell/chunks/chmod-WYYTXGVB.js +0 -3
  197. package/dist/bin/shell/chunks/chunk-43E7AR6A.js +0 -3
  198. package/dist/bin/shell/chunks/chunk-6LDOIBU7.js +0 -9
  199. package/dist/bin/shell/chunks/chunk-AI7JUA46.js +0 -13
  200. package/dist/bin/shell/chunks/chunk-GQKQEK5N.js +0 -11
  201. package/dist/bin/shell/chunks/chunk-JQM3DKCH.js +0 -12
  202. package/dist/bin/shell/chunks/chunk-OLEQNRKX.js +0 -8
  203. package/dist/bin/shell/chunks/chunk-PPVF2N6C.js +0 -3
  204. package/dist/bin/shell/chunks/chunk-WEHA3JFD.js +0 -3
  205. package/dist/bin/shell/chunks/chunk-YQEKKZTU.js +0 -12
  206. package/dist/bin/shell/chunks/cp-OMVKM35F.js +0 -3
  207. package/dist/bin/shell/chunks/du-LUFPVLNW.js +0 -3
  208. package/dist/bin/shell/chunks/find-AWH2KOY7.js +0 -3
  209. package/dist/bin/shell/chunks/gzip-FDPA6EKG.js +0 -3
  210. package/dist/bin/shell/chunks/ln-B35EKC2Q.js +0 -3
  211. package/dist/bin/shell/chunks/ls-Q6ZZ6SQ2.js +0 -3
  212. package/dist/bin/shell/chunks/mkdir-74BP5NTG.js +0 -3
  213. package/dist/bin/shell/chunks/mv-76LAHXFR.js +0 -3
  214. package/dist/bin/shell/chunks/readlink-EHXJNW62.js +0 -3
  215. package/dist/bin/shell/chunks/rg-PXNKNIMY.js +0 -3
  216. package/dist/bin/shell/chunks/rm-TF2NJQIA.js +0 -3
  217. package/dist/bin/shell/chunks/rmdir-7ZIVFZJX.js +0 -3
  218. package/dist/bin/shell/chunks/split-ZNNNHKSP.js +0 -3
  219. package/dist/bin/shell/chunks/sqlite3-KP42NNIY.js +0 -3
  220. package/dist/bundle/chunks/chmod-WM2JFFQC.js +0 -2
  221. package/dist/bundle/chunks/chunk-7IJWBG4I.js +0 -2
  222. package/dist/bundle/chunks/chunk-MLIO3BK2.js +0 -7
  223. package/dist/bundle/chunks/chunk-PIRTX3D3.js +0 -8
  224. package/dist/bundle/chunks/chunk-TBVD66LC.js +0 -10
  225. package/dist/bundle/chunks/chunk-W2Z6LYL6.js +0 -11
  226. package/dist/bundle/chunks/chunk-WN54B5NQ.js +0 -12
  227. package/dist/bundle/chunks/chunk-WXPNKIIH.js +0 -2
  228. package/dist/bundle/chunks/chunk-YJKHREBZ.js +0 -11
  229. package/dist/bundle/chunks/chunk-YUSFCOM7.js +0 -2
  230. package/dist/bundle/chunks/cp-2KZIPZJS.js +0 -2
  231. package/dist/bundle/chunks/du-7AZCLKR6.js +0 -2
  232. package/dist/bundle/chunks/find-END3EAHV.js +0 -2
  233. package/dist/bundle/chunks/gzip-RQOBR22H.js +0 -2
  234. package/dist/bundle/chunks/ln-OPY2ZVGQ.js +0 -2
  235. package/dist/bundle/chunks/ls-ZPXGZX63.js +0 -2
  236. package/dist/bundle/chunks/mkdir-WDDKK2P3.js +0 -2
  237. package/dist/bundle/chunks/mv-ZF6UBVRB.js +0 -2
  238. package/dist/bundle/chunks/readlink-U5OM5A3M.js +0 -2
  239. package/dist/bundle/chunks/rg-NAMXBYKS.js +0 -2
  240. package/dist/bundle/chunks/rm-OAZOF7XI.js +0 -2
  241. package/dist/bundle/chunks/rmdir-YJKPQVCK.js +0 -2
  242. package/dist/bundle/chunks/split-JICLAMP6.js +0 -2
  243. package/dist/bundle/chunks/sqlite3-4EZ4GXVD.js +0 -2
@@ -7,6 +7,10 @@
7
7
  * This command is Node.js only (uses worker_threads).
8
8
  */
9
9
  import type { RuntimeCommand } from "../../types.js";
10
+ /** @internal Set the idle-teardown delay — for focused lifecycle tests only. */
11
+ export declare function _setJsExecWorkerIdleTimeoutMsForTests(ms: number): void;
12
+ /** @internal Whether the shared worker is currently alive — for tests only. */
13
+ export declare function _hasSharedJsExecWorkerForTests(): boolean;
10
14
  /** @internal Reset singleton worker state — for focused protocol tests only. */
11
15
  export declare function _resetJsExecWorkerForTests(): void;
12
16
  export declare const jsExecCommand: RuntimeCommand;
@@ -12,9 +12,6 @@ export interface BridgeOutput {
12
12
  stderr: string;
13
13
  exitCode: number;
14
14
  }
15
- /**
16
- * Handles requests from a worker thread.
17
- */
18
15
  export declare class BridgeHandler {
19
16
  private fs;
20
17
  private cwd;
@@ -29,6 +26,10 @@ export declare class BridgeHandler {
29
26
  private outputLimitExceeded;
30
27
  private startTime;
31
28
  private timeoutMs;
29
+ /** Complete buffer of the last published oversized result, retained
30
+ * so the worker can fetch it in READ_RESULT_RANGE slices. Replaced on
31
+ * every publish. */
32
+ private lastResult;
32
33
  constructor(sharedBuffer: SharedArrayBuffer, fs: IFileSystem, cwd: string, commandName: string, secureFetch?: SecureFetch | undefined, maxOutputSize?: number, exec?: ((command: string, options: CommandExecOptions) => Promise<ExecResult>) | undefined, invokeTool?: ((path: string, argsJson: string) => Promise<string>) | undefined);
33
34
  /**
34
35
  * Returns remaining milliseconds before the overall execution deadline.
@@ -45,8 +46,13 @@ export declare class BridgeHandler {
45
46
  run(timeoutMs: number): Promise<BridgeOutput>;
46
47
  stop(): void;
47
48
  private handleOperation;
49
+ /** Publish a result of any size; oversized results are fetched by the
50
+ * worker in READ_RESULT_RANGE slices from the retained buffer. */
51
+ private publishResult;
52
+ private handleReadResultRange;
48
53
  private resolvePath;
49
54
  private handleReadFile;
55
+ private handleWriteFileRange;
50
56
  private handleWriteFile;
51
57
  private handleStat;
52
58
  private handleLstat;
@@ -33,6 +33,8 @@ export declare const OpCode: {
33
33
  readonly REALPATH: 13;
34
34
  readonly RENAME: 14;
35
35
  readonly COPY_FILE: 15;
36
+ readonly WRITE_FILE_RANGE: 17;
37
+ readonly READ_RESULT_RANGE: 18;
36
38
  readonly WRITE_STDOUT: 100;
37
39
  readonly WRITE_STDERR: 101;
38
40
  readonly EXIT: 102;
@@ -41,14 +43,34 @@ export declare const OpCode: {
41
43
  readonly INVOKE_TOOL: 400;
42
44
  };
43
45
  export type OpCodeType = (typeof OpCode)[keyof typeof OpCode];
44
- /** Status codes for synchronization */
45
- export declare const Status: {
46
- readonly PENDING: 0;
47
- readonly READY: 1;
48
- readonly SUCCESS: 2;
49
- readonly ERROR: 3;
46
+ /**
47
+ * Two-word request/result state. The previous single STATUS word was
48
+ * written by THREE actors with TWO meanings (worker READY = "request
49
+ * published", host SUCCESS/ERROR = "result published", stop() READY =
50
+ * "wake for cancel") — a worker waiting for a result could observe a
51
+ * request-channel value and read a torn buffer (the CI flake class).
52
+ * Splitting the channels makes the torn state unrepresentable: a
53
+ * result wait is only ever woken by a result write. A state outside
54
+ * the tables on wake is a genuine protocol bug and fails loudly.
55
+ */
56
+ /** Worker + stop() write; the host waits on this word. */
57
+ export declare const RequestState: {
58
+ readonly IDLE: 0;
59
+ /** Worker has published an op. */
60
+ readonly REQUEST: 1;
61
+ /** stop() cancellation wake. The host treats any non-REQUEST wake as
62
+ * loop-abort (stop() also flips its `running` flag). */
63
+ readonly CANCELLED: 2;
50
64
  };
51
- export type StatusType = (typeof Status)[keyof typeof Status];
65
+ type RequestStateType = (typeof RequestState)[keyof typeof RequestState];
66
+ /** Host writes; the worker waits on this word. stop() never touches
67
+ * it, so a result wait cannot be torn by cancellation. */
68
+ export declare const ResultState: {
69
+ readonly NONE: 0;
70
+ readonly SUCCESS: 1;
71
+ readonly ERROR: 2;
72
+ };
73
+ type ResultStateType = (typeof ResultState)[keyof typeof ResultState];
52
74
  /** Error codes */
53
75
  export declare const ErrorCode: {
54
76
  readonly NONE: 0;
@@ -59,11 +81,35 @@ export declare const ErrorCode: {
59
81
  readonly PERMISSION_DENIED: 5;
60
82
  readonly INVALID_PATH: 6;
61
83
  readonly IO_ERROR: 7;
62
- readonly TIMEOUT: 8;
63
84
  readonly NETWORK_ERROR: 9;
64
85
  readonly NETWORK_NOT_CONFIGURED: 10;
86
+ readonly NOT_EMPTY: 11;
87
+ readonly LOOP: 12;
88
+ readonly FILE_TOO_LARGE: 13;
89
+ readonly NO_SPACE: 14;
90
+ readonly BUSY: 15;
91
+ readonly READ_ONLY: 16;
92
+ readonly CROSS_DEVICE: 17;
65
93
  };
66
94
  export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
95
+ /**
96
+ * The canonical errno-name <-> wire-code table. Both bridge sides ship
97
+ * together, so the table lives here (not per-side copies): the host
98
+ * maps thrown errnos to wire codes with it, and workers derive guest-
99
+ * facing codes from its inverse via wireToErrnoName(). EPERM precedes
100
+ * EACCES so the inverse prefers the common node spelling.
101
+ */
102
+ export declare const ERRNO_TO_WIRE: Record<string, ErrorCodeType>;
103
+ /** Errno name for a wire code, or undefined for non-errno wire codes
104
+ * (NONE, IO_ERROR, network conditions). PERMISSION_DENIED inverts to
105
+ * EACCES (the more common spelling in node guests). */
106
+ export declare function wireToErrnoName(code: number): string | undefined;
107
+ /** Buffer sizes */
108
+ export declare const Size: {
109
+ readonly PATH_BUFFER: 4096;
110
+ readonly DATA_BUFFER: 8388608;
111
+ readonly TOTAL: 8392740;
112
+ };
67
113
  /** Flags for operations */
68
114
  export declare const Flags: {
69
115
  readonly NONE: 0;
@@ -82,8 +128,10 @@ export declare class ProtocolBuffer {
82
128
  constructor(buffer: SharedArrayBuffer);
83
129
  getOpCode(): OpCodeType;
84
130
  setOpCode(code: OpCodeType): void;
85
- getStatus(): StatusType;
86
- setStatus(status: StatusType): void;
131
+ getRequest(): RequestStateType;
132
+ setRequest(request: RequestStateType): void;
133
+ getResultState(): ResultStateType;
134
+ setResultState(state: ResultStateType): void;
87
135
  getPathLength(): number;
88
136
  setPathLength(length: number): void;
89
137
  getDataLength(): number;
@@ -104,6 +152,13 @@ export declare class ProtocolBuffer {
104
152
  setDataFromString(str: string): void;
105
153
  getResult(): Uint8Array;
106
154
  setResult(data: Uint8Array): void;
155
+ /**
156
+ * Publish a result of ANY size: the first DATA_BUFFER bytes land in
157
+ * the data region and RESULT_LENGTH carries the FULL length. The
158
+ * worker detects the overflow and fetches the rest with
159
+ * READ_RESULT_RANGE ops (served from the host's retained buffer).
160
+ */
161
+ setResultPrefix(data: Uint8Array): void;
107
162
  getResultAsString(): string;
108
163
  setResultFromString(str: string): void;
109
164
  encodeStat(stat: {
@@ -122,20 +177,16 @@ export declare class ProtocolBuffer {
122
177
  size: number;
123
178
  mtime: Date;
124
179
  };
125
- waitForReady(timeout?: number): "ok" | "timed-out" | "not-equal";
126
- waitForReadyAsync(timeout?: number): {
127
- async: false;
128
- value: "not-equal" | "timed-out";
129
- } | {
130
- async: true;
131
- value: Promise<"ok" | "timed-out">;
132
- };
133
180
  /**
134
- * Wait for status to become READY.
135
- * Returns immediately if status is already READY, or waits until it changes.
181
+ * Wait for the request word to become REQUEST.
182
+ * Returns immediately if already REQUEST; CANCELLED or any unexpected
183
+ * state returns false (the caller's running-flag check aborts the loop).
136
184
  */
137
185
  waitUntilReady(timeout: number): Promise<boolean>;
186
+ /** Worker-side blocking wait on the RESULT word (host writes). */
138
187
  waitForResult(timeout?: number): "ok" | "timed-out" | "not-equal";
139
- notify(): number;
188
+ notifyRequest(): number;
189
+ notifyResult(): number;
140
190
  reset(): void;
141
191
  }
192
+ export {};
@@ -12,6 +12,9 @@ export declare class SyncBackend {
12
12
  private operationTimeoutMs;
13
13
  constructor(sharedBuffer: SharedArrayBuffer, operationTimeoutMs?: number);
14
14
  private execSync;
15
+ /** Build an op failure that preserves the bridge's numeric error code,
16
+ * so downstream errno mapping prefers it over substring matching. */
17
+ private opError_;
15
18
  readFile(path: string): Uint8Array;
16
19
  writeFile(path: string, data: Uint8Array): void;
17
20
  stat(path: string): {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Filesystem error carrying its errno as a STRUCTURED field.
3
+ *
4
+ * The contract: the errno is chosen at the throw site and carried on
5
+ * `.code`; consumers read `.code` and never re-derive it from message
6
+ * prose. There is deliberately NO message-parsing fallback here —
7
+ * parsing "CODE:" prefixes back out of strings is the legacy path this
8
+ * module replaces, and keeping it would leave two sources of truth
9
+ * (the field and the phrasing) with an illusion of robustness.
10
+ *
11
+ * Message format is preserved as a DISPLAY convention only: FsError
12
+ * renders `${code}: ${message}` so existing user-facing text and
13
+ * message-asserting tests are unchanged. The prefix is produced FROM
14
+ * the code, never parsed back INTO one.
15
+ *
16
+ * Contract for IFileSystem implementations (documented on the
17
+ * interface): throw FsError, or any node-style error with a string
18
+ * `.code`. Errors without a structured code are coerced to EIO at
19
+ * classification boundaries (toFsError) — an honest unknown, never a
20
+ * fabricated specific errno.
21
+ */
22
+ export declare class FsError extends Error {
23
+ readonly code: string;
24
+ /** The message WITHOUT the "CODE: " transport prefix — what
25
+ * user-facing display paths should render. */
26
+ readonly bareMessage: string;
27
+ constructor(code: string, message: string);
28
+ }
29
+ /** Extract a structured errno from any thrown value, or undefined when
30
+ * the value carries none (bare Errors, strings, foreign objects). */
31
+ export declare function fsErrorCode(err: unknown): string | undefined;
32
+ /** True when the error carries the given structured errno. */
33
+ export declare function isFsErrorCode(err: unknown, code: string): boolean;
34
+ /**
35
+ * Classify any thrown value at a boundary: structured codes pass
36
+ * through unchanged; everything else becomes the fallback (EIO by
37
+ * default) with the original message preserved. Provided as PUBLIC API
38
+ * for custom IFileSystem implementations and host-side adapters that
39
+ * need to normalize third-party errors before handing them to
40
+ * just-bash internals. Internally, boundaries that only need the code
41
+ * (e.g. the worker bridge) read fsErrorCode directly instead.
42
+ */
43
+ export declare function toFsError(err: unknown, fallbackCode?: string): FsError;
@@ -96,6 +96,19 @@ export interface CpOptions {
96
96
  * - InMemoryFs (in-memory, default)
97
97
  * - Real filesystem (via node:fs)
98
98
  * - Custom implementations (e.g., remote storage, browser IndexedDB)
99
+ *
100
+ * ERROR CONTRACT: implementations signal failure kind by throwing
101
+ * {@link FsError} (or any node-style error with a string `.code`
102
+ * property) using POSIX errno names — `"ENOENT"`, `"EACCES"`,
103
+ * `"EEXIST"`, `"ENOTDIR"`, `"EISDIR"`, `"ENOTEMPTY"`, `"EPERM"`,
104
+ * `"EINVAL"`, `"EBUSY"`, `"ELOOP"`, `"EFBIG"`, `"EXDEV"`, `"ENOSPC"`,
105
+ * `"EROFS"`, `"EIO"`. Consumers
106
+ * (commands, bridged runtimes like python3, and error mappings) read
107
+ * the code from `.code`; they never parse it out of message prose.
108
+ * Errors with no structured code are classified at the boundary as
109
+ * EIO — an honest unknown, never a fabricated specific errno.
110
+ * Message text is free-form; by convention FsError renders
111
+ * `"ECODE: message"` for display.
99
112
  */
100
113
  export interface IFileSystem {
101
114
  /**
@@ -1,5 +1,6 @@
1
1
  import { type ByteString } from "../../encoding.js";
2
2
  import type { BufferEncoding, CpOptions, FileContent, FsStat, IFileSystem, MkdirOptions, ReadFileOptions, RmOptions, WriteFileOptions } from "../interface.js";
3
+ import type { OverlayDiff } from "../overlay-fs/overlay-fs.js";
3
4
  /**
4
5
  * Configuration for a mount point
5
6
  */
@@ -58,6 +59,25 @@ export declare class MountableFs implements IFileSystem {
58
59
  mountPoint: string;
59
60
  filesystem: IFileSystem;
60
61
  }>;
62
+ /**
63
+ * Combined change set across all diff-capable mounts. `space` selects
64
+ * the path form: "vfs" (default) keeps full virtual paths (mount
65
+ * prefix retained — directly replayable by a template merge);
66
+ * "host" maps each entry to its real-absolute host path (for
67
+ * template.apply / applyDiffToRealFs). The base fs is shared scratch
68
+ * and is not diffed. Mounts without a diff() method fail loudly —
69
+ * a merged view cannot silently omit a mount's changes.
70
+ */
71
+ diff(options?: {
72
+ space?: "vfs" | "host";
73
+ }): OverlayDiff;
74
+ /**
75
+ * Reconciliation support (template merge replay): set the changedAt
76
+ * ordering stamp of the node at `path` and its ancestors, routing to
77
+ * the owning mount. Paths outside every mount (shared scratch) have
78
+ * no stampable overlay and are ignored.
79
+ */
80
+ restamp(path: string, changedAt: number, scope?: "full" | "ancestors"): void;
61
81
  /**
62
82
  * Check if a path is exactly a mount point.
63
83
  */
@@ -17,6 +17,20 @@ export declare function removeFromRealFs(path: string): void;
17
17
  * symlinks are recreated with the recorded target; files get the recorded
18
18
  * content (unless metadataOnly — a chmod/utimes copy-up), the recorded
19
19
  * mode (POSIX only — mode bits are advisory on Windows), and the recorded
20
- * mtime restored after the content write.
20
+ * mtime restored after the content write. A type-conflicting on-disk
21
+ * target is replaced (the change-set supersedes the base).
21
22
  */
22
23
  export declare function applyWriteToRealFs(path: string, write: Omit<OverlayWrite, "path">): void;
24
+ /**
25
+ * Apply a change set with real absolute paths to the real filesystem:
26
+ * deletions first (deepest path first, so subtrees go before their
27
+ * parents), then writes (shallowest first, so explicit directory
28
+ * entries land before their children). Standalone counterpart of
29
+ * AgentSandbox.applyChanges for the fork model — no overlay instance
30
+ * is involved and nothing is dropped; the caller owns the overlays'
31
+ * lifecycle (typically: discard after diff()).
32
+ */
33
+ export declare function applyDiffToRealFs(diff: {
34
+ writes: OverlayWrite[];
35
+ deletions: string[];
36
+ }): void;
@@ -1 +1,2 @@
1
+ export { applyDiffToRealFs } from "./apply.js";
1
2
  export { type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./overlay-fs.js";
@@ -48,6 +48,13 @@ export interface OverlayWrite {
48
48
  mode: number;
49
49
  /** Modification time; hosts may apply it (utimes) for fidelity. */
50
50
  mtime: Date;
51
+ /**
52
+ * Wall-clock time the overlay recorded this mutation (attach or last
53
+ * in-place change). Unlike `mtime` this cannot be set from inside the
54
+ * sandbox, which makes it the trustworthy ordering key for merging
55
+ * change sets from independent overlays (see mergeDiffs).
56
+ */
57
+ changedAt?: number;
51
58
  /**
52
59
  * When true, only metadata changed (chmod/utimes via a metacopy shadow):
53
60
  * apply `mode` and `mtime` and never touch file content.
@@ -70,6 +77,13 @@ export interface OverlayDiff {
70
77
  * never nest, so each entry is a top-most deletion covering its subtree.
71
78
  */
72
79
  deletions: string[];
80
+ /**
81
+ * Wall-clock creation time of each whiteout, aligned with `deletions`
82
+ * (same order). Overlay-assigned and untamperable — the ordering key
83
+ * for merging deletions against writes from other overlays. Present
84
+ * whenever `deletions` is non-empty; absent otherwise.
85
+ */
86
+ deletionChangedAt?: number[];
73
87
  }
74
88
  export interface OverlayFsOptions {
75
89
  /**
@@ -116,6 +130,20 @@ export declare class OverlayFs implements IFileSystem {
116
130
  private readonly allowSymlinks;
117
131
  private readonly tree;
118
132
  private nextMemoryIdentity;
133
+ /** The real directory this overlay reads through and diffs against. */
134
+ get rootDir(): string;
135
+ /**
136
+ * Reconciliation support (template merge replay, host-side change-set
137
+ * application): stamp the node at `path` with `changedAt` (the
138
+ * replayed entry's own stamp), and correct any NEWER stamps among its
139
+ * ancestors and descendants. Post-sort, a newer stamp can only be a
140
+ * fresh wall-clock stamp minted by the replay op itself (ensured
141
+ * parents, resurrection whiteouts) — those belong to the replayed
142
+ * entry and take its stamp; earlier replayed entries (older stamps)
143
+ * are never touched. This is a host-side reconciliation API, not a
144
+ * guest operation: changedAt is the ordering key merges rely on.
145
+ */
146
+ restamp(path: string, changedAt: number, scope?: "full" | "ancestors"): void;
119
147
  private identityFor;
120
148
  constructor(options: OverlayFsOptions);
121
149
  /**
@@ -285,6 +313,9 @@ export declare class OverlayFs implements IFileSystem {
285
313
  */
286
314
  private inheritedMode;
287
315
  appendFile(path: string, content: FileContent, options?: WriteFileOptions | BufferEncoding): Promise<void>;
316
+ /** Append to an existing upper file node, completing a metacopy
317
+ * copy-up first when needed (preserving the node's upper mode). */
318
+ private appendToExisting_;
288
319
  exists(path: string): Promise<boolean>;
289
320
  stat(path: string, seen?: Set<string>): Promise<FsStat>;
290
321
  lstat(path: string): Promise<FsStat>;
@@ -36,6 +36,21 @@ export interface OverlayFileNode {
36
36
  lowerSize?: number;
37
37
  /** Append segments retained without copying the complete file per append. */
38
38
  appendChunks?: Uint8Array[];
39
+ /**
40
+ * Mutation sequence stamp (tree-local, monotonically increasing).
41
+ * Set by in-place mutations (appendChunk, chmod, utimes) so sync()
42
+ * can compare-and-swap: an entry checked against disk is only
43
+ * detached if it has not been mutated since the check began.
44
+ * Node replacement (attach/detach) needs no stamp — object identity
45
+ * already changes. Undefined means never mutated in place.
46
+ */
47
+ seq?: number;
48
+ /**
49
+ * Wall-clock time of the last mutation (attach/touch), stamped by the
50
+ * tree. Distinct from `mtime`: user-visible and utimes-able, whereas
51
+ * changedAt is the untamperable ordering key mergeDiffs relies on.
52
+ */
53
+ changedAt?: number;
39
54
  mode: number;
40
55
  mtime: Date;
41
56
  identity?: string;
@@ -44,6 +59,10 @@ export interface OverlayDirNode {
44
59
  type: "directory";
45
60
  /** Child nodes keyed by name segment, in insertion order. */
46
61
  children: Map<string, OverlayNode>;
62
+ /** In-place mutation stamp (see OverlayFileNode.seq). */
63
+ seq?: number;
64
+ /** Wall-clock time of the last mutation (see OverlayFileNode.changedAt). */
65
+ changedAt?: number;
47
66
  mode: number;
48
67
  mtime: Date;
49
68
  identity?: string;
@@ -51,12 +70,18 @@ export interface OverlayDirNode {
51
70
  export interface OverlaySymlinkNode {
52
71
  type: "symlink";
53
72
  target: string;
73
+ /** In-place mutation stamp (see OverlayFileNode.seq). */
74
+ seq?: number;
75
+ /** Wall-clock time of the last mutation (see OverlayFileNode.changedAt). */
76
+ changedAt?: number;
54
77
  mode: number;
55
78
  mtime: Date;
56
79
  }
57
80
  /** Leaf marker hiding a lower-layer (real-FS) path and everything under it. */
58
81
  export interface OverlayWhiteoutNode {
59
82
  type: "whiteout";
83
+ /** Wall-clock time the whiteout was created (merge ordering). */
84
+ changedAt?: number;
60
85
  }
61
86
  export type OverlayEntryNode = OverlayFileNode | OverlayDirNode | OverlaySymlinkNode;
62
87
  export type OverlayNode = OverlayEntryNode | OverlayWhiteoutNode;
@@ -101,6 +126,7 @@ export declare class OverlayTree {
101
126
  private readonly maxMemoryBytes;
102
127
  private rootNode;
103
128
  private bytes;
129
+ private mutationSeq_;
104
130
  constructor(maxMemoryBytes: number);
105
131
  /** The root directory node — the VFS root ("/"). */
106
132
  get root(): OverlayDirNode;
@@ -160,6 +186,19 @@ export declare class OverlayTree {
160
186
  * Append a chunk to a file node, accounting its bytes against capacity.
161
187
  */
162
188
  appendChunk(node: OverlayFileNode, chunk: Uint8Array): void;
189
+ /** Stamp a node as mutated in place (see OverlayFileNode.seq). */
190
+ touch(node: {
191
+ seq?: number;
192
+ changedAt?: number;
193
+ }): void;
194
+ /**
195
+ * Compare-and-swap detach: remove the node at `path` only if it is
196
+ * still the exact same object with the same mutation sequence — i.e.
197
+ * nothing changed it while the caller's (async) verification ran.
198
+ * Also refuses to detach a directory that has gained children.
199
+ * Returns whether the detach happened.
200
+ */
201
+ detachIfUnchanged(path: string, node: OverlayNode, seq?: number): boolean;
163
202
  /**
164
203
  * Yield every node, parents before children (pre-order), starting with the
165
204
  * root at path "/". Whiteouts are included; callers filter as needed.
@@ -39,6 +39,11 @@ export declare function resolvePath(base: string, path: string): string;
39
39
  * Join a parent path with a child name.
40
40
  * Handles the root-path edge case (`"/" + "child"` → `"/child"`).
41
41
  */
42
+ /** Convert an OS-form path fragment to VFS slash form (win32
43
+ * backslashes become slashes; on POSIX this is a no-op). VFS paths are
44
+ * always slash-form — use when reconstructing a virtual path from a
45
+ * real-OS path (e.g. slicing off a real root). */
46
+ export declare function toVirtualSlashes(path: string): string;
42
47
  export declare function joinPath(parent: string, child: string): string;
43
48
  /**
44
49
  * Resolve a symlink target relative to the symlink's location.
@@ -0,0 +1,74 @@
1
+ import { MountableFs } from "./mountable-fs/mountable-fs.js";
2
+ import type { OverlayDiff } from "./overlay-fs/overlay-fs.js";
3
+ /** One privatized subtree: virtual mount point + real backing root. */
4
+ export interface VfsTemplateMount {
5
+ /** Virtual absolute path the overlay serves (e.g. "/project"). */
6
+ at: string;
7
+ /** Real absolute directory the overlay reads through and diffs against. */
8
+ root: string;
9
+ }
10
+ export interface VfsTemplateOptions {
11
+ /**
12
+ * Subtrees privatized per fork. Everything else (/, /tmp, ...) lives
13
+ * in the template's shared scratch memory fs: writes there are
14
+ * visible to every fork immediately and never appear in diffs.
15
+ */
16
+ mounts: VfsTemplateMount[];
17
+ /**
18
+ * Per-overlay content-byte budget (OverlayFs maxMemoryBytes),
19
+ * bounding each fork's copy-up memory. Default: the OverlayFs
20
+ * default.
21
+ */
22
+ maxMemoryBytes?: number;
23
+ }
24
+ /**
25
+ * Template for cheap parallel forks (see
26
+ * docs/design/vfs-template-fork-model.md). The template owns the shared
27
+ * scratch fs and the mount configuration; each fork() stamps out a
28
+ * ready MountableFs with fresh copy-on-write overlays over every
29
+ * mounted real root. Forks are unmanaged: the template keeps no
30
+ * registry, consumes nothing, and never tracks lifecycles.
31
+ *
32
+ * MERGE IS A REPLAY. `merge` combines the given change sets (fork
33
+ * instances are diffed in vfs space; plain diffs pass through), orders
34
+ * every entry by (changedAt, input order), and applies the entries to a
35
+ * fresh fork with the stock filesystem operations — the same codepath
36
+ * the forks themselves used, over the same lower. Anything the
37
+ * filesystem refuses (write under a file, chmod of a deleted path, ...)
38
+ * is skipped, which keeps conflicting (racy) input deterministic and
39
+ * contained to the conflicted path. After each replayed operation the
40
+ * touched node's changedAt is rewritten from the replay position, so
41
+ * merged output is byte-deterministic and wall-clock-free. The merged
42
+ * fork is returned: diff it and discard it, or keep working on it.
43
+ *
44
+ * ```ts
45
+ * const tpl = createVfsTemplate({ mounts: [{ at: "/project", root: projDir }] });
46
+ * const a = tpl.fork();
47
+ * const b = tpl.fork();
48
+ * await Promise.all([runAgent(a), runAgent(b)]);
49
+ * const merged = await tpl.merge([a, b]);
50
+ * tpl.apply(merged.diff({ space: "host" }));
51
+ * ```
52
+ */
53
+ export interface VfsTemplate {
54
+ /** A fresh per-call filesystem: shared scratch + fresh overlays. */
55
+ fork(): MountableFs;
56
+ /**
57
+ * Replay the given sources' change sets onto a fresh fork and return
58
+ * it. Sources may be fork instances (diffed in vfs space) or plain
59
+ * diffs (e.g. serialized across a process boundary). Array order is
60
+ * the tie-break for equal changedAt stamps. Entries outside every
61
+ * mount point replay into the shared scratch (ordinary fork
62
+ * semantics, never reported in diff()) — merge expects diffs in
63
+ * this template's vfs space.
64
+ */
65
+ merge(sources: Array<MountableFs | OverlayDiff>): Promise<MountableFs>;
66
+ /**
67
+ * Validate a merged host-space diff against the mount map (every
68
+ * entry inside a registered root, no symlink writes, no root-self
69
+ * deletion) and apply it to the real filesystem. Type-conflicting
70
+ * on-disk targets are replaced (the change-set supersedes the base).
71
+ */
72
+ apply(merged: OverlayDiff): void;
73
+ }
74
+ export declare function createVfsTemplate(options: VfsTemplateOptions): VfsTemplate;
package/dist/index.d.cts CHANGED
@@ -8,11 +8,13 @@ export type { CommandContextOptions, CustomCommand, LazyCommand, } from "./custo
8
8
  export { createCommandContext, defineCommand, } from "./custom-commands.js";
9
9
  export type { ByteString, OutputKind } from "./encoding.js";
10
10
  export { bytesOutput, decodeBytesToUtf8, EMPTY_BYTES, encodeUtf8ToBytes, latin1FromBytes, stdoutAsBytes, stdoutKind, textOutput, unsafeBytesFromLatin1, } from "./encoding.js";
11
+ export { FsError, fsErrorCode, isFsErrorCode, toFsError, } from "./fs/fs-error.js";
11
12
  export { InMemoryFs } from "./fs/in-memory-fs/index.js";
12
13
  export type { BufferEncoding, CpOptions, DirectoryEntry, FileContent, FileEntry, FileInit, FileSystemFactory, FsEntry, FsStat, InitialFiles, LazyFileEntry, LazyFileProvider, MkdirOptions, RmOptions, SymlinkEntry, } from "./fs/interface.js";
13
14
  export { MountableFs, type MountableFsOptions, type MountConfig, } from "./fs/mountable-fs/index.js";
14
- export { type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./fs/overlay-fs/index.js";
15
+ export { applyDiffToRealFs, type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./fs/overlay-fs/index.js";
15
16
  export { ReadWriteFs, type ReadWriteFsOptions, } from "./fs/read-write-fs/index.js";
17
+ export { createVfsTemplate, type VfsTemplate, type VfsTemplateMount, type VfsTemplateOptions, } from "./fs/vfs-template.js";
16
18
  export type { AllowedUrl, AllowedUrlEntry, NetworkConfig, RequestTransform, SecureFetch, } from "./network/index.js";
17
19
  export { NetworkAccessDeniedError, RedirectNotAllowedError, TooManyRedirectsError, } from "./network/index.js";
18
20
  export { parse } from "./parser/parser.js";
package/dist/index.d.ts CHANGED
@@ -8,11 +8,13 @@ export type { CommandContextOptions, CustomCommand, LazyCommand, } from "./custo
8
8
  export { createCommandContext, defineCommand, } from "./custom-commands.js";
9
9
  export type { ByteString, OutputKind } from "./encoding.js";
10
10
  export { bytesOutput, decodeBytesToUtf8, EMPTY_BYTES, encodeUtf8ToBytes, latin1FromBytes, stdoutAsBytes, stdoutKind, textOutput, unsafeBytesFromLatin1, } from "./encoding.js";
11
+ export { FsError, fsErrorCode, isFsErrorCode, toFsError, } from "./fs/fs-error.js";
11
12
  export { InMemoryFs } from "./fs/in-memory-fs/index.js";
12
13
  export type { BufferEncoding, CpOptions, DirectoryEntry, FileContent, FileEntry, FileInit, FileSystemFactory, FsEntry, FsStat, InitialFiles, LazyFileEntry, LazyFileProvider, MkdirOptions, RmOptions, SymlinkEntry, } from "./fs/interface.js";
13
14
  export { MountableFs, type MountableFsOptions, type MountConfig, } from "./fs/mountable-fs/index.js";
14
- export { type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./fs/overlay-fs/index.js";
15
+ export { applyDiffToRealFs, type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./fs/overlay-fs/index.js";
15
16
  export { ReadWriteFs, type ReadWriteFsOptions, } from "./fs/read-write-fs/index.js";
17
+ export { createVfsTemplate, type VfsTemplate, type VfsTemplateMount, type VfsTemplateOptions, } from "./fs/vfs-template.js";
16
18
  export type { AllowedUrl, AllowedUrlEntry, NetworkConfig, RequestTransform, SecureFetch, } from "./network/index.js";
17
19
  export { NetworkAccessDeniedError, RedirectNotAllowedError, TooManyRedirectsError, } from "./network/index.js";
18
20
  export { parse } from "./parser/parser.js";
@@ -21,6 +21,16 @@ export type ExecuteUserScriptFn = (scriptPath: string, args: string[], stdin?: s
21
21
  /**
22
22
  * Dispatch context containing dependencies needed for builtin dispatch
23
23
  */
24
+ /** Stdio contract passed to builtin handlers. */
25
+ export interface BuiltinIo {
26
+ stdin: string;
27
+ /** True when a redirection gave this command its own fd 0. `stdin`
28
+ * alone cannot express it: `cmd < empty-file` and an unredirected
29
+ * command both arrive as `""`, but only the first means EOF rather
30
+ * than "inherit the shell's stdin". */
31
+ stdinRedirected: boolean;
32
+ stdinSourceFd: number;
33
+ }
24
34
  export interface BuiltinDispatchContext {
25
35
  ctx: InterpreterContext;
26
36
  runCommand: RunCommandFn;