Skip to content

plugin-tool-code-runner — 代码执行工具 ​

包名: @aalis/plugin-tool-code-runner
源码: packages/plugin-tool-code-runner/src/index.ts

概述 ​

执行 Python / JavaScript 代码的工具,带输出大小限制,等多久由执行器的让出时限管。默认在 OS 沙箱内运行 (经 code-sandbox 服务,由 plugin-code-sandbox-os 提供)—— 把代码的写入限制在「工作目录 + 本次临时目录」、默认断网、只放行白名单环境变量(读取不受限, 见下文「沙箱(隔离执行)」)。无可用沙箱后端时 fail-closed(拒绝执行,给出可操作提示), 不会退回无隔离运行。

插件声明 ​

typescript
export default definePlugin({
  name: '@aalis/plugin-tool-code-runner',
  uses: {
    tools: optional(tools),
    storage,
    proc: processService,
    codeSandbox: optional(codeSandbox),
    logger,
    config,
  },
  apply(caps) { /* 见源码 */ },
});

配置 ​

字段类型默认值说明
pythonobject—Python
python.enabledbooleantrue启用 run_python
python.interpreterstring'python3'解释器路径:Python 解释器路径或命令名,如 python3、/usr/bin/python3
javascriptobject—JavaScript (Node.js)
javascript.enabledbooleantrue启用 run_javascript
javascript.interpreterstring'node'解释器路径:Node.js 解释器路径或命令名
maxOutputSizenumber131072最大输出字节:stdout/stderr 各自的最大输出字节数
workingDirectorystring'workspace:/'逻辑工作目录:脚本执行时的 storage URI 工作目录,如 workspace:/ 或 tmp:/run;相对路径会解释为 workspace:/ 下路径。
sandboxobject—代码沙箱
sandbox.modeselect'auto'隔离模式:auto:经 code-sandbox 服务(Linux bubblewrap / macOS sandbox-exec)把代码限制在「工作区 + 本次临时目录」、默认断网、只放行白名单环境变量;无可用沙箱后端时拒绝执行(fail-closed)。none:退回无隔离裸进程,每次告警。说明:v1 读放开(解释器需系统库),防的是写出工作区/联网外泄/篡改系统,不防读取本机其它文件。
sandbox.networkselect'deny'子进程网络:仅 auto 模式生效。deny:脚本内联网(含 fetch)会失败。allow:放开子进程网络(无法按域名白名单过滤)。

解释器、工作目录、启用开关以及沙箱模式/网络选项若显式给出无效值,插件会拒绝激活,不会以默认解释器或更宽的执行方式启动。

沙箱(隔离执行) ​

auto 模式下取 code-sandbox 服务执行:每次运行按本次工作目录与临时目录构造隔离策略 (目录写白名单 + 网络开关),连同环境变量白名单一起交给该服务,由 OS 沙箱后端强制执行。需安装一个 code-sandbox 实现 (默认 plugin-code-sandbox-os:macOS sandbox-exec / Linux bubblewrap); npm create aalis 选了本插件会自动带上它。两种模式下脚本都只拿到白名单里的环境变量(见插件的 safeEnv):none 模式走 process 服务,它的 env 是替换语义。边界:v1 防「写出工作区 / 联网外泄 / 篡改系统」, 不防读取本机其它文件(读放开,因解释器需系统库);要防读取需更强的 WASM/microVM 实现。

注册工具 ​

工具说明
run_python执行 Python 代码
run_javascript执行 JavaScript 代码

结果是 JSON 对象:exitCode、stdout、stderr(各按 maxOutputSize 截断),以下情形另带 error,工具调用即按 api-tools 的 toolFailed 记为失败(工具卡、后台任务的结局与结束通知同此):

情形error
脚本跑完、以非零退出,且调用没被中止'退出码 N'(N 为 exitCode)
脚本没被中止却被信号结束(如内存不够被系统杀掉,exitCode 为 -1)'被信号 <信号名> 结束';Linux 的 bubblewrap 沙箱把这种结束转成退出码 128+N,按上一行报
要求沙箱但没有可用后端(fail-closed)可操作的安装提示
起脚本进程失败失败原因

退出码为 0 的不带 error;调用被中止时(下一节)也不带:标为 aborted 的与注明「调用已中止」的都不带。

转后台、停止与中止结果 ​

两个工具都声明可转后台(background,让出时限用执行器的默认 30 秒):在前台等满让出时限还没结束,plugin-tools 的执行器把这次调用登记成后台任务、先回给模型,脚本照常跑,正常结束时通知起它的会话。参数里没有超时,配置里也没有,等多久由执行器的让出时限管(模型可用 waitSeconds 改,runInBackground 立即转后台)。

停只认执行器给这次调用的中止信号(工具调用上下文的 signal):在前台时随回合中止(WebUI 的停止键、禁言、停机等停下这一轮),转到后台之后随停止、工具撤回登记与执行器停用中止。它交给执行路径:沙箱路径经 code-sandbox 服务的 SandboxRunRequest.signal,无沙箱路径经 process 服务的 SpawnOptions.signal,两条路径都不带超时。中止时脚本按进程组停掉:POSIX 先 SIGTERM、宽限 2000ms 后 SIGKILL。

结果只在脚本确实是被信号结束时标为中止,免得下一轮模型把已经生效的脚本再跑一遍:

情形结果
中止时脚本被信号结束(含宽限到点的 SIGKILL)aborted: true、message: '代码已中止',带已收集的输出
中止前脚本已自行退出,或结束时没有终止信号(Windows 下强制结束可能如此)按实际退出码与输出回报,另加 note: '调用已中止'
起脚本进程前已中止不运行脚本,返回 aborted: true 与同一条 message

边界:中止只打原进程组。无沙箱路径(sandbox.mode: none)与 macOS Seatbelt 路径上,主动脱离进程组的后代(Python 的 os.setsid、Node 以 detached: true 起的子进程等守护化写法)会活下来;Linux bwrap 路径有 pid 命名空间,外层 bwrap 退出后它们随之结束。详见 plugin-code-sandbox-os 的「停止键与超时的边界」。

工作目录解析 ​

workingDirectory 必须是 storage URI(workspace:/project、pluginData:/... 等)或相对 workspace:/ 的路径。不接受宿主机绝对路径。

解析使用 @aalis/api-storage 导出的共享函数 resolveAgainstCwd,与 plugin-tool-system 的 shell/file/http 工具行为一致。

文档对应 dev@c4e046e41,core 0.21.0,构建于 2026-11-02
基于 MIT 许可发布