如果文章对你有帮助,请点个“关注”
一、工具概述
功能:执行 shell 命令(最核心的基础工具)
核心特性:
• 支持后台运行(yieldMs/background) • 支持超时控制(timeout) • 支持 PTY 模式(pty=true 用于 TTY 命令) • 支持多主机(sandbox/gateway/node) • 支持提权执行(elevated) • 支持安全模式(deny/allowlist/full) • 支持审批模式(off/on-miss/always) • 支持环境变量(env) • 支持工作目录(cwd/workdir)
二、Schema 定义
位置:第 14782 行
const execSchema = Type.Object({
command: Type.String({ description: "Shell command to execute" }),
workdir: Type.Optional(Type.String({ description: "Working directory (defaults to cwd)" })),
env: Type.Optional(Type.Record(Type.String(), Type.String())),
yieldMs: Type.Optional(Type.Number({ description: "Milliseconds to wait before backgrounding (default 10000)" })),
background: Type.Optional(Type.Boolean({ description: "Run in background immediately" })),
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (optional, kills process on expiry)" })),
pty: Type.Optional(Type.Boolean({ description: "Run in a pseudo-terminal (PTY) when available (TTY-required CLIs, coding agents)" })),
elevated: Type.Optional(Type.Boolean({ description: "Run on the host with elevated permissions (if allowed)" })),
host: Type.Optional(Type.String({ description: "Exec host (sandbox|gateway|node)." })),
security: Type.Optional(Type.String({ description: "Exec security mode (deny|allowlist|full)." })),
ask: Type.Optional(Type.String({ description: "Exec ask mode (off|on-miss|always)." })),
node: Type.Optional(Type.String({ description: "Node id/name for host=node." }))
});三、完整执行代码
位置:第 16558 行
functioncreateExecTool(defaults) {
// 1. 解析默认配置
const defaultBackgroundMs = clampWithDefault(defaults?.backgroundMs ?? readEnvInt("PI_BASH_YIELD_MS"), 1e4, 10, 12e4);
const allowBackground = defaults?.allowBackground ?? true;
const defaultTimeoutSec = typeof defaults?.timeoutSec === "number" && defaults.timeoutSec > 0 ? defaults.timeoutSec : 1800;
// 2. 解析安全策略(白名单、黑名单等)
const { safeBins, safeBinProfiles, trustedSafeBinDirs } = resolveExecSafeBinRuntimePolicy({...});
// 3. 返回工具定义
return {
name: "exec",
label: "exec",
description: "Execute shell commands with background continuation. Use yieldMs/background to continue later via process tool. Use pty=true for TTY-required commands (terminal UIs, coding agents).",
parameters: execSchema,
execute: async (_toolCallId, args, signal, onUpdate) => {
// === 执行逻辑开始 ===
const params = args;
// 1. 参数验证
if (!params.command) thrownewError("Provide a command to start.");
// 2. 解析后台运行请求
const backgroundRequested = params.background === true;
const yieldRequested = typeof params.yieldMs === "number";
const yieldWindow = allowBackground ?
(backgroundRequested ? 0 : clampWithDefault(params.yieldMs ?? defaultBackgroundMs, defaultBackgroundMs, 10, 12e4)) : null;
// 3. 解析提权请求
const elevatedRequested = elevatedMode !== "off";
if (elevatedRequested) {
if (!elevatedDefaults?.enabled || !elevatedDefaults.allowed) {
thrownewError("elevated is not available right now...");
}
}
// 4. 确定执行主机(sandbox/gateway/node)
let host = requestedHost ?? configuredHost;
if (elevatedRequested) host = "gateway";
// 5. 确定安全模式
let security = minSecurity(configuredSecurity, normalizeExecSecurity$1(params.security) ?? configuredSecurity);
if (elevatedRequested && elevatedMode === "full") security = "full";
// 6. 确定审批模式
let ask = maxAsk(configuredAsk, normalizeExecAsk$1(params.ask) ?? configuredAsk);
const bypassApprovals = elevatedRequested && elevatedMode === "full";
if (bypassApprovals) ask = "off";
// 7. 解析工作目录
const rawWorkdir = params.workdir?.trim() || defaults?.cwd || process.cwd();
let workdir = rawWorkdir;
if (sandbox) {
const resolved = awaitresolveSandboxWorkdir({ workdir: rawWorkdir, sandbox, warnings });
workdir = resolved.hostWorkdir;
}
// 8. 解析环境变量(安全检查)
const env = sandbox && host === "sandbox" ?
buildSandboxEnv({...}) :
hostEnvResult?.env ?? inheritedBaseEnv;
// 9. 处理不同主机的执行逻辑
if (host === "node") {
returnexecuteNodeHostCommand({...});
}
if (host === "gateway" && !bypassApprovals) {
// 10. Gateway 白名单检查(可能需要用户审批)
const gatewayResult = awaitprocessGatewayAllowlist({...});
if (gatewayResult.pendingResult) {
// 需要审批,返回待处理状态
return gatewayResult.pendingResult;
}
execCommandOverride = gatewayResult.execCommandOverride;
}
// 11. 执行命令
const run = awaitrunExecProcess({
command: params.command,
execCommand: execCommandOverride,
workdir,
env,
sandbox,
containerWorkdir,
usePty: params.pty === true && !sandbox,
warnings,
maxOutput: DEFAULT_MAX_OUTPUT,
pendingMaxOutput: DEFAULT_PENDING_MAX_OUTPUT,
notifyOnExit,
notifyOnExitEmptySuccess,
timeoutSec: effectiveTimeout,
onUpdate
});
// 12. 处理后台运行
let yielded = false;
let yieldTimer = null;
constonYieldNow = () => {
if (yieldTimer) clearTimeout(yieldTimer);
if (yielded) return;
yielded = true;
markBackgrounded(run.session);
resolveRunning();
};
if (allowBackground && yieldWindow !== null) {
if (yieldWindow === 0) onYieldNow();
else yieldTimer = setTimeout(onYieldNow, yieldWindow);
}
// 13. 等待执行完成
returnnewPromise((resolve, reject) => {
run.promise.then((outcome) => {
if (yieldTimer) clearTimeout(yieldTimer);
if (yielded || run.session.backgrounded) return;
resolve(buildExecForegroundResult({ outcome, cwd: run.session.cwd, warningText: getWarningText() }));
}).catch((err) => {
if (yieldTimer) clearTimeout(yieldTimer);
if (yielded || run.session.backgrounded) return;
reject(err);
});
});
}
};
}四、执行流程图
exec 工具调用
↓
1. 参数验证(command 必填)
↓
2. 解析后台运行请求(background/yieldMs)
↓
3. 解析提权请求(elevated)
↓
4. 确定主机(sandbox/gateway/node)
↓
5. Gateway 白名单检查
├─ 需要审批 → 返回 pending 状态,等待用户 /approve
└─ 无需审批 → 继续
↓
6. 启动进程(runExecProcess)
├─ 创建子进程
├─ 监听输出
├─ 记录日志
└─ 返回 session
↓
7. 判断是否后台运行
├─ 是 → 立即返回"正在运行",用户可用 process 工具管理
└─ 否 → 等待完成,返回完整输出五、返回结果格式
前台运行完成
{
"content":[{
"type":"text",
"text":"命令输出内容..."
}],
"details":{
"status":"completed",
"exitCode":0,
"cwd":"/path/to/workdir"
}
}后台运行中
{
"content":[{
"type":"text",
"text":"Command still running (session abc123, pid 12345). Use process (list/poll/log/write/kill/clear/remove) for follow-up."
}],
"details":{
"status":"running",
"sessionId":"abc123",
"pid":12345,
"startedAt":1711716000000,
"cwd":"/path/to/workdir",
"tail":"部分输出..."
}
}需要审批
{
"content":[{
"type":"text",
"text":"Approval required (id abc123).\nHost: gateway\nCWD: /workspace\nCommand:\n```sh\nrm -rf /tmp/test\n```\nReply with: /approve abc123 allow-once|allow-always|deny"
}],
"details":{
"status":"pending_approval",
"approvalId":"abc123"
}
}六、参数详解
6.1 必填参数
| command |
6.2 可选参数
| workdir | |||
| env | |||
| yieldMs | |||
| background | |||
| timeout | |||
| pty | |||
| elevated | |||
| host | |||
| security | |||
| ask | |||
| node |
七、使用示例
7.1 简单命令
{
"name": "exec",
"arguments": {
"command": "ls -la"
}
}7.2 后台运行
{
"name": "exec",
"arguments": {
"command": "python long_running_script.py",
"yieldMs": 5000
}
}7.3 超时控制
{
"name": "exec",
"arguments": {
"command": "ping -n 100 www.example.com",
"timeout": 30
}
}7.4 PTY 模式
{
"name": "exec",
"arguments": {
"command": "htop",
"pty": true
}
}7.5 环境变量
{
"name": "exec",
"arguments": {
"command": "echo $MY_VAR",
"env": {
"MY_VAR": "Hello World"
}
}
}八、安全机制
8.1 安全模式
| deny | |
| allowlist | |
| full |
8.2 审批模式
| off | |
| on-miss | |
| always |
8.3 提权执行
{
"name": "exec",
"arguments": {
"command": "apt update",
"elevated": true,
"host": "gateway"
}
}前提条件:
• elevatedDefaults.enabled = true • elevatedDefaults.allowed = true
九、配套工具
process 工具
exec 的配套工具,用于管理后台进程:
| list | |
| poll | |
| log | |
| write | |
| send-keys | |
| paste | |
| submit | |
| kill |
如果文章对你有帮助,请点个“关注”
夜雨聆风