@jerryan/just-bash 3.5.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +334 -308
  2. package/LICENSE +201 -201
  3. package/README.md +827 -827
  4. package/dist/agent-sandbox.d.ts +27 -0
  5. package/dist/bin/chunks/{chunk-7WBCA2BB.js → chunk-3DWQCLNP.js} +1 -1
  6. package/dist/bin/chunks/{chunk-QFTZKD5O.js → chunk-6TUILMPB.js} +1 -1
  7. package/dist/bin/chunks/chunk-JHEAT7AR.js +12 -0
  8. package/dist/bin/chunks/{chunk-MF3GVFLC.js → chunk-L345YZ6R.js} +1 -1
  9. package/dist/bin/chunks/chunk-QKJAAJ7J.js +3 -0
  10. package/dist/bin/chunks/chunk-SE7JHGP3.js +9 -0
  11. package/dist/bin/{shell/chunks/chunk-NLU5LALY.js → chunks/chunk-YCOJCFGU.js} +1 -1
  12. package/dist/bin/{shell/chunks/flag-coverage-J2ZHA465.js → chunks/flag-coverage-FYJ2V7J4.js} +1 -1
  13. package/dist/bin/chunks/{js-exec-DJQLXWCS.js → js-exec-5QZZCOR7.js} +7 -7
  14. package/dist/bin/chunks/js-exec-worker.js +155 -33
  15. package/dist/bin/{shell/chunks/python3-JCJHSX4Z.js → chunks/python3-TMFYDQAP.js} +1 -1
  16. package/dist/bin/chunks/{rm-3PLW4O4B.js → rm-NLG5344W.js} +1 -1
  17. package/dist/bin/chunks/{sleep-DVFE2GSF.js → sleep-MOJQBZMY.js} +1 -1
  18. package/dist/bin/{shell/chunks/sqlite3-KP42NNIY.js → chunks/sqlite3-3EUPHVEZ.js} +1 -1
  19. package/dist/bin/chunks/{timeout-YGFHQJKX.js → timeout-RQOWSWYG.js} +1 -1
  20. package/dist/bin/chunks/worker.js +162 -48
  21. package/dist/bin/just-bash.js +307 -307
  22. package/dist/bin/shell/chunks/{chunk-7WBCA2BB.js → chunk-3DWQCLNP.js} +1 -1
  23. package/dist/bin/shell/chunks/{chunk-QFTZKD5O.js → chunk-6TUILMPB.js} +1 -1
  24. package/dist/bin/shell/chunks/chunk-JHEAT7AR.js +12 -0
  25. package/dist/bin/shell/chunks/{chunk-MF3GVFLC.js → chunk-L345YZ6R.js} +1 -1
  26. package/dist/bin/shell/chunks/chunk-QKJAAJ7J.js +3 -0
  27. package/dist/bin/shell/chunks/chunk-SE7JHGP3.js +9 -0
  28. package/dist/bin/{chunks/chunk-NLU5LALY.js → shell/chunks/chunk-YCOJCFGU.js} +1 -1
  29. package/dist/bin/{chunks/flag-coverage-J2ZHA465.js → shell/chunks/flag-coverage-FYJ2V7J4.js} +1 -1
  30. package/dist/bin/shell/chunks/{js-exec-DJQLXWCS.js → js-exec-5QZZCOR7.js} +7 -7
  31. package/dist/bin/{chunks/python3-JCJHSX4Z.js → shell/chunks/python3-TMFYDQAP.js} +1 -1
  32. package/dist/bin/shell/chunks/{rm-3PLW4O4B.js → rm-NLG5344W.js} +1 -1
  33. package/dist/bin/shell/chunks/{sleep-DVFE2GSF.js → sleep-MOJQBZMY.js} +1 -1
  34. package/dist/bin/{chunks/sqlite3-KP42NNIY.js → shell/chunks/sqlite3-3EUPHVEZ.js} +1 -1
  35. package/dist/bin/shell/chunks/{timeout-YGFHQJKX.js → timeout-RQOWSWYG.js} +1 -1
  36. package/dist/bin/shell/shell.js +237 -237
  37. package/dist/bundle/browser.js +240 -240
  38. package/dist/bundle/chunks/{chunk-N3A4JVFJ.js → chunk-6IA4DXG2.js} +1 -1
  39. package/dist/bundle/chunks/chunk-G3IRZ4FN.js +8 -0
  40. package/dist/bundle/chunks/{chunk-24NLAR7R.js → chunk-KEMO3CBM.js} +1 -1
  41. package/dist/bundle/chunks/chunk-PS2RJNZZ.js +2 -0
  42. package/dist/bundle/chunks/{chunk-DMWNBCXJ.js → chunk-TLPMB3FO.js} +1 -1
  43. package/dist/bundle/chunks/chunk-ULXGGWAD.js +11 -0
  44. package/dist/bundle/chunks/{chunk-XV2V56NQ.js → chunk-YFFU74KY.js} +1 -1
  45. package/dist/bundle/chunks/{flag-coverage-DPSAPFXA.js → flag-coverage-XSPWRCVG.js} +1 -1
  46. package/dist/bundle/chunks/{js-exec-XKF2B7C3.js → js-exec-57QMXJG5.js} +7 -7
  47. package/dist/bundle/chunks/js-exec-worker.js +155 -33
  48. package/dist/bundle/chunks/{python3-F5S5TYGS.js → python3-NNONJINM.js} +1 -1
  49. package/dist/bundle/chunks/{rm-JQ4GDKDD.js → rm-R2ZENNSN.js} +1 -1
  50. package/dist/bundle/chunks/{sleep-PPRBTSVR.js → sleep-25QUPVDK.js} +1 -1
  51. package/dist/bundle/chunks/{sqlite3-4EZ4GXVD.js → sqlite3-M4UZPXDO.js} +1 -1
  52. package/dist/bundle/chunks/{timeout-C6WDYPLV.js → timeout-37GM7IIL.js} +1 -1
  53. package/dist/bundle/chunks/worker.js +162 -48
  54. package/dist/bundle/index.cjs +760 -760
  55. package/dist/bundle/index.js +306 -306
  56. package/dist/commands/js-exec/js-exec.d.ts +4 -0
  57. package/dist/commands/worker-bridge/bridge-handler.d.ts +9 -0
  58. package/dist/commands/worker-bridge/protocol.d.ts +17 -0
  59. package/dist/commands/worker-bridge/sync-backend.d.ts +3 -0
  60. package/dist/fs/overlay-fs/apply.d.ts +13 -0
  61. package/dist/fs/overlay-fs/index.d.ts +2 -0
  62. package/dist/fs/overlay-fs/merge.d.ts +24 -0
  63. package/dist/fs/overlay-fs/overlay-fs.d.ts +17 -0
  64. package/dist/fs/overlay-fs/overlay-tree.d.ts +39 -0
  65. package/dist/fs/real-fs-utils.d.ts +15 -0
  66. package/dist/fs/vfs-template.d.ts +51 -0
  67. package/dist/index.d.cts +2 -1
  68. package/dist/index.d.ts +2 -1
  69. package/dist/timers.d.ts +9 -0
  70. package/package.json +32 -33
  71. package/vendor/cpython-emscripten/python.cjs +2 -2
  72. package/dist/bin/chunks/chunk-JQM3DKCH.js +0 -12
  73. package/dist/bin/chunks/chunk-PPVF2N6C.js +0 -3
  74. package/dist/bin/chunks/chunk-YOIFOOGX.js +0 -9
  75. package/dist/bin/shell/chunks/chunk-JQM3DKCH.js +0 -12
  76. package/dist/bin/shell/chunks/chunk-PPVF2N6C.js +0 -3
  77. package/dist/bin/shell/chunks/chunk-YOIFOOGX.js +0 -9
  78. package/dist/bundle/chunks/chunk-LPJEKUCB.js +0 -8
  79. package/dist/bundle/chunks/chunk-WXPNKIIH.js +0 -2
  80. package/dist/bundle/chunks/chunk-YJKHREBZ.js +0 -11
@@ -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;
@@ -29,6 +29,10 @@ export declare class BridgeHandler {
29
29
  private outputLimitExceeded;
30
30
  private startTime;
31
31
  private timeoutMs;
32
+ /** Complete buffer of the last published oversized result, retained
33
+ * so the worker can fetch it in READ_RESULT_RANGE slices. Replaced on
34
+ * every publish. */
35
+ private lastResult;
32
36
  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
37
  /**
34
38
  * Returns remaining milliseconds before the overall execution deadline.
@@ -45,8 +49,13 @@ export declare class BridgeHandler {
45
49
  run(timeoutMs: number): Promise<BridgeOutput>;
46
50
  stop(): void;
47
51
  private handleOperation;
52
+ /** Publish a result of any size; oversized results are fetched by the
53
+ * worker in READ_RESULT_RANGE slices from the retained buffer. */
54
+ private publishResult;
55
+ private handleReadResultRange;
48
56
  private resolvePath;
49
57
  private handleReadFile;
58
+ private handleWriteFileRange;
50
59
  private handleWriteFile;
51
60
  private handleStat;
52
61
  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;
@@ -62,8 +64,16 @@ export declare const ErrorCode: {
62
64
  readonly TIMEOUT: 8;
63
65
  readonly NETWORK_ERROR: 9;
64
66
  readonly NETWORK_NOT_CONFIGURED: 10;
67
+ readonly NOT_EMPTY: 11;
65
68
  };
66
69
  export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
70
+ /** Buffer sizes */
71
+ export declare const Size: {
72
+ readonly CONTROL_REGION: 32;
73
+ readonly PATH_BUFFER: 4096;
74
+ readonly DATA_BUFFER: 8388608;
75
+ readonly TOTAL: 8392736;
76
+ };
67
77
  /** Flags for operations */
68
78
  export declare const Flags: {
69
79
  readonly NONE: 0;
@@ -104,6 +114,13 @@ export declare class ProtocolBuffer {
104
114
  setDataFromString(str: string): void;
105
115
  getResult(): Uint8Array;
106
116
  setResult(data: Uint8Array): void;
117
+ /**
118
+ * Publish a result of ANY size: the first DATA_BUFFER bytes land in
119
+ * the data region and RESULT_LENGTH carries the FULL length. The
120
+ * worker detects the overflow and fetches the rest with
121
+ * READ_RESULT_RANGE ops (served from the host's retained buffer).
122
+ */
123
+ setResultPrefix(data: Uint8Array): void;
107
124
  getResultAsString(): string;
108
125
  setResultFromString(str: string): void;
109
126
  encodeStat(stat: {
@@ -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): {
@@ -20,3 +20,16 @@ export declare function removeFromRealFs(path: string): void;
20
20
  * mtime restored after the content write.
21
21
  */
22
22
  export declare function applyWriteToRealFs(path: string, write: Omit<OverlayWrite, "path">): void;
23
+ /**
24
+ * Apply a change set with real absolute paths to the real filesystem:
25
+ * deletions first (deepest path first, so subtrees go before their
26
+ * parents), then writes (shallowest first, so explicit directory
27
+ * entries land before their children). Standalone counterpart of
28
+ * AgentSandbox.applyChanges for the fork model — no overlay instance
29
+ * is involved and nothing is dropped; the caller owns the overlays'
30
+ * lifecycle (typically: discard after diff()).
31
+ */
32
+ export declare function applyDiffToRealFs(diff: {
33
+ writes: OverlayWrite[];
34
+ deletions: string[];
35
+ }): void;
@@ -1 +1,3 @@
1
+ export { applyDiffToRealFs } from "./apply.js";
2
+ export { mergeDiffs } from "./merge.js";
1
3
  export { type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./overlay-fs.js";
@@ -0,0 +1,24 @@
1
+ import type { OverlayDiff } from "./overlay-fs.js";
2
+ /**
3
+ * Merge change sets from independent overlays into one deterministic
4
+ * change set. Paths must already share one space (e.g. real absolute
5
+ * paths — map mount-relative diffs through each mount's root first).
6
+ *
7
+ * Semantics (see docs/design/vfs-template-fork-model.md):
8
+ *
9
+ * - Ordering key per entry: `changedAt` (overlay-assigned, untamperable;
10
+ * missing stamps count as 0), ties broken by input order — later wins.
11
+ * - One winner per path (the latest entry).
12
+ * - A winning whiteout suppresses earlier entries under its subtree;
13
+ * later entries survive and the path resurrects as their scaffolding
14
+ * (overlayfs opaque-dir semantics).
15
+ * - A winning file/symlink drops every entry under its path, regardless
16
+ * of time — files cannot have children.
17
+ * - Scaffolding and metadataOnly need no special rules: ensured-parent
18
+ * dir entries only ever win alongside their fork's surviving
19
+ * descendants, and a metadataOnly winner already carries the metadata.
20
+ *
21
+ * Conflicts are racy input by definition; this merge owes determinism,
22
+ * not correctness. It never throws on malformed combinations.
23
+ */
24
+ export declare function mergeDiffs(diffs: OverlayDiff[]): OverlayDiff;
@@ -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
  /**
@@ -285,6 +299,9 @@ export declare class OverlayFs implements IFileSystem {
285
299
  */
286
300
  private inheritedMode;
287
301
  appendFile(path: string, content: FileContent, options?: WriteFileOptions | BufferEncoding): Promise<void>;
302
+ /** Append to an existing upper file node, completing a metacopy
303
+ * copy-up first when needed (preserving the node's upper mode). */
304
+ private appendToExisting_;
288
305
  exists(path: string): Promise<boolean>;
289
306
  stat(path: string, seen?: Set<string>): Promise<FsStat>;
290
307
  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.
@@ -9,6 +9,7 @@
9
9
  * live in path-utils.ts (no node:fs dependency, browser-safe) and are
10
10
  * re-exported here for convenience.
11
11
  */
12
+ import * as fs from "node:fs";
12
13
  export { isSameOrDescendantPath, normalizePath, validatePath, } from "./path-utils.js";
13
14
  /**
14
15
  * Check whether `resolved` is equal to, or a child of, `canonicalRoot`.
@@ -88,3 +89,17 @@ export declare function sanitizeSymlinkTarget(rawTarget: string, canonicalRoot:
88
89
  * a generic `"CODE: operation 'virtualPath'"` message.
89
90
  */
90
91
  export declare function sanitizeFsError(e: unknown, virtualPath: string, operation: string, passthroughPatterns: readonly string[]): never;
92
+ /**
93
+ * lstat a validated real path, normalizing Windows mode fiction: libuv
94
+ * reports no execute bits on Windows, so directories stat as 0o40666.
95
+ * Nothing inside the sandbox enforces mode bits, but real POSIX runtimes
96
+ * behind the fs bridge do — the python worker's Emscripten VFS masks
97
+ * `mode & 0o777`, making a 0o666 directory untraversable (chdir, listdir,
98
+ * open all EACCES). Report POSIX-conventional 0o755 for directories,
99
+ * matching DEFAULT_DIR_MODE so lower and upper dirs present uniformly.
100
+ * File modes are left as reported (executable-bit fiction for .exe etc.
101
+ * is a separate question). No-op on other platforms.
102
+ */
103
+ export declare function lstatReal(path: string): Promise<fs.Stats>;
104
+ /** Synchronous counterpart of {@link lstatReal}. */
105
+ export declare function lstatRealSync(path: string): fs.Stats;
@@ -0,0 +1,51 @@
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 in-memory scratch fs and behaves like
14
+ * shared memory: visible to every fork immediately.
15
+ */
16
+ mounts: VfsTemplateMount[];
17
+ }
18
+ /**
19
+ * A process image for parallel agent tool calls (see
20
+ * docs/design/vfs-template-fork-model.md). The template owns the shared
21
+ * scratch fs and the mount configuration; each fork() stamps out a
22
+ * ready MountableFs with fresh copy-on-write overlays over every
23
+ * mounted real root. Forks are single-use: run, diff via merge(),
24
+ * discard.
25
+ *
26
+ * ```ts
27
+ * const tpl = createVfsTemplate({ mounts: [{ at: "/project", root: projDir }] });
28
+ * const a = tpl.fork();
29
+ * const b = tpl.fork();
30
+ * await Promise.all([runA(a), runB(b)]);
31
+ * const merged = tpl.merge([a, b]); // completion order = tiebreak
32
+ * tpl.apply(merged);
33
+ * ```
34
+ */
35
+ export interface VfsTemplate {
36
+ /** A fresh per-call filesystem: shared scratch + fresh overlays. */
37
+ fork(): MountableFs;
38
+ /**
39
+ * Merge the change sets of the given forks (in completion order —
40
+ * the tiebreak under changedAt) into one diff with real absolute
41
+ * paths. Forks not listed are merged after, in registration order.
42
+ */
43
+ merge(forksInCompletionOrder?: MountableFs[]): OverlayDiff;
44
+ /**
45
+ * Validate a merged diff against the mount map (an entry outside
46
+ * every registered root fails loudly) and apply it to the real
47
+ * roots. Consumes all registered forks.
48
+ */
49
+ apply(merged: OverlayDiff): void;
50
+ }
51
+ export declare function createVfsTemplate(options: VfsTemplateOptions): VfsTemplate;
package/dist/index.d.cts CHANGED
@@ -11,8 +11,9 @@ export { bytesOutput, decodeBytesToUtf8, EMPTY_BYTES, encodeUtf8ToBytes, latin1F
11
11
  export { InMemoryFs } from "./fs/in-memory-fs/index.js";
12
12
  export type { BufferEncoding, CpOptions, DirectoryEntry, FileContent, FileEntry, FileInit, FileSystemFactory, FsEntry, FsStat, InitialFiles, LazyFileEntry, LazyFileProvider, MkdirOptions, RmOptions, SymlinkEntry, } from "./fs/interface.js";
13
13
  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";
14
+ export { applyDiffToRealFs, mergeDiffs, type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./fs/overlay-fs/index.js";
15
15
  export { ReadWriteFs, type ReadWriteFsOptions, } from "./fs/read-write-fs/index.js";
16
+ export { createVfsTemplate, type VfsTemplate, type VfsTemplateMount, type VfsTemplateOptions, } from "./fs/vfs-template.js";
16
17
  export type { AllowedUrl, AllowedUrlEntry, NetworkConfig, RequestTransform, SecureFetch, } from "./network/index.js";
17
18
  export { NetworkAccessDeniedError, RedirectNotAllowedError, TooManyRedirectsError, } from "./network/index.js";
18
19
  export { parse } from "./parser/parser.js";
package/dist/index.d.ts CHANGED
@@ -11,8 +11,9 @@ export { bytesOutput, decodeBytesToUtf8, EMPTY_BYTES, encodeUtf8ToBytes, latin1F
11
11
  export { InMemoryFs } from "./fs/in-memory-fs/index.js";
12
12
  export type { BufferEncoding, CpOptions, DirectoryEntry, FileContent, FileEntry, FileInit, FileSystemFactory, FsEntry, FsStat, InitialFiles, LazyFileEntry, LazyFileProvider, MkdirOptions, RmOptions, SymlinkEntry, } from "./fs/interface.js";
13
13
  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";
14
+ export { applyDiffToRealFs, mergeDiffs, type OverlayDiff, OverlayFs, type OverlayFsOptions, type OverlayNodeType, type OverlayWrite, } from "./fs/overlay-fs/index.js";
15
15
  export { ReadWriteFs, type ReadWriteFsOptions, } from "./fs/read-write-fs/index.js";
16
+ export { createVfsTemplate, type VfsTemplate, type VfsTemplateMount, type VfsTemplateOptions, } from "./fs/vfs-template.js";
16
17
  export type { AllowedUrl, AllowedUrlEntry, NetworkConfig, RequestTransform, SecureFetch, } from "./network/index.js";
17
18
  export { NetworkAccessDeniedError, RedirectNotAllowedError, TooManyRedirectsError, } from "./network/index.js";
18
19
  export { parse } from "./parser/parser.js";
package/dist/timers.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  export declare const _setTimeout: typeof globalThis.setTimeout;
2
2
  export declare const _clearTimeout: typeof globalThis.clearTimeout;
3
+ /**
4
+ * Native setTimeout WITHOUT execution-context binding. `_setTimeout`
5
+ * binds callbacks to the scheduling execution's defense context, which
6
+ * DefenseInDepthBox suppresses once that execution deactivates — correct
7
+ * for intra-execution timers (sleep, fetch deadlines), fatal for host
8
+ * infrastructure callbacks that must fire AFTER the execution ends
9
+ * (e.g. idle teardown of a shared worker). Use this for the latter.
10
+ */
11
+ export declare const _setTimeoutUnbound: typeof globalThis.setTimeout;
3
12
  export interface FiniteTimeoutHandle {
4
13
  cleared: boolean;
5
14
  remainingMs: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jerryan/just-bash",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "description": "A simulated bash environment with virtual filesystem",
5
5
  "repository": {
6
6
  "type": "git",
@@ -62,37 +62,6 @@
62
62
  "access": "public",
63
63
  "registry": "https://registry.npmjs.org/"
64
64
  },
65
- "scripts": {
66
- "build": "rm -rf dist && tsc && pnpm build:lib && pnpm build:lib:cjs && pnpm build:browser && pnpm build:cli && pnpm build:shell && pnpm build:worker && pnpm build:clean && cp dist/index.d.ts dist/index.d.cts && sed '1,/^-->/d' AGENTS.npm.md > dist/AGENTS.md",
67
- "build:clean": "find dist -name '*.test.js' -delete && find dist -name '*.test.d.ts' -delete",
68
- "build:worker": "esbuild src/commands/python3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/python3/worker.js --external:../../../vendor/cpython-emscripten/* && cp src/commands/python3/worker.js dist/commands/python3/worker.js && mkdir -p dist/bin/chunks && cp src/commands/python3/worker.js dist/bin/chunks/worker.js && mkdir -p dist/bundle/chunks && cp src/commands/python3/worker.js dist/bundle/chunks/worker.js && esbuild src/commands/js-exec/js-exec-worker.ts --bundle --platform=node --format=esm --outfile=src/commands/js-exec/js-exec-worker.js --external:quickjs-emscripten && cp src/commands/js-exec/js-exec-worker.js dist/commands/js-exec/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bin/chunks/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bundle/chunks/js-exec-worker.js && esbuild src/commands/sqlite3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/sqlite3/worker.js --external:sql.js && mkdir -p dist/commands/sqlite3 && cp src/commands/sqlite3/worker.js dist/commands/sqlite3/worker.js && cp src/commands/sqlite3/worker.js dist/bin/chunks/sqlite3-worker.js && cp src/commands/sqlite3/worker.js dist/bundle/chunks/sqlite3-worker.js",
69
- "build:lib": "esbuild dist/index.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bundle --chunk-names=chunks/[name]-[hash] --banner:js='import{createRequire} from\"node:module\";const require=createRequire(import.meta.url);' --external:diff --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --external:guarded-fetch",
70
- "build:lib:cjs": "esbuild dist/index.js --bundle --platform=node --format=cjs --minify --outfile=dist/bundle/index.cjs --external:diff --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --external:guarded-fetch",
71
- "build:browser": "esbuild dist/browser.js --bundle --platform=browser --format=esm --minify --outfile=dist/bundle/browser.js --external:diff --external:sprintf-js --external:turndown --external:undici --external:guarded-fetch --external:node:zlib --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --define:__BROWSER__=true --alias:node:async_hooks=./src/shims/browser-unsupported.js --alias:node:dns=./src/shims/browser-unsupported.js --alias:node:module=./src/shims/browser-unsupported.js",
72
- "build:cli": "esbuild dist/cli/just-bash.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node\nimport{createRequire} from\"node:module\";const require=createRequire(import.meta.url);' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip",
73
- "build:shell": "esbuild dist/cli/shell.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin/shell --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node\nimport{createRequire} from\"node:module\";const require=createRequire(import.meta.url);' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip",
74
- "prepublishOnly": "pnpm test:dist",
75
- "validate": "pnpm lint && pnpm knip && pnpm typecheck && pnpm build && pnpm check:worker-sync && pnpm test:run && pnpm test:wasm && pnpm test:dist",
76
- "typecheck": "tsc --noEmit",
77
- "lint": "pnpm lint:banned",
78
- "check:worker-sync": "node scripts/check-worker-sync.js",
79
- "lint:banned": "node scripts/check-banned-patterns.js",
80
- "lint:fix": "pnpm --workspace-root lint:fix",
81
- "knip": "knip",
82
- "test": "vitest",
83
- "test:run": "vitest run --exclude src/security/fuzzing/ --exclude src/commands/python3/ --exclude src/commands/sqlite3/ --exclude src/commands/js-exec/ --exclude src/agent-examples/python-scripting.test.ts",
84
- "test:dist": "vitest run src/cli/just-bash.bundle.test.ts src/package-contents.test.ts",
85
- "test:unit": "vitest run --config vitest.unit.config.ts",
86
- "test:wasm": "vitest run --config vitest.wasm.config.ts",
87
- "test:comparison": "vitest run --config vitest.comparison.config.ts",
88
- "test:comparison:record": "RECORD_FIXTURES=1 vitest run --config vitest.comparison.config.ts",
89
- "test:coverage": "vitest run --coverage",
90
- "test:coverage:unit": "vitest run --config vitest.unit.config.ts --coverage",
91
- "test:fuzz": "vitest run src/security/fuzzing/",
92
- "test:fuzz:long": "FUZZ_RUNS=10000 vitest run src/security/fuzzing/",
93
- "shell": "tsx src/cli/shell.ts",
94
- "dev:exec": "tsx src/cli/exec.ts"
95
- },
96
65
  "keywords": [],
97
66
  "author": "Malte and Claude",
98
67
  "license": "Apache-2.0",
@@ -132,5 +101,35 @@
132
101
  "optionalDependencies": {
133
102
  "@mongodb-js/zstd": "^7.0.0",
134
103
  "node-liblzma": "^2.2.0"
104
+ },
105
+ "scripts": {
106
+ "build": "rm -rf dist && tsc && pnpm build:lib && pnpm build:lib:cjs && pnpm build:browser && pnpm build:cli && pnpm build:shell && pnpm build:worker && pnpm build:clean && cp dist/index.d.ts dist/index.d.cts && sed '1,/^-->/d' AGENTS.npm.md > dist/AGENTS.md",
107
+ "build:clean": "find dist -name '*.test.js' -delete && find dist -name '*.test.d.ts' -delete",
108
+ "build:worker": "esbuild src/commands/python3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/python3/worker.js --external:../../../vendor/cpython-emscripten/* && cp src/commands/python3/worker.js dist/commands/python3/worker.js && mkdir -p dist/bin/chunks && cp src/commands/python3/worker.js dist/bin/chunks/worker.js && mkdir -p dist/bundle/chunks && cp src/commands/python3/worker.js dist/bundle/chunks/worker.js && esbuild src/commands/js-exec/js-exec-worker.ts --bundle --platform=node --format=esm --outfile=src/commands/js-exec/js-exec-worker.js --external:quickjs-emscripten && cp src/commands/js-exec/js-exec-worker.js dist/commands/js-exec/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bin/chunks/js-exec-worker.js && cp src/commands/js-exec/js-exec-worker.js dist/bundle/chunks/js-exec-worker.js && esbuild src/commands/sqlite3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/sqlite3/worker.js --external:sql.js && mkdir -p dist/commands/sqlite3 && cp src/commands/sqlite3/worker.js dist/commands/sqlite3/worker.js && cp src/commands/sqlite3/worker.js dist/bin/chunks/sqlite3-worker.js && cp src/commands/sqlite3/worker.js dist/bundle/chunks/sqlite3-worker.js",
109
+ "build:lib": "esbuild dist/index.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bundle --chunk-names=chunks/[name]-[hash] --banner:js='import{createRequire} from\"node:module\";const require=createRequire(import.meta.url);' --external:diff --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --external:guarded-fetch",
110
+ "build:lib:cjs": "esbuild dist/index.js --bundle --platform=node --format=cjs --minify --outfile=dist/bundle/index.cjs --external:diff --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --external:guarded-fetch",
111
+ "build:browser": "esbuild dist/browser.js --bundle --platform=browser --format=esm --minify --outfile=dist/bundle/browser.js --external:diff --external:sprintf-js --external:turndown --external:undici --external:guarded-fetch --external:node:zlib --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip --define:__BROWSER__=true --alias:node:async_hooks=./src/shims/browser-unsupported.js --alias:node:dns=./src/shims/browser-unsupported.js --alias:node:module=./src/shims/browser-unsupported.js",
112
+ "build:cli": "esbuild dist/cli/just-bash.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node\nimport{createRequire} from\"node:module\";const require=createRequire(import.meta.url);' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip",
113
+ "build:shell": "esbuild dist/cli/shell.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin/shell --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node\nimport{createRequire} from\"node:module\";const require=createRequire(import.meta.url);' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:seek-bzip",
114
+ "validate": "pnpm lint && pnpm knip && pnpm typecheck && pnpm build && pnpm check:worker-sync && pnpm test:run && pnpm test:wasm && pnpm test:dist",
115
+ "typecheck": "tsc --noEmit",
116
+ "lint": "pnpm lint:banned",
117
+ "check:worker-sync": "node scripts/check-worker-sync.js",
118
+ "lint:banned": "node scripts/check-banned-patterns.js",
119
+ "lint:fix": "pnpm --workspace-root lint:fix",
120
+ "knip": "knip",
121
+ "test": "vitest",
122
+ "test:run": "vitest run --exclude src/security/fuzzing/ --exclude src/commands/python3/ --exclude src/commands/sqlite3/ --exclude src/commands/js-exec/ --exclude src/agent-examples/python-scripting.test.ts",
123
+ "test:dist": "vitest run src/cli/just-bash.bundle.test.ts src/package-contents.test.ts",
124
+ "test:unit": "vitest run --config vitest.unit.config.ts",
125
+ "test:wasm": "vitest run --config vitest.wasm.config.ts",
126
+ "test:comparison": "vitest run --config vitest.comparison.config.ts",
127
+ "test:comparison:record": "RECORD_FIXTURES=1 vitest run --config vitest.comparison.config.ts",
128
+ "test:coverage": "vitest run --coverage",
129
+ "test:coverage:unit": "vitest run --config vitest.unit.config.ts --coverage",
130
+ "test:fuzz": "vitest run src/security/fuzzing/",
131
+ "test:fuzz:long": "FUZZ_RUNS=10000 vitest run src/security/fuzzing/",
132
+ "shell": "tsx src/cli/shell.ts",
133
+ "dev:exec": "tsx src/cli/exec.ts"
135
134
  }
136
- }
135
+ }