From 086e37ef6691934b26704e8e71d38346ae31450f Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Tue, 7 Apr 2026 00:12:13 +0000 Subject: [PATCH] feat(js): expose mounts option, mountReal, and unmount on wrapper Adds mounts option to BashOptions for constructor-time real FS mounts. Exposes mountReal() and unmount() methods on both Bash and BashTool wrapper classes, achieving parity with the Python bindings' mount API. Closes #1128 --- .../__test__/runtime-compat/vfs.test.mjs | 13 +++++++ crates/bashkit-js/wrapper.ts | 38 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/crates/bashkit-js/__test__/runtime-compat/vfs.test.mjs b/crates/bashkit-js/__test__/runtime-compat/vfs.test.mjs index fd333e39..b88171c4 100644 --- a/crates/bashkit-js/__test__/runtime-compat/vfs.test.mjs +++ b/crates/bashkit-js/__test__/runtime-compat/vfs.test.mjs @@ -121,4 +121,17 @@ describe("VFS API", () => { const entries = fs.readDir("/tmp"); assert.ok(entries.some((e) => e.name === "fsapi.txt")); }); + + it("mountReal and unmount", () => { + const bash = new Bash(); + // Mount /tmp as a real filesystem at /host-tmp + bash.mountReal("/tmp", "/host-tmp", true); + // The mount should be accessible + const r = bash.executeSync("ls /host-tmp 2>/dev/null; echo status=$?"); + assert.ok(r.stdout.includes("status=0")); + // Unmount + bash.unmount("/host-tmp"); + const r2 = bash.executeSync("ls /host-tmp 2>/dev/null; echo status=$?"); + assert.ok(r2.stdout.includes("status=")); + }); }); diff --git a/crates/bashkit-js/wrapper.ts b/crates/bashkit-js/wrapper.ts index 613b78d9..5d27fd86 100644 --- a/crates/bashkit-js/wrapper.ts +++ b/crates/bashkit-js/wrapper.ts @@ -63,6 +63,19 @@ export interface BashOptions { * ``` */ files?: Record; + /** + * Real filesystem mounts. Each mount maps a host directory into the VFS. + * + * @example + * ```typescript + * const bash = new Bash({ + * mounts: [ + * { path: "/docs", root: "/real/path/to/docs", readOnly: true }, + * ], + * }); + * ``` + */ + mounts?: Array<{ path: string; root: string; readOnly?: boolean }>; /** * Enable embedded Python execution (`python`/`python3` builtins). * @@ -136,6 +149,11 @@ function toNativeOptions( maxLoopIterations: options?.maxLoopIterations, maxMemory: options?.maxMemory, files: resolvedFiles, + mounts: options?.mounts?.map((m) => ({ + hostPath: m.root, + vfsPath: m.path, + readOnly: m.readOnly, + })), python: options?.python, externalFunctions: options?.externalFunctions, }; @@ -403,6 +421,16 @@ export class Bash { return this.native.fs(); } + /** Mount a host directory into the VFS. readOnly defaults to true. */ + mountReal(hostPath: string, vfsPath: string, readOnly?: boolean): void { + this.native.mountReal(hostPath, vfsPath, readOnly); + } + + /** Unmount a previously mounted filesystem. */ + unmount(vfsPath: string): void { + this.native.unmount(vfsPath); + } + /** * List entry names in a directory. Returns empty array if directory does not exist. */ @@ -625,6 +653,16 @@ export class BashTool { return this.native.fs(); } + /** Mount a host directory into the VFS. readOnly defaults to true. */ + mountReal(hostPath: string, vfsPath: string, readOnly?: boolean): void { + this.native.mountReal(hostPath, vfsPath, readOnly); + } + + /** Unmount a previously mounted filesystem. */ + unmount(vfsPath: string): void { + this.native.unmount(vfsPath); + } + /** * List entry names in a directory. Returns empty array if directory does not exist. */