oh-my-pi bash 工具深度解析:输入参数、审批策略、拦截路由与 PTY/后台作业执行路径 📅 发布时间:2026/9/10 0:54:37 👁 浏览次数: oh-my-pi bash 工具深度解析输入参数、审批策略、拦截路由与 PTY/后台作业执行路径【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi在 oh-my-pi一个把 IDE 能力接入的编码 Agent中bash工具是模型执行 shell 命令的统一入口它负责命令校验、审批策略判定、专用工具拦截路由、内部 URL 展开、PTY/前台/后台/客户端终端四种执行路径的分发以及输出截断与 artifact 溢出。读完本篇你将掌握bash工具全部输入参数的语义与默认值、bash.patterns与bashInterceptor.patterns两套独立策略的配置方式与交互规则以及从BashTool.execute()到原生 Shell/PtySession 的完整执行管线从而能够精确配置审批与路由规则并排查执行异常。一、bash 工具的定位与源码布局oh-my-pi 中存在两个 bash 执行面工具调用面toolName: bash模型调用 bash 工具时使用入口是 BashTool.execute()参数包括command、可选的env、timeout、cwd、pty以及当async.enabled为 true 时的async用户 bang 命令面交互式输入或 RPC 中的!cmd会话级辅助路径入口是AgentSession.executeBash()。两者最终都会走到 bash-executor.ts 中的executeBash()完成非 PTY 执行但只有工具调用面会执行命令规范化、拦截interception、受管后台作业处理和工具渲染逻辑。在配置中设置bash.enabled: false可以把模型可见的bash工具从工具注册表移除但不会影响用户 bang 命令或 RPCbash请求。核心源文件布局如下均来自 docs/tools/bash.md 的 Source 清单文件职责packages/coding-agent/src/tools/bash.ts工具入口输入处理、拦截检查、路径选择、结果/错误映射、渲染器packages/coding-agent/src/prompts/tools/bash.md面向模型的工具提示词packages/coding-agent/src/tools/bash-interactive.tsPTY/TUI 执行路径packages/coding-agent/src/tools/bash-interceptor.ts拦截该用专用工具而非 shell的命令packages/coding-agent/src/tools/bash-skill-urls.ts内部 URLskill://、local://等展开为本地路径packages/coding-agent/src/tools/bash-pty-selection.tscanUseInteractiveBashPty()判定是否可走本地 PTY 叠加层packages/coding-agent/src/tools/gh-cache-invalidation.ts对变更类gh issue/gh pr子命令使github-cache行失效packages/coding-agent/src/exec/bash-executor.ts非 PTY shell 执行器packages/coding-agent/src/session/streaming-output.ts输出 tail 缓冲、截断、artifact 溢出packages/coding-agent/src/tools/tool-timeouts.ts超时常量与钳制逻辑packages/coding-agent/src/config/settings-schema.ts默认拦截器规则更底层的 shell 会话复用、快照、原生超时行为见配套文档 docs/bash-tool-runtime.md。二、输入参数详解工具输入由 bash.ts 中的 schema 定义字段如下字段类型必填说明commandstring是要执行的 shell 命令文本。仅当cwd未提供时前导的cd path ...会被改写为cwd字段并从命令中剥离envRecordstring, string否额外环境变量。键必须匹配^[A-Za-z_][A-Za-z0-9_]*$否则工具抛错。值会经过内部 URL 展开并作为环境变量值而非 shell 文本传入timeoutnumber否超时秒数。默认3000表示禁用截止时间。正值先受tools.maxTimeout若为正限制再钳制到 Bash 区间1..3600cwdstring否工作目录经resolveToCwd相对session.cwd解析必须存在且为目录ptyboolean否请求 PTY 模式默认false。仅当pty: true、PI_NO_PTY ! 1且工具上下文存在 UI 时才真正使用 PTYasyncboolean否请求后台执行。仅当会话开启了async.enabled时该字段存在。立即返回 job id 而不是等待它不改变有效截止时间包括timeout: 0禁用的截止时间几个在源码中可直接验证的细节env 键校验bash.ts 中BASH_ENV_NAME_PATTERN /^[A-Za-z_][A-Za-z0-9_]*$/非法键会抛出ToolError(Invalid bash env name: key)。cd前缀改写execute()在 bash.ts#L1027-L1033 调用extractLeadingCdTarget()只捕获单个路径 token涉及重定向、参数或 shell 展开的写法如cd /tmp 2/dev/null ...不会被吸收进结构化cwd仍留给 shell 处理。改写仅限首行的顶层。timeout 常量tool-timeouts.ts 中TOOL_TIMEOUTS.bash { default: 300, min: 1, max: 3600 }。clampTimeout()的语义是tools.maxTimeout作为正的全局上限先钳制解析值包括省略timeout走默认值的路径再套用每工具的min/maxmaxTimeout 0表示不设全局上限。三、输出结构与结果元数据工具返回单个text内容块加上可选的details四种形态前台成功content[0].text命令输出命令无任何输出时为(no output)。details.timeoutSeconds经全局/每工具钳制后的有效正超时timeout: 0时改为details.timeoutDisabled: true。details.requestedTimeoutSeconds请求的正超时与有效超时时限时出现。details.wallTimeMs本地/客户端终端完成的运行所耗挂钟毫秒数。details.terminalId执行被路由到客户端终端桥接时出现。details.exitCode命令以非零码完成时出现。details.timedOut: true本地/PTY 超时结果。details.meta.truncation输出在内存中被截断时出现若完整输出已溢出到 artifact还包含artifactId。非零退出码和本地/PTY 超时返回标记isError的工具结果确定性非零输出以Command exited with code n结尾。后台启动async: true或自动后台化content[0].text可选的预览尾部与提示随后是Backgrounded as job id; result will be delivered automatically.details.async{ state: running, jobId, type: bash }。后台进度 / 完成通过onUpdate/ 异步作业管理器投递而不是初始返回值。运行中更新只包含尾部文本且details.async.state: running仅在作业被视为已后台化后出现。完成/失败更新携带最终文本与details.async.state: completed | failed非零退出或超时被记录为失败的后台作业。失败取消、缺失退出状态、校验失败、被拦截的命令、客户端终端桥接超时均抛出ToolError/ToolAbortError。注意stdout 与 stderr 在模型看到之前已被合并确定性非零退出码会追加到返回的错误结果文本末尾形式为Command exited with code n。四、命令策略bash.patterns与bashInterceptor.patterns有两套相互独立的设置可以阻止 Bash 子进程启动。它们目的不同、在工具调用生命周期中作用点也不同设置目的规则语法命中时的结果bash.patterns命令级执行策略带*通配符的字面文本放行、请求人工审批或拒绝该调用bashInterceptor.patterns优先使用专用工具而非 BashJavaScript 正则、可选 flags、工具名与消息返回 Bash 工具错误指示模型改调指定专用工具4.1bash.patterns审批策略bash.patterns用于无论是否存在其他工具都能完成该命令都必须被允许、人工确认或拒绝的场景。规则有序第一条匹配的规则生效。每条规则由matchglob 与approvalallow/prompt/deny组成bash: patterns: - match: git * approval: allow - match: curl * approval: prompt - match: rm -rf * approval: deny匹配语义在 bash.ts 中实现要点deny在BashTool.execute()运行之前即中止调用包括yolo模式prompt显示审批请求只有被接受的请求才会进入BashTool.execute()allow可以为简单命令降低审批层级但不能批准复合命令例如match: git *不会批准git status rm -rf build。源码层面allow规则要求 glob 匹配整个命令且命令含 shell 控制字符;、、|、$、反引号等含引号内的可重解释形态时直接不适用deny与prompt同时检查完整命令和每个 shell 命令段复用共享 shell tokenizer识别;、、||、|、、子 shell、换行等全部边界因此match: rm -rf *能捕获cd /tmp rm -rf build。该设置服务于安全与用户控制对没有合适替代工具的命令破坏性删除、网络访问、部署脚本、项目私有脚本尤其有用。另外bash.ts#L177-L222 内置了CRITICAL_BASH_PATTERNS一组安全关键正则刻意保持收紧覆盖递归销毁绝对路径各种 flag 顺序的rm -rf /、--no-preserve-root、任意sudo rm、chmod -R/chown -R指向/、fork 炸弹、写块设备/mkfs/dd/shred/cryptsetup、覆写/etc/passwd、/etc/shadow、/etc/sudoers、curl|wget管道进 shell、bash (curl …)/eval $(curl …)等远端拉取即执行形态、kill -9 1、shutdown/reboot/halt/init 0、nc -e/nc -c网络 shell。这些检查先于用户模式策略执行且对原始输入与规范化输入、整行与各命令段同时生效——允许前缀不能掩盖后续的关键段。4.2bashInterceptor.patterns专用工具路由bashInterceptor是可选启用opt-in的路由层bashInterceptor.enabled默认false。它针对技术上合法、但用现成专用工具表达更好的命令。每条规则是一个正则附带替代工具名与展示给模型的说明消息bashInterceptor: enabled: true patterns: - pattern: ^\s*(cat|head|tail)\s tool: read message: Use the read tool instead; it handles binary files and provides better context. - pattern: ^\s*(grep|rg)\s tool: grep message: Use the grep tool instead; it respects .gitignore and returns structured results.关键行为实现在 bash-interceptor.ts 的checkBashInterception()只有当规则的tool在当前会话可用时规则才生效检查ctx.toolNames。若read被禁用指向read的cat规则不会阻止 Bash 调用。这使拦截器是尽力而为的能力偏好而不是执行安全边界无效的自定义正则在compileRules()中被静默跳过命中时抛出ToolError消息为Blocked: rule.message并附上原始命令。片段匹配算法为兼容既有自定义正则拦截器总是先检查完整原始命令然后检查由未加引号、未转义的、||、;、|、或换行分隔的原始扁平命令段排除通过未加引号|或|消费上一级 stdout 的段——见interceptionCandidates()最后检查剥离前导NAMEvalue赋值后的片段。因此锚定规则^\s*git\scommit\b能同时命中git add file git commit -m message GIT_AUTHOR_NAMEDev git commit -m message而printf x\n | grep x中的grep x不会被视为拦截候选它读取的是管道 stdin路径类专用工具无法提供只有独立命令或管道首段才会匹配。管道后空行与仅注释的续行保持该上下文引号内、转义与注释文本不算命令。heredoc、参数展开、命令替换、反引号、分组与错误引号只保留完整命令检查——拦截器刻意不做完整 shell 解析。内置默认规则DEFAULT_BASH_INTERCEPTOR_RULES见 settings-schema.ts#L409正则目标路由到cat/head/tail/less/morereadgrep/rg/ripgrep/ag/ackgrep带-name/-iname/-type/--type/-glob的find/fd/locateglobsed -i、perl -i、awk -i inplaceedit带文件重定向的echo/printf/cat /dev/null等设备目标除外fd 复制2不匹配writenohup、行尾裸hubop:startbun/npm/pnpm/yarn dev|start、vite、next dev、nuxt dev、nodemon、lldb、gdb、tail -f、未 detach 的docker compose uphub服务/调试器bun/npm/pnpm/yarn script、cargo watch、watchexec、pytest、vitest、jest、tsc且带--watch/-whubwatch 模式4.3 两套策略的交互与选择指南审批策略在执行前解析命中的bash.patternsdeny永远不会到达拦截器prompt只有在用户接受审批请求后才到达拦截器若已接受的调用又命中拦截器规则Bash 调用依然不会运行模型收到路由错误并应改调专用工具避免对同一操作在两处都配置除非你有意要这种两步行为。例如cat *的prompt规则加上启用的cat→read拦截器会先请求用户批准 Bash随后拒绝 Bash 并要求模型使用read。选择原则问题是这条命令能否执行 → 用bash.patterns问题是该操作应由哪个工具完成 → 用bashInterceptor.patterns。此外要理解一个边界来自 docs/bash-tool-runtime.mdbash.patterns只约束bash工具本身无法约束eval等可通过子进程起 shell 的工具——同一条命令走eval时deny规则不生效。要跨两个面加固破坏性命令需要为eval另行配置tools.approval.evalprompt或deny。审批也不意味着隔离批准后进程仍拥有 shell 的完整文件系统、网络与子进程访问。五、执行管线从 execute() 到子进程BashTool.execute()的完整流程编号继承自 docs/tools/bash.md 的 16 步管线关键步骤均与 bash.ts#L1004-L1144 源码对应读取command校验envtimeout默认300若cwd缺失把前导cd path ...改写为结构化cwd字段并剥离该前缀若请求了async: true但async.enabled为关抛ToolError任何执行都不会发生若bashInterceptor.enabled开启对原始命令与cd 剥离后命令两种形态各跑一遍checkBashInterception()每形态内完整输入 → 扁平段 → 去前导赋值片段。命中的启用规则在 URL 展开或执行之前抛错expandInternalUrls()改写command、每个env值以及形如协议的cwd中的内部 URL。命令替换会做 shell 转义env与cwd替换使用原始文件系统/字符串值noEscape: true因为它们不会被插入 shell 文本resolveToCwd()相对session.cwd解析cwdfs.stat()验证目标存在且是目录timeout: 0禁用截止时间否则clampTimeout(bash, requested, tools.maxTimeout)先应用正全局上限若配置再套TOOL_TIMEOUTS.bashmin: 1max: 3600。发生钳制时#buildCompletedResult()/#buildBackgroundStartResult()追加一条提示行执行路径分叉async: true→#startManagedBashJob()注册会话异步作业并立即返回非 PTY 且bash.autoBackground.enabled开启、异步作业管理器未达运行上限、且无客户端终端桥接可用两者同时满足时桥接优先→ 启动受管作业最多等待min(thresholdMs, timeoutMs - 1000)要么返回已完成结果要么把运行转为后台作业非 PTY 客户端终端桥接会话声明了 terminal 能力且pty为 false → 创建远端终端、流式/轮询当前输出、完成后释放终端其余走前台执行前台非 PTY 且无客户端终端时调用 bash-executor.ts 的executeBash()该路径自行完成 direnv/devenv 预检前台 PTY 与客户端终端路径在分发前于BashTool内执行同样的 direnv 预检。bash.direnv: auto默认下被允许的.envrc可把环境变量变更合并进命令off禁用。bash.direnvLoadTimeoutMs默认30_000正的命令超时也会约束预检时长本地非 PTY 与 PTY 路径在session.allocateOutputArtifact可用时先分配输出 artifactartifact 路径/ID 传入 sink大输出可溢出到磁盘executeBash()加载 shell 配置、可选 shell 快照与 shell minimizer 设置然后通过持久原生Shell会话或一次性executeShell()运行详见 docs/bash-tool-runtime.mdrunInteractiveBashPty()创建PtySession叠加 xterm 支撑的控制台 UI把用户按键输入转发进 PTY经OutputSink捕获输出并在关闭/销毁时杀掉 PTY客户端终端桥接模式调用session.getClientBridge().createTerminal(...)发出terminalId更新轮询输出直至退出/超时/中止把信号退出映射为137并在finally中释放句柄完成时#buildCompletedResult()按需格式化(no output)附加来自输出摘要的截断元数据、耗时/超时/退出提示并在返回前复查未结束状态本地/PTY 超时结果变为带details.timedOut的isError结果客户端终端超时与取消/缺失退出状态路径在可获取捕获输出的情况下抛错。面向模型的提示词prompts/tools/bash.md 定义了模型看到的 bash 工具说明其中约束直接影响模型行为仅在单个二进制或计算事实的短管道wc -l、sort | uniq -c、diff时使用 bash内联脚本、heredoc、$(…)、复杂控制流应交给eval或专门工具/入库脚本用cwd而不是cd多行/重引号值用env: { NAME: … }pty: true仅用于终端交互sudo、ssh顺序依赖命令用一次调用内的独立调用可并发永不使用 shellgrep/rg/ls/find输出会被捕获、截断并以artifact://id链接无需head/tail服务、watcher、调试器、REPL 必须用hubop:start。该提示词还会在启用内建 uutils 时列出进程内可用的辅助命令mkdir、wc、sort、diff、jq等并说明async: true只是推迟有限命令的结果交付、不会延长timeout。六、执行模式与变体#模式触发条件说明1前台非 PTY 本地无客户端终端桥接时的默认路径使用executeBash()经streamTailUpdates()与TailBuffer(DEFAULT_MAX_BYTES)流式推送仅尾部更新2前台非 PTY 客户端终端session.getClientBridge()?.capabilities.terminal为 true、存在createTerminal且pty为 false以轮询更新流式输出当前终端内容带details.terminalId执行相同超时/中止行为后释放终端句柄3前台 PTYpty: true、UI 上下文、PI_NO_PTY ! 1runInteractiveBashPty()PtySession叠加层支持交互输入叠加层中按Esc杀掉会话4显式后台作业async: true且async.enabled向session.asyncJobManager注册作业并立即返回{ state: running, jobId }timeout: 0使作业没有工具强加的截止时间5自动后台化非 PTY 作业bash.autoBackground.enabled、无 PTY/客户端终端桥接、作业管理器未达运行上限先按前台受管作业启动存活超过等待窗口即后台化达到容量上限时 Bash 回退到直接前台执行6被拦截命令拦截器命中且替代工具可用不创建子进程返回指向read、grep、glob、edit或write的ToolErrorPTY 在 non-UI 上下文与PI_NO_PTY1时被忽略由canUseInteractiveBashPty()把关工具回退到非 PTY 执行并追加pty requested but unavailable in this environment; ran without a terminal提示。七、输出处理截断、溢出与最小化内存 tail 上限50 * 1024字节streaming-output.ts 中的DEFAULT_MAX_BYTES。超出后 sink 只在内存保留尾部窗口并标记截断流式回调节流executeBash()中启用流式时相邻两次onChunk调用间隔50msTUI 折叠预览在 Agent UI 中内联渲染时10视觉行bash.ts 的BASH_DEFAULT_PREVIEW_LINES——这是渲染器上限不是工具输出上限artifact 溢出截断发生时若分配成功完整输出写入 artifact截断元数据带artifactId工具包装层自动附加模型可见的恢复提示如Read artifact://id for full outputshell 最小化器非 PTY 执行把 minimizer 设置传入原生Shell会话当最小化器重写冗长输出时可见文本被替换为最小化文本若onMinimizedSave持久化了原始文本会追加[raw output: artifact://id]页脚原始文本存为独立的bash-originalartifact。八、限制与上限汇总项值出处默认超时300stool-timeouts.tsTOOL_TIMEOUTS.bash.defaulttimeout: 0禁用命令截止时间—正超时钳制可选全局上限tools.maxTimeout0表示不设全局上限随后是 Bash 区间1..3600s同上自动后台默认阈值60_000msbash.ts 的DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS来自 async 模块存在截止时间时进一步压到timeoutMs - 1000截止时间被禁用则阈值不受压—非 PTY 执行器定时器有截止时间时在max(1_000, timeoutMs)处挂宿主侧定时器并把相同正超时传给原生运行timeout: 0不传截止时间。超时的持久 shell 会话会被隔离quarantinebash-executor.ts内存 tail 上限50KBstreaming-output.tsDEFAULT_MAX_BYTES流式回调节流50msexecuteBash()TUI 折叠预览10视觉行BASH_DEFAULT_PREVIEW_LINES九、副作用清单文件系统fs.stat()校验cwd可能为完整本地输出bash与最小化器保留的原始输出bash-original分配并写入 artifact 文件expandInternalUrls(..., { ensureLocalParentDirs: true })在执行前为local://路径创建父目录子进程 / 原生绑定 / 客户端终端非 PTY 本地执行使用oh-my-pi/pi-natives的原生 shellShell.run()或executeShell()PTY 使用原生PtySession.start()客户端终端模式把进程执行委托给已连接客户端的 terminal 能力会话状态读取 async、auto-background、拦截器、direnv、全局超时上限、工具可用性与 shell 配置为显式/自动后台运行向session.asyncJobManager注册作业用session.getSessionId()隔离 shell 复用与异步会话键用session.allocateOutputArtifact()分配溢出文件命令含变更类gh issue/gh pr子命令时在执行前失效github-cache行invalidateGithubCacheForBashCommand使后续issue:///pr://读到变更后的状态用户可见提示 / 交互 UIPTY 模式打开标题为Console的 TUI 叠加层并转发输入后台启动消息说明结果完成时会自动投递且在此之前可用hub工具等待后台工作 / 取消async 与 auto-background 作业在初始工具返回后继续运行直到完成、取消或截止时间除非timeout: 0禁用了它取消会中止原生运行PTY 叠加层关闭也会杀掉 PTY。十、错误处理一览输入校验非法 env 键 →ToolError(Invalid bash env name: key)禁用时请求 async →ToolError(Async bash execution is disabled...)缺少异步作业管理器 →ToolError(Background job manager unavailable for this session.)cwd缺失/非法 →ToolError(Working directory does not exist: ...)或ToolError(Working directory is not a directory: ...)对应 bash.ts#L1100-L1109。拦截器命中命令 → 附Blocked: rule.message与原始命令的ToolError非法拦截器正则在compileRules()中被静默跳过。内部 URL 展开不支持的 scheme、未知 skill、路径穿越、缺少路由支持、路由解析失败均从 bash-skill-urls.ts 抛出ToolError。执行非零退出 → 标记isError的工具结果带details.exitCode文本以Command exited with code n结尾缺失退出码 → 抛出ToolError(Command failed: missing exit status)超时 → 本地/PTY 执行返回isError结果且details.timedOut: true并附超时提示客户端终端桥接在杀掉终端并尝试最后一次输出读取后抛出ToolError受管后台执行把两种形态都记录为失败作业用户中止 → 调用方 signal 被中止时抛ToolAbortErrorartifact 分配/保存失败在saveBashOriginalArtifact()与OutputSink.#createFileSink()中被吞掉执行在没有该 artifact 的情况下继续。十一、进阶行为与实战提示并发模型BashTool设置strict trueconcurrency按调用解析——pty: true为exclusive占用终端 UI其余为shared因此一条 assistant 消息中多个非 PTY bash 调用可并行。并行调用在同一 shell 会话键上重叠时第一个调用拥有持久Shell其余在隔离的一次性 shell 中运行见 bash-executor.ts 的shellSessionsInUseshell 会话复用executeBash()以shell 路径、配置前缀、快照路径、序列化 env、可选会话键、minimizer 配置为键缓存原生Shell实例工具调用路径传sessionKey: session.getSessionId()。会话级 key 按会话隔离复用无 key 时回退到 shell 配置/快照/env非交互环境加固非 PTY 运行经buildNonInteractiveEnv()把NON_INTERACTIVE_ENV与env合并分页器禁用PAGERcat、编辑器提示禁用、TERMdumb、GIT_TERMINAL_PROMPT0、CItrue等细节见 docs/bash-tool-runtime.mdPTY 运行则继承用户环境并在自定义env值之前前置TERMxterm-256color让编辑器、分页器与 TUI 表现如正常终端URL 展开转义差异command中的替换做 shell 转义env与cwd使用noEscape: true因为它们是环境变量值/文件系统路径而非 shell 文本拦截器边界checkBashInterception()仅在规则tool名出现在ctx.toolNames中时才阻止它是向专用工具的尽力路由不是 shell 安全策略——heredoc、参数展开、命令替换、反引号、分组与错误引号只接受整输入检查direnvbash.direnv默认auto且遵守 direnv 允许列表未被允许的.envrc不执行设off可绕过预检bash.direnvLoadTimeoutMs控制冷加载预算。综上oh-my-pi 的bash工具是一套分层设计审批策略bash.patterns 内置关键模式回答能否执行拦截器bashInterceptor.patterns回答该谁执行执行层再按 PTY/桥接/后台/前台分派到不同后端并由OutputSink统一处理截断与溢出。配置时按此分层思考即可在不牺牲安全性的前提下获得可预测的命令执行行为。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考