just-bash 1.0.0 → 1.1.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.
package/README.md CHANGED
@@ -15,7 +15,8 @@ Supports optional network access via `curl` with secure-by-default URL filtering
15
15
  - [Usage](#usage)
16
16
  - [Basic API](#basic-api)
17
17
  - [Configuration](#configuration)
18
- - [OverlayFs (Copy-on-Write)](#overlayfs-copy-on-write)
18
+ - [Custom Commands](#custom-commands)
19
+ - [Filesystem Options](#filesystem-options)
19
20
  - [AI SDK Tool](#ai-sdk-tool)
20
21
  - [Vercel Sandbox Compatible API](#vercel-sandbox-compatible-api)
21
22
  - [CLI Binary](#cli-binary)
@@ -72,30 +73,69 @@ const env = new Bash({
72
73
  await env.exec("echo $TEMP", { env: { TEMP: "value" }, cwd: "/tmp" });
73
74
  ```
74
75
 
75
- ### OverlayFs (Copy-on-Write)
76
+ ### Custom Commands
76
77
 
77
- Seed the bash environment with files from a real directory. The agent can read but not write to the real filesystem - all changes stay in memory.
78
+ Extend just-bash with your own TypeScript commands using `defineCommand`:
78
79
 
79
80
  ```typescript
80
- import { Bash, OverlayFs } from "just-bash";
81
+ import { Bash, defineCommand } from "just-bash";
82
+
83
+ const hello = defineCommand("hello", async (args, ctx) => {
84
+ const name = args[0] || "world";
85
+ return { stdout: `Hello, ${name}!\n`, stderr: "", exitCode: 0 };
86
+ });
87
+
88
+ const upper = defineCommand("upper", async (args, ctx) => {
89
+ return { stdout: ctx.stdin.toUpperCase(), stderr: "", exitCode: 0 };
90
+ });
91
+
92
+ const bash = new Bash({ customCommands: [hello, upper] });
93
+
94
+ await bash.exec("hello Alice"); // "Hello, Alice!\n"
95
+ await bash.exec("echo 'test' | upper"); // "TEST\n"
96
+ ```
97
+
98
+ Custom commands receive the full `CommandContext` with access to `fs`, `cwd`, `env`, `stdin`, and `exec` for running subcommands.
99
+
100
+ ### Filesystem Options
101
+
102
+ Three filesystem implementations are available:
103
+
104
+ **InMemoryFs** (default) - Pure in-memory filesystem, no disk access:
105
+
106
+ ```typescript
107
+ import { Bash } from "just-bash";
108
+ const env = new Bash(); // Uses InMemoryFs by default
109
+ ```
110
+
111
+ **OverlayFs** - Copy-on-write over a real directory. Reads come from disk, writes stay in memory:
112
+
113
+ ```typescript
114
+ import { Bash } from "just-bash";
115
+ import { OverlayFs } from "just-bash/fs/overlay-fs";
81
116
 
82
- // Files are mounted at /home/user/project by default
83
117
  const overlay = new OverlayFs({ root: "/path/to/project" });
84
118
  const env = new Bash({ fs: overlay, cwd: overlay.getMountPoint() });
85
119
 
86
- // Reads come from the real filesystem
87
- await env.exec("cat package.json"); // reads /path/to/project/package.json
120
+ await env.exec("cat package.json"); // reads from disk
121
+ await env.exec('echo "modified" > package.json'); // stays in memory
122
+ ```
123
+
124
+ **ReadWriteFs** - Direct read-write access to a real directory. Use this if you want the agent to be agle to write to your disk:
125
+
126
+ ```typescript
127
+ import { Bash } from "just-bash";
128
+ import { ReadWriteFs } from "just-bash/fs/read-write-fs";
88
129
 
89
- // Writes stay in memory (real files unchanged)
90
- await env.exec('echo "modified" > package.json');
130
+ const rwfs = new ReadWriteFs({ root: "/path/to/sandbox" });
131
+ const env = new Bash({ fs: rwfs });
91
132
 
92
- // Custom mount point
93
- const overlay2 = new OverlayFs({ root: "/path/to/project", mountPoint: "/" });
133
+ await env.exec('echo "hello" > file.txt'); // writes to real filesystem
94
134
  ```
95
135
 
96
136
  ### AI SDK Tool
97
137
 
98
- Creates a bash tool for use with the [AI SDK](https://ai-sdk.dev/):
138
+ Creates a bash tool for use with the [AI SDK](https://ai-sdk.dev/), because [agents love bash](https://vercel.com/blog/we-removed-80-percent-of-our-agents-tools).
99
139
 
100
140
  ```typescript
101
141
  import { createBashTool } from "just-bash/ai";
package/dist/Bash.d.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * and delegates execution to the Interpreter.
9
9
  */
10
10
  import { type CommandName } from "./commands/registry.js";
11
- import { type IFileSystem } from "./fs.js";
12
- import type { InitialFiles } from "./fs-interface.js";
11
+ import { type CustomCommand } from "./custom-commands.js";
12
+ import type { IFileSystem, InitialFiles } from "./fs/interface.js";
13
13
  import { type ExecutionLimits } from "./limits.js";
14
14
  import { type NetworkConfig } from "./network/index.js";
15
15
  import type { BashExecResult, Command } from "./types.js";
@@ -53,6 +53,24 @@ export interface BashOptions {
53
53
  * Useful for testing with mock clocks.
54
54
  */
55
55
  sleep?: (ms: number) => Promise<void>;
56
+ /**
57
+ * Custom commands to register alongside built-in commands.
58
+ * These take precedence over built-ins with the same name.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * import { defineCommand } from "just-bash";
63
+ *
64
+ * const hello = defineCommand("hello", async (args) => ({
65
+ * stdout: `Hello, ${args[0] || "world"}!\n`,
66
+ * stderr: "",
67
+ * exitCode: 0,
68
+ * }));
69
+ *
70
+ * const bash = new Bash({ customCommands: [hello] });
71
+ * ```
72
+ */
73
+ customCommands?: CustomCommand[];
56
74
  }
57
75
  export interface ExecOptions {
58
76
  /**
@@ -1,7 +1,7 @@
1
1
  import { type Tool } from "ai";
2
2
  import { type BashOptions } from "../Bash.js";
3
3
  import type { CommandName } from "../commands/registry.js";
4
- import type { IFileSystem, InitialFiles } from "../fs-interface.js";
4
+ import type { IFileSystem, InitialFiles } from "../fs/interface.js";
5
5
  type BashToolInput = {
6
6
  command: string;
7
7
  };