@everruns/bashkit (英语)
JavaScript 和 TypeScript 的 sandboxed bash 解释器 。 土著的《国家行动计划》 -- -- RS对《公约》的约束 bashkit 为Node.js、Bun和Deno提供粗核.
主页 : bash.sh
特征
- Sandboxed, 以虚拟文件系统进行进程内执行
- 完整的shash语法:变量、管道、方向、循环、函数和数组
- 164个内置命令,包括
grep, (中文(简体) ),sed, (中文(简体) ),awk, (中文(简体) ),jq, (中文(简体) ),curl,以及find - 同步并同步执行 API
- 直接 VFS 助手, 独立
FileSystem手柄和活的挂载 - 通过
cancel() - 通过粘性取消回收
clearCancel() - 抓取并恢复支持
Bash - 输出的 HTTP (
curl编号 :wget在 URL 许可列表后面,并带有透明的证书注入 - OpenAI、Anthropic、Vercel AI SDK和LangChain的AI框架适配器
安装
npm install @everruns/bashkit # Node.js
bun add @everruns/bashkit # Bun
deno add npm:@everruns/bashkit # Deno
使用补丁,支持的节点.js发布. 2026年10月的安全最低标准
为: 22.23.4 联合国, (中文(简体) ), 24.22.1 国家,以及 第26.12.1条 在各自的释放线内;
继续接受后续的上游安全更新 包裹 engines >=18
是ABI兼容层,而不是EOL节点18/20的安全支持. 这个
节点20 CI道只检查遗留相容性. 巴什基特不捆接节点.
快速启动
同步执行
import { 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\n
同步执行
import { 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\n
现场输出
const 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 是可选的,在执行时会起火,形状像块状物体
{ stdout, stderr }春克不是线相通的,也不是精确的终端连接,但是
调用所有调用块与 final 匹配 ExecResult.stdout 财务报告和审定财务报表
ExecResult.stderr。处理器必须同步;承诺返回
处理者被拒绝。 别再回话了 Bash 编号 : BashTool
实例 onOutput 通过 execute*, (中文(简体) ), readFile, (中文(简体) ), fs(),或类似
同样的 APIs.
配置
巴什选项
import { Bash } from "@everruns/bashkit";
const bash = new Bash({
username: "agent",
hostname: "sandbox",
cwd: "/home/agent",
env: { PROJECT: "bashkit" },
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 曝光相同的直接文件系统助手.
文件系统访问器
import { Bash, FileSystem } from "@everruns/bashkit";
const source = new FileSystem();
source.writeFile("/org/repo/README.md", "hello\n");
const bash = new Bash();
bash.mount("/workspace", source);
console.log(bash.executeSync("cat /workspace/org/repo/README.md").stdout);
打电话 bash.fs() 或者说 tool.fs() 当您需要当前解释文件系统的活控。 使用 new FileSystem() 或者说 FileSystem.real(...) 当您需要独立的可挂载文件系统时.
原生的 Addon Interop
import { Bash, FileSystem } from "@everruns/bashkit";
import { createFilesystem } from "filesystem-addon";
const imported = FileSystem.fromExternal(createFilesystem());
const bash = new Bash();
bash.mount("/workspace", imported);
console.log(bash.executeSync("ls /workspace").stdout);
toExternal() 编号 : fromExternal() 交换不透明的本地 External 符号
版本稳定 ABI 控件,所以 JavaScript 无法检查或变异本地
操作字节。 本地添加物应依赖 bashkit 与 interop
特性和使用 bashkit::interop::fs。 。 。 .
启动前文件
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, FileSystem } from "@everruns/bashkit";
const bash = new Bash();
const workspace = FileSystem.real("./src", {
writable: true,
allowedMountPaths: ["./src"],
});
bash.mount("/workspace", workspace);
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 instance
cancel() 贴出一面粘贴的旗帜,使未来的处决失败
"execution cancelled"调用 clearCancel() 取消执行后
已完成重用同一实例而不丢失 shell 或 VFS 状态 .
使用 reset() 只有在您想要完全放弃状态时.
BashTool 揭发同样的 cancel(), (中文(简体) ), clearCancel(),以及 reset() 方法。 。 .
对于同步执行, executeSync(...) 财务报告和审定财务报表 executeSyncOrThrow(...)
还接受 { signal }。 。 。 .
自动同步 execute(...) 电话按例顺序排列,并保持界限
积压。 如果有太多的死刑已经等待执行 Bash, (中文(简体) ), BashTool, (中文(简体) ),
或者说 ScriptedTool中,下一个同步呼叫拒绝,而不是保留另一个
已队列命令字符串 。 大于 maxInputBytes 之前被拒绝
输入同步队列.
巴什图
BashTool 将解释器与代理框架的工具合同元数据相连接:
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);
脚本工具
使用 ScriptedTool 将 JavaScript 调用注册为 shash- callable 工具 :
import { ScriptedTool } from "@everruns/bashkit/scripted";
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); // Alice
自定义构造
将JS召回登记为共享 Bash 编号 : BashTool
实例的 VFS, 在一个呼叫中创建的文件持续到 execute() 打电话.
import { Bash } from "@everruns/bashkit";
// Constructor-time registration
const bash = new Bash({
customBuiltins: {
"get-order": (ctx) =>
JSON.stringify({ id: ctx.argv[0], status: "shipped" }) + "\n",
},
});
// Or post-construction (safe to call at any time — no interpreter rebuild)
bash.addBuiltin("greet", (ctx) => `hello ${ctx.argv[0] ?? "world"}\n`);
// Tool output flows through the shared VFS
await bash.execute("mkdir -p /scratch");
await bash.execute("get-order 42 > /scratch/order.json");
console.log((await bash.execute("cat /scratch/order.json")).stdout);
// {"id":"42","status":"shipped"}
bash.removeBuiltin("greet"); // when you no longer need it
回调可以同步( N)string 返回)或合成(Promise<string>) (中文(简体) ). 两者
rust方面一致等待。 例外/拒绝改为
出站代码为一号站台.
const bash = new Bash({
customBuiltins: {
fetch: async (ctx) => {
const data = await fetchSomething(ctx.argv[0]);
return JSON.stringify(data) + "\n";
},
fail: () => {
throw new Error("nope"); // → stderr, exit 1
},
},
});
回调收到一个 BuiltinContext数字 :
name: string,引用的命令名称argv: string[],参数(不包括命令名称)stdin: string | null,管道输入,或null如果没有管子env: Record<string, string>,环境变量(仅导出名称)cwd: string,当前工作目录fs: FileSystem中,显示实例虚拟文件系统的活控点
ctx.fs 执行脚本看到的是相同的 VFS: 读取较早的观察
脚本写作,并被后续的命令所可见. 继承
挂载并只读配置 .
const bash = new Bash({
customBuiltins: {
head10: (ctx) =>
ctx.fs.readFile(ctx.argv[0]).split("\n").slice(0, 10).join("\n") + "\n",
},
});
await bash.execute("head10 /var/log/app.log");
覆盖优先级: shell 函数 > POSIX 特殊内建 > 自定义内建
烤入内置 >
PATH。自定义内建可以覆盖烤入命令 (如包装)cat),但脚本中定义的 shell 函数仍然取胜.
自定义内建构幸存 reset()。它们是主机侧配置,并且
没有 保存者 snapshot() 编号 : restoreSnapshot(),通过
customBuiltins 重复或调用 addBuiltin 恢复后.
使用 execute() (同声. 如果脚本引用了下面的自定义
executeSync() 内建中以退出代码 1 和 stderr 快速失败
"<name>: custom builtins require execute() (async). ..." 改为
陷入僵局,JS事件循环被封锁 而同步呼叫是
在飞行中,所以基础 Promise<string> 召回无法运行.
脚本分析
analyze() 报告一个脚本静态的称呼,命令,参数,
重定向目标,函数定义,而不运行. 用它来决定
模型生成的命令是否需要用户批准 .
const analysis = bash.analyze("cat notes.txt | grep -i todo > out.txt");
analysis.commandNames; // ["cat", "grep"]
analysis.commands[1].args; // ["-i", "todo"]
analysis.redirects[0].path; // "out.txt"
analysis.redirects[0].isWrite; // true
analysis.isOpaque; // false
未完整文字报告的单词 null,从不带部分字符串:
const a = bash.analyze('rm "$target" /tmp/fixed');
a.commands[0].args; // [null, "/tmp/fixed"]
只有咨询. 静态分析无法通过动态调度看到, eval, (中文(简体) ),
函数或别名。 这些 isOpaque,并需要查询许可列表检查
这, c=rm; $c -rf /data 包含没有不允许的命令名称 :
const READ_ONLY = new Set(["ls", "cat", "head", "grep", "wc", "echo"]);
function safeToRun(script: string): boolean {
let analysis;
try {
analysis = bash.analyze(script); // throws if the script does not parse
} catch {
return false;
}
if (analysis.isOpaque) return false;
return analysis.commands.every(
(c) => c.isAssignmentOnly || (c.name != null && READ_ONLY.has(c.name)),
);
}
内部的图标 $(…),循环,分支,和职能机构也被报告,
每个标记 context (单位:千美元)"direct", (中文(简体) ), "substitution", (中文(简体) ), "function_body"),这样
echo $(rm -rf /) 从来不像赤裸裸的 echo执行与
内建注册、网络许可列表和挂载策略.
抓图/ 恢复
国家简介可在两个网站查阅 Bash 财务报告和审定财务报表 BashTool 实例.
安全:未按键 Bash 快照使用公共腐败侦查摘要和
可伪造。 使用 hmacKey 当快照字节跨越信任边界时
(网络,用户上传,共享存储). BashTool 需要快照 hmacKey
因为它们包括工具会话状态、 VFS 内容和可能是
在多租户代理服务中恢复.
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 secretKey = Buffer.from(process.env.BASHKIT_SNAPSHOT_KEY!, "hex");
const keyedSnapshot = bash.snapshotKeyed(secretKey);
const keyedRestored = Bash.fromSnapshotKeyed(keyedSnapshot, secretKey);
const restored = Bash.fromSnapshot(snapshot);
console.log((await restored.execute("echo $BUILD_ID")).stdout); // 42\n
restored.reset();
restored.restoreSnapshot(snapshot);
restored.restoreSnapshot(shellOnly);
restored.restoreSnapshotKeyed(keyedSnapshot, secretKey);
console.log(restored.executeSync("pwd").stdout); // /workspace\n
const tool = new BashTool({ username: "agent", maxCommands: 5 });
tool.executeSync("export TOOL_STATE=ready");
const hmacKey = new TextEncoder().encode(process.env.SNAPSHOT_SECRET!);
const toolSnapshot = tool.snapshot({ hmacKey });
const toolShellOnly = tool.snapshot({ excludeFilesystem: true, hmacKey });
const restoredTool = BashTool.fromSnapshot(
toolSnapshot,
{
username: "agent",
maxCommands: 5,
},
{ hmacKey },
);
console.log(restoredTool.executeSync("echo $TOOL_STATE").stdout); // ready\n
restoredTool.restoreSnapshot(toolShellOnly, { hmacKey });
网络
输出的 HTTP (curl 编号 : wget)默认已禁用。 启用它与
network 选项,它需要明确的 URL 允许列表或
allowAll: true数字 :
const bash = new Bash({
network: {
allow: ["https://api.example.com/**"],
blockPrivateIps: true, // default
},
});
await bash.execute("curl https://api.example.com/users"); // allowed
await bash.execute("curl https://evil.example.org"); // denied: not in allowlist
证书注射
在不暴露于脚本的情况下将证书附加到匹配请求中 :
const bash = new Bash({
network: {
allow: ["https://api.example.com/**"],
// Direct injection — the script never sees the secret.
credentials: [
{
pattern: "https://api.example.com/**",
kind: "bearer",
token: process.env.API_TOKEN!,
},
],
// Placeholder mode — the script sees an opaque `bk_placeholder_...`
// value in $MY_TOKEN; it is swapped for the real secret on the wire.
credentialPlaceholders: [
{
env: "MY_TOKEN",
pattern: "https://api.example.com/**",
kind: "header",
name: "X-Api-Key",
value: process.env.API_KEY!,
},
],
},
});
证书类型 : "bearer" (所需经费) token, (中文(简体) ), "header" (所需经费)
name + 键 value, (中文(简体) ), "headers" (所需经费) headers: Array<{ name, value }>) (中文(简体) ).
框架整合
开放AI
import { bashTool } from "@everruns/bashkit/openai";
const bash = bashTool();
安东尼
import { bashTool } from "@everruns/bashkit/anthropic";
const bash = bashTool();
维塞尔 AI SDK
import { bashTool } from "@everruns/bashkit/ai";
const bash = bashTool();
兰彩
import {
createBashTool,
createScriptedTool,
} from "@everruns/bashkit/langchain";
API 参考
巴许
new Bash(options?)Bash.create(options?)executeSync(commands, options?)execute(commands, options?)executeSyncOrThrow(commands, options?)executeOrThrow(commands, options?)cancel()clearCancel()reset()addBuiltin(name, callback)编号 :removeBuiltin(name),注册/未注册 持久性JS内建snapshot(options?)编号 :snapshotKeyed(key, options?)restoreSnapshot(data, options?)编号 :restoreSnapshotKeyed(data, key)Bash.fromSnapshot(data, options?)编号 :Bash.fromSnapshotKeyed(data, key)- 直接 VFS 助手 :
readFile, (中文(简体) ),writeFile, (中文(简体) ),appendFile, (中文(简体) ),mkdir, (中文(简体) ),remove, (中文(简体) ),exists, (中文(简体) ),stat, (中文(简体) ),readDir, (中文(简体) ),ls, (中文(简体) ),glob, (中文(简体) ),mount, (中文(简体) ),unmount, (中文(简体) ),fs shellState(),轻量级检查快照(可变,env,cwd,阵列,别名,陷阱)analyze(script),静态,预执行的回溯(参见: 脚本分析(中文(简体) )
巴什图
- 所有执行,取消(
cancel(), (中文(简体) ),clearCancel()),重置,自定义内建,快照,还原,以及直接从 VFS 帮助Bash - 工具元数据 :
name, (中文(简体) ),version, (中文(简体) ),shortDescription snapshot({ hmacKey, ...options })restoreSnapshot(data, { hmacKey })BashTool.fromSnapshot(data, options?, { hmacKey })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?: string -
hostname?: string -
cwd?: string,初始工作目录(避免主目录)cd(中文(简体) ) -
env?: Record<string, string>,初始环境变量(避免export序曲) -
maxCommands?: number -
maxLoopIterations?: number -
maxMemory?: number -
maxInputBytes?: number -
timeoutMs?: number -
files?: Record<string, string | (() => string) | (() => Promise<string>)> -
mounts?: Array<{ path: string; root: string; writable?: boolean }> -
python?: boolean -
externalFunctions?: string[], Python 主机函数名 -
externalHandler?: ExternalHandler,同步/同步 Python 调度器(name, args, kwargs, { signal }) -
typescript?: boolean,选择嵌入式脚本(ts编号 :node编号 :deno编号 :bun(中文(简体) ) -
typescriptExternalFunctions?: string[] -
typescriptExternalHandler?: TypeScriptExternalHandler,调度器(name, args, { signal }) -
customBuiltins?: Record<string, (ctx: BuiltinContext) => string | Promise<string>>,JS的回调登记为bash buins(见 自定义构造(中文(简体) ) -
network?: NetworkOptions外出 HTTP 配置(见 网络(中文(简体) )
主机呼叫需要同步 execute()见 主机函数指南 jSON 值、调用语境、取消和重返规则.
脚本分析
归来 analyze(script)。 。 。 .
commands: AnalyzedCommand[],每个简单的命令,按源顺序redirects: AnalyzedRedirect[], (中文(简体) ),{ path: string | null, mode: "read" | "write" | "append", isWrite: boolean }functions: string[],函数名称 脚本定义commandNames: string[],显著的静态已知名称,先见顺序hasDynamicCommands: boolean编号 :hasInterpreterReentry: boolean编号 :hasCommandSubstitution: boolean编号 :truncated: booleanisOpaque: boolean,脚本隐藏工作;许可列表检查必须将此视为"任务"
AnalyzedCommand数字 : { name: string | null, args: Array<string | null>, context: "direct" | "substitution" | "function_body", assignments: string[], isAssignmentOnly: boolean }
内建连接
name: string,引用的命令名称argv: string[],参数(不包括命令名称)stdin: string | null管道输入,null如果没有管子env: Record<string, string>,导出环境变量cwd: string,当前工作目录fs: FileSystem中,显示实例虚拟文件系统的活控点
执行选项
signal?: AbortSignalonOutput?: (chunk: { stdout: string; stderr: string }) => void
执行结果和巴什 Error
ExecResult.stdoutExecResult.stderrExecResult.exitCodeExecResult.errorExecResult.successExecResult.stdoutTruncatedExecResult.stderrTruncatedBashError.exitCodeBashError.stderr
平台支持
| 监督办 | 建筑 |
|---|---|
| 马科斯 | x86_64, (中文(简体) ), aarch64 |
| 链接 | x86_64, (中文(简体) ), aarch64 |
| 窗口 | x86_64 |
| 刚才 | wasm32-wasip1-threads |
如何运作
JavaScript 软件包包装了 Rust bashkit 通过NAPI-RS绑定的翻译. 命令针对虚拟文件系统执行进程内执行,由 Rust 核心执行解析,执行,和资源限制,而JS 包则暴露出一个TypeScript友好的API和框架适配器.
部分 Everruns 语录
巴什基特属于 永远 生态系统。 见 bashkit 单曲 用于 Rust 核心, Python 软件包 (bashkit)和相关的工具.
许可证
麻省理工学院