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.ts | PTY/TUI 执行路径 |
| packages/coding-agent/src/tools/bash-interceptor.ts | 拦截"该用专用工具而非 shell"的命令 |
| packages/coding-agent/src/tools/bash-skill-urls.ts | 内部 URL(skill://、local://等)展开为本地路径 |
| packages/coding-agent/src/tools/bash-pty-selection.ts | canUseInteractiveBashPty()判定是否可走本地 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 定义,字段如下:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
command | string | 是 | 要执行的 shell 命令文本。仅当cwd未提供时,前导的cd <path> && ...会被改写为cwd字段并从命令中剥离 |
env | Record<string, string> | 否 | 额外环境变量。键必须匹配^[A-Za-z_][A-Za-z0-9_]*$,否则工具抛错。值会经过内部 URL 展开,并作为环境变量值(而非 shell 文本)传入 |
timeout | number | 否 | 超时秒数。默认300;0表示禁用截止时间。正值先受tools.maxTimeout(若为正)限制,再钳制到 Bash 区间1..3600 |
cwd | string | 否 | 工作目录,经resolveToCwd相对session.cwd解析;必须存在且为目录 |
pty | boolean | 否 | 请求 PTY 模式,默认false。仅当pty: true、PI_NO_PTY !== "1"且工具上下文存在 UI 时才真正使用 PTY |
async | boolean | 否 | 请求后台执行。仅当会话开启了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/max;maxTimeout <= 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 | 优先使用专用工具而非 Bash | JavaScript 正则、可选 flags、工具名与消息 | 返回 Bash 工具错误,指示模型改调指定专用工具 |
4.1bash.patterns:审批策略
bash.patterns用于"无论是否存在其他工具都能完成,该命令都必须被允许、人工确认或拒绝"的场景。规则有序,第一条匹配的规则生效。每条规则由matchglob 与approval(allow/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());最后检查剥离前导NAME=value赋值后的片段。因此锚定规则^\s*git\s+commit\b能同时命中:
git add file && git commit -m "message" GIT_AUTHOR_NAME=Dev 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/more | read |
grep/rg/ripgrep/ag/ack | grep |
带-name/-iname/-type/--type/-glob的find/fd/locate | glob |
sed -i、perl -i、awk -i inplace | edit |
带文件重定向的echo/printf/cat <<(/dev/null等设备目标除外,fd 复制>&2不匹配) | write |
nohup、行尾裸& | hub(op:"start") |
bun/npm/pnpm/yarn dev|start、vite、next dev、nuxt dev、nodemon、lldb、gdb、tail -f、未 detach 的docker compose up | hub(服务/调试器) |
bun/npm/pnpm/yarn <script>、cargo watch、watchexec、pytest、vitest、jest、tsc且带--watch/-w | hub(watch 模式) |
4.3 两套策略的交互与选择指南
- 审批策略在执行前解析:命中的
bash.patternsdeny永远不会到达拦截器;prompt只有在用户接受审批请求后才到达拦截器; - 若已接受的调用又命中拦截器规则,Bash 调用依然不会运行,模型收到路由错误并应改调专用工具;
- 避免对同一操作在两处都配置,除非你有意要这种"两步"行为。例如
cat *的prompt规则加上启用的cat→read拦截器,会先请求用户批准 Bash,随后拒绝 Bash 并要求模型使用read。
选择原则:
- 问题是"这条命令能否执行" → 用
bash.patterns; - 问题是"该操作应由哪个工具完成" → 用
bashInterceptor.patterns。
此外要理解一个边界(来自 docs/bash-tool-runtime.md):bash.patterns只约束bash工具本身,无法约束eval等可通过子进程起 shell 的工具——同一条命令走eval时deny规则不生效。要跨两个面加固破坏性命令,需要为eval另行配置tools.approval.eval(prompt或deny)。审批也不意味着隔离:批准后进程仍拥有 shell 的完整文件系统、网络与子进程访问。
五、执行管线:从 execute() 到子进程
BashTool.execute()的完整流程(编号继承自 docs/tools/bash.md 的 16 步管线,关键步骤均与 bash.ts#L1004-L1144 源码对应):
- 读取
command,校验env,timeout默认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解析cwd,fs.stat()验证目标存在且是目录;timeout: 0禁用截止时间;否则clampTimeout("bash", requested, tools.maxTimeout)先应用正全局上限(若配置),再套TOOL_TIMEOUTS.bash(min: 1,max: 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可用时先分配输出 artifact,artifact 路径/ID 传入 sink,大输出可溢出到磁盘; executeBash()加载 shell 配置、可选 shell 快照与 shell minimizer 设置,然后通过持久原生Shell会话或一次性executeShell()运行(详见 docs/bash-tool-runtime.md);runInteractiveBashPty()创建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 必须用hub(op:"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 | 前台 PTY | pty: true、UI 上下文、PI_NO_PTY !== "1" | runInteractiveBashPty()+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的ToolError |
PTY 在 non-UI 上下文与PI_NO_PTY=1时被忽略(由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调用间隔50ms; - TUI 折叠预览:在 Agent UI 中内联渲染时
10视觉行(bash.ts 的BASH_DEFAULT_PREVIEW_LINES)——这是渲染器上限,不是工具输出上限; - artifact 溢出:截断发生时若分配成功,完整输出写入 artifact,截断元数据带
artifactId,工具包装层自动附加模型可见的恢复提示(如Read artifact://<id> for full output); - shell 最小化器:非 PTY 执行把 minimizer 设置传入原生
Shell会话;当最小化器重写冗长输出时,可见文本被替换为最小化文本,若onMinimizedSave持久化了原始文本,会追加[raw output: artifact://<id>]页脚(原始文本存为独立的bash-originalartifact)。
八、限制与上限汇总
| 项 | 值 | 出处 |
|---|---|---|
| 默认超时 | 300s | tool-timeouts.tsTOOL_TIMEOUTS.bash.default |
timeout: 0 | 禁用命令截止时间 | — |
| 正超时钳制 | 可选全局上限tools.maxTimeout(0表示不设全局上限),随后是 Bash 区间1..3600s | 同上 |
| 自动后台默认阈值 | 60_000ms(bash.ts 的DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS,来自 async 模块),存在截止时间时进一步压到timeoutMs - 1000;截止时间被禁用则阈值不受压 | — |
| 非 PTY 执行器定时器 | 有截止时间时在max(1_000, timeoutMs)处挂宿主侧定时器,并把相同正超时传给原生运行;timeout: 0不传截止时间。超时的持久 shell 会话会被隔离(quarantine) | bash-executor.ts |
| 内存 tail 上限 | 50KB | streaming-output.tsDEFAULT_MAX_BYTES |
| 流式回调节流 | 50ms | executeBash() |
| TUI 折叠预览 | 10视觉行 | BASH_DEFAULT_PREVIEW_LINES |
九、副作用清单
- 文件系统:
fs.stat()校验cwd;可能为完整本地输出(bash)与最小化器保留的原始输出(bash-original)分配并写入 artifact 文件;expandInternalUrls(..., { ensureLocalParentDirs: true })在执行前为local://路径创建父目录; - 子进程 / 原生绑定 / 客户端终端:非 PTY 本地执行使用
@oh-my-pi/pi-natives的原生 shell(Shell.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://读到变更后的状态; - 用户可见提示 / 交互 UI:PTY 模式打开标题为
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 被中止时抛
ToolAbortError; - artifact 分配/保存失败在
saveBashOriginalArtifact()与OutputSink.#createFileSink()中被吞掉,执行在没有该 artifact 的情况下继续。
十一、进阶行为与实战提示
- 并发模型:
BashTool设置strict = true;concurrency按调用解析——pty: true为"exclusive"(占用终端 UI),其余为"shared",因此一条 assistant 消息中多个非 PTY bash 调用可并行。并行调用在同一 shell 会话键上重叠时,第一个调用拥有持久Shell,其余在隔离的一次性 shell 中运行(见 bash-executor.ts 的shellSessionsInUse); - shell 会话复用:
executeBash()以(shell 路径、配置前缀、快照路径、序列化 env、可选会话键、minimizer 配置)为键缓存原生Shell实例;工具调用路径传sessionKey: session.getSessionId()。会话级 key 按会话隔离复用;无 key 时回退到 shell 配置/快照/env; - 非交互环境加固:非 PTY 运行经
buildNonInteractiveEnv()把NON_INTERACTIVE_ENV与env合并(分页器禁用PAGER=cat、编辑器提示禁用、TERM=dumb、GIT_TERMINAL_PROMPT=0、CI=true等,细节见 docs/bash-tool-runtime.md);PTY 运行则继承用户环境并在自定义env值之前前置TERM=xterm-256color,让编辑器、分页器与 TUI 表现如正常终端; - URL 展开转义差异:
command中的替换做 shell 转义;env与cwd使用noEscape: true,因为它们是环境变量值/文件系统路径而非 shell 文本; - 拦截器边界:
checkBashInterception()仅在规则tool名出现在ctx.toolNames中时才阻止;它是向专用工具的尽力路由,不是 shell 安全策略——heredoc、参数展开、命令替换、反引号、分组与错误引号只接受整输入检查; - direnv:
bash.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),仅供参考