Sandboxed bash interpreter for JavaScript and TypeScript. Native NAPI-RS bindings to the bashkit Rust core for Node.js, Bun, and Deno.
- Sandboxed, in-process execution with a virtual filesystem
- Full bash syntax: variables, pipelines, redirects, loops, functions, and arrays
- 160 built-in commands including
grep,sed,awk,jq,curl, andfind - Sync and async execution APIs
- Direct VFS helpers, constructor mounts, and live host mounts
- Cancellation support via
cancel() - Sticky cancellation recovery via
clearCancel() - Snapshot and restore support on
Bash - AI framework adapters for OpenAI, Anthropic, Vercel AI SDK, and LangChain
npm install @everruns/bashkit # Node.js
bun add @everruns/bashkit # Bun
deno add npm:@everruns/bashkit # Denoimport { Bash } from "@everruns/bashkit";
const bash = new Bash();
const result = bash.executeSync('echo "Hello, World!"');
console.log(result.stdout); // Hello, World!\n
bash.executeSync("X=42");
console.log(bash.executeSync("echo $X").stdout); // 42\nimport { Bash } from "@everruns/bashkit";
const bash = new Bash();
const result = await bash.execute('echo -e "banana\\napple\\ncherry" | sort');
console.log(result.stdout); // apple\nbanana\ncherry\n
await bash.execute('printf "data\\n" > /tmp/file.txt');
console.log((await bash.execute("cat /tmp/file.txt")).stdout); // data\nconst bash = new Bash();
const result = await bash.execute(
'for i in 1 2 3; do echo out-$i; echo err-$i >&2; done',
{
onOutput({ stdout, stderr }) {
if (stdout) process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);
},
},
);onOutput is optional and fires during execution with chunk objects shaped like
{ stdout, stderr }. Chunks are not line-aligned or exact terminal interleaving, but
concatenating all callback chunks matches the final ExecResult.stdout and
ExecResult.stderr. The handler must be synchronous; Promise-returning
handlers are rejected. Do not call back into the same Bash / BashTool
instance from onOutput via execute*, readFile, fs(), or similar
same-instance APIs.
import { Bash } from "@everruns/bashkit";
const bash = new Bash({
username: "agent",
hostname: "sandbox",
maxCommands: 1000,
maxLoopIterations: 10000,
maxMemory: 10 * 1024 * 1024,
timeoutMs: 30_000,
mounts: [{ path: "/workspace", root: "./src", writable: true }],
python: false,
});import { Bash } from "@everruns/bashkit";
const bash = new Bash();
bash.mkdir("/data", true);
bash.writeFile("/data/config.json", '{"debug":true}');
bash.appendFile("/data/config.json", "\n");
console.log(bash.readFile("/data/config.json"));
console.log(bash.exists("/data/config.json"));
console.log(bash.ls("/data"));
console.log(bash.glob("/data/*.json"));BashTool exposes the same direct filesystem helpers.
Call bash.fs() or tool.fs() when you need the underlying filesystem handle directly. For most applications, the convenience methods on Bash and BashTool are simpler and more explicit.
import { Bash } from "@everruns/bashkit";
const bash = new Bash({
files: {
"/config.json": '{"key":"value"}',
"/lazy.txt": () => "computed on first read",
},
});
console.log(bash.readFile("/config.json"));
const asyncBash = await Bash.create({
files: {
"/async.txt": async () => "loaded asynchronously",
},
});import { Bash } from "@everruns/bashkit";
const bash = new Bash({
mounts: [
{ path: "/docs", root: "./docs" },
{ path: "/workspace", root: "./src", writable: true },
],
});
console.log(bash.executeSync("ls /workspace").stdout);import { Bash } from "@everruns/bashkit";
const bash = new Bash();
bash.mount("./src", "/workspace", true);
console.log(bash.executeSync("ls /workspace").stdout);
bash.unmount("/workspace");import { Bash, BashError } from "@everruns/bashkit";
const bash = new Bash();
try {
bash.executeSyncOrThrow("exit 1");
} catch (err) {
if (err instanceof BashError) {
console.log(err.exitCode);
console.log(err.stderr);
}
}import { Bash } from "@everruns/bashkit";
const bash = new Bash();
const running = bash.execute("sleep 60");
bash.cancel();
await running;
bash.clearCancel(); // preserve session/VFS state before reusing the instancecancel() sets a sticky flag that causes future executions to fail with
"execution cancelled". Call clearCancel() after the cancelled execution
has finished to reuse the same instance without losing shell or VFS state.
Use reset() only when you want to discard state entirely.
BashTool exposes the same cancel(), clearCancel(), and reset() methods.
For synchronous execution, executeSync(...) and executeSyncOrThrow(...)
also accept { signal }.
BashTool wraps the interpreter with tool-contract metadata for agent frameworks:
nameversionshortDescriptiondescription()help()systemPrompt()inputSchema()outputSchema()
import { BashTool } from "@everruns/bashkit";
const tool = new BashTool();
console.log(tool.name);
console.log(tool.inputSchema());
const result = tool.executeSync("echo hello");
console.log(result.stdout);Use ScriptedTool to register JavaScript callbacks as bash-callable tools:
import { ScriptedTool } from "@everruns/bashkit";
const tool = new ScriptedTool({ name: "api" });
tool.addTool("get_user", "Fetch user by ID", (params) => {
return JSON.stringify({ id: params.id, name: "Alice" });
});
const result = tool.executeSync("get_user --id 1 | jq -r '.name'");
console.log(result.stdout); // AliceState snapshots are available on both Bash and BashTool instances:
import { Bash, BashTool } from "@everruns/bashkit";
const bash = new Bash({ username: "agent", maxCommands: 100 });
await bash.execute(
"export BUILD_ID=42; mkdir -p /workspace && cd /workspace && echo ready > state.txt",
);
const snapshot = bash.snapshot();
const shellOnly = bash.snapshot({ excludeFilesystem: true });
const promptOnly = bash.snapshot({
excludeFilesystem: true,
excludeFunctions: true,
});
const restored = Bash.fromSnapshot(snapshot);
console.log((await restored.execute("echo $BUILD_ID")).stdout); // 42\n
restored.reset();
restored.restoreSnapshot(snapshot);
restored.restoreSnapshot(shellOnly);
console.log(restored.executeSync("pwd").stdout); // /workspace\n
const tool = new BashTool({ username: "agent", maxCommands: 5 });
tool.executeSync("export TOOL_STATE=ready");
const toolSnapshot = tool.snapshot();
const toolShellOnly = tool.snapshot({ excludeFilesystem: true });
const restoredTool = BashTool.fromSnapshot(toolSnapshot, {
username: "agent",
maxCommands: 5,
});
console.log(restoredTool.executeSync("echo $TOOL_STATE").stdout); // ready\n
restoredTool.restoreSnapshot(toolShellOnly);import { bashTool } from "@everruns/bashkit/openai";
const bash = bashTool();import { bashTool } from "@everruns/bashkit/anthropic";
const bash = bashTool();import { bashTool } from "@everruns/bashkit/ai";
const bash = bashTool();import {
createBashTool,
createScriptedTool,
} from "@everruns/bashkit/langchain";new Bash(options?)Bash.create(options?)executeSync(commands, options?)execute(commands, options?)executeSyncOrThrow(commands, options?)executeOrThrow(commands, options?)cancel()clearCancel()reset()snapshot()restoreSnapshot(data)Bash.fromSnapshot(data)- Direct VFS helpers:
readFile,writeFile,appendFile,mkdir,remove,exists,stat,readDir,ls,glob,mount,unmount,fs
- All execution, cancellation (
cancel(),clearCancel()), reset, snapshot, restore, and direct VFS helpers fromBash - Tool metadata:
name,version,shortDescription snapshot()restoreSnapshot(data)BashTool.fromSnapshot(data, options?)description()help()systemPrompt()inputSchema()outputSchema()
new ScriptedTool(options)addTool(name, description, callback, schema?)executeSync(script)execute(script)executeSyncOrThrow(script)executeOrThrow(script)env(key, value)toolCount()
username?: stringhostname?: stringmaxCommands?: numbermaxLoopIterations?: numbermaxMemory?: numbertimeoutMs?: numberfiles?: Record<string, string | (() => string) | (() => Promise<string>)>mounts?: Array<{ path: string; root: string; writable?: boolean }>python?: booleanexternalFunctions?: string[]
signal?: AbortSignalonOutput?: (chunk: { stdout: string; stderr: string }) => void
ExecResult.stdoutExecResult.stderrExecResult.exitCodeExecResult.errorExecResult.successExecResult.stdoutTruncatedExecResult.stderrTruncatedBashError.exitCodeBashError.stderr
| OS | Architecture |
|---|---|
| macOS | x86_64, aarch64 |
| Linux | x86_64, aarch64 |
| Windows | x86_64 |
| WASM | wasm32-wasip1-threads |
The JavaScript package wraps the Rust bashkit interpreter through NAPI-RS bindings. Commands execute in-process against a virtual filesystem, with the Rust core enforcing parsing, execution, and resource limits while the JS wrapper exposes a TypeScript-friendly API and framework adapters.
Bashkit is part of the Everruns ecosystem. See the bashkit monorepo for the Rust core, the Python package (bashkit), and related tooling.
MIT