cli-gateway
统一 CLI 进程调用网关:让"AI 调 CLI 是否真跑、输出能否逐字核对"变得可观测、可审计、可回放。
v1 定位:本地 subprocess 薄包装。不涉及任何 URL/网络请求。
为什么
团队有 9 个 CLI(claude / codex / kilo / mcode / goose / opencode / qoder / crush / hermes)作为 agent 运行时。痛点:
- 调用了但无法直接观测原始 stdout/stderr,只能靠进程日志+时间戳佐证"跑了";
- 无法逐字核对输出、无法回放、无法统一审计。
cli-gateway 把任何 CLI 调用包装成"全量日志落盘 + 可回放 + 结构化返回",让每次调用可观测、可审计、可验证。
快速开始
pip install ai-cli-gateway # PyPI 安装(PyPI 上 `cli-gateway` 名字已被占,故用此名)
pip install -e . # 或本仓开发安装(或直接 PYTHONPATH=.)
# 作为库
from cli_gateway import run, replay
res = run(["claude", "--version"])
print(res.exit_code, res.duration_ms, res.log_path)
print(replay(res.log_path)) # 逐字回放
# 作为 CLI
# 注意:--timeout/--dir 必须写在 -- 之前;-- 之后的参数全部属于被调命令
cli-gateway run --timeout 10 -- echo hello
cli-gateway replay ~/.cli-gateway/logs/20261018-190000-echo-abc12345.log
cli-gateway logs
cli-gateway doctor
返回结构(GatewayResult)
| 字段 | 说明 |
|---|---|
command |
原始命令 |
exit_code |
退出码:命令未找到=127,启动失败(不可执行等)=126,超时=124,其余为命令真实退出码(POSIX 被信号杀死时为负信号值) |
duration_ms |
耗时毫秒 |
log_path |
全量日志文件路径 |
stdout_tail / stderr_tail |
输出尾部(默认各 2000 字符,从日志文件取尾,内存有界) |
timestamp / pid / success |
时间戳 / 真实进程号 / 是否成功 |
state |
枚举:completed / timed_out / not_found / spawn_error / killed(spawn_error 是对 shell 惯例枚举的扩展) |
cwd |
本次调用的 working directory(未传为 None) |
truncated |
输出超过 tail 长度被截断时为 True |
timed_out |
超时标志(等价 state == "timed_out") |
env_overrides |
显式传入的 env 覆盖项(含 KEY/TOKEN/SECRET/PASSWORD 的值脱敏为 ***,不记录全量环境) |
日志格式
每次调用写一个文件 <ts>-<cmd>-<id>.log:元数据头(command/timestamp/pid/cwd/env_overrides/timeout)+ STDOUT 段 + STDERR 段 + END + 结果尾(exit_code/duration_ms/state),可逐字回放、可审计。超时路径保留已产生的部分输出(partial)。
注册表(run_cli,白名单路径)
from cli_gateway import REGISTRY, CliSpec, run_cli
REGISTRY["claude"] = CliSpec(default_timeout=600, tail=2000) # 零依赖顶层 dict
res = run_cli("claude", "--version") # 未注册名字 → KeyError;run() 不查注册表
CliSpec 字段:default_timeout / capture / tail / cwd / env / retry_on。重试只在显式传 retry_count 时发生,且只重试"启动前失败"(not_found / spawn_error;超时与非零退出绝不自动重试),每次尝试独立日志文件。retry_on 字段当前仅支持 "spawn",其他值保留但不生效。
Windows 须知:npm shim 与直调
Windows 上 Popen 直控走 CreateProcess,只解析 .exe——npm 全局安装的 CLI(claude / kilo / mcode / opencode 等)目录里只有 xxx.cmd shim,直接传名字会得到 127/not_found。两种调用方式:
- 原生 exe 直调:codex / goose / qoderclicn / hermes;crush 的 npm shim 没有
.exe同名文件,CreateProcess会继续搜 PATH 命中 WinGet 的crush.exe,同样直调可用; - npm shim 用
cmd /c <name> ...包装:gateway 的taskkill /T树杀仍覆盖 cmd 与其下的整棵进程树。
Git Bash 传参注意:MSYS 会把独立参数 /c 转换成 C:\,命令行路径须写 cmd //c 或设 MSYS_NO_PATHCONV=1;走 Python 库传 argv 列表不受影响。
真机验证矩阵(2026-10-18,Windows Server 2022)
9 个 CLI 全部经网关真实验证:--version 探测 + 真实模型调用(提示词"只回复两个字:收到")各一次。
| CLI | 版本 | 直调方式 | 非交互用法 | 真实调用 |
|---|---|---|---|---|
| claude | 2.1.177 | cmd /c claude |
claude -p "<prompt>" |
✅ |
| codex | 0.162.1 | 直调 exe | codex exec "<prompt>" |
✅ |
| kilo | 7.7.5 | cmd /c kilo |
kilo run "<prompt>" |
✅ |
| mcode | 0.4.12 | cmd /c mcode |
mcode exec "<prompt>" |
✅ |
| goose | 1.51.0 | 直调 exe | goose run -t "<prompt>" |
✅ |
| opencode | 1.18.31 | cmd /c opencode |
opencode run "<prompt>" |
✅ |
| qoder | 1.1.72 | 直调 exe(命令名 qoderclicn) |
qoderclicn -p "<prompt>" |
✅ |
| crush | v0.88.0 | 直调 exe(自动命中 WinGet 版) | crush run "<prompt>" |
✅ |
| hermes | v0.21.3 | 直调 exe | hermes -z "<prompt>" |
✅ |
矩阵可在你自己的机器上实时复现:
cli-gateway doctor # 体检内置 9 个编码 CLI:形态/版本/日志路径
cli-gateway doctor --json # 结构化输出(agent 消费友好)
cli-gateway doctor --no-probe # 静态快查:只看安装形态,不实跑版本探测
cli-gateway doctor --only codex # 只查指定命令
形态判定以网关真实调用结果为准(先直调,Windows 下 not_found 自动再试 cmd /c 包装),不是静态猜测;全部可用退出码 0,否则 1——可直接接进 CI 或 agent 的启动自检。
观测模式:原子 / 元素 / 化合物
任务过程的物理化学模型(--observe + --task-id):
- 原子:一次网关调用。问(argv/prompt)与答(输出)成对出现、天然不可分割;
exit_code+duration_ms是"结合完成"的证据。守恒律:原始日志是唯一真相,事件 sidecar(<log>.events.jsonl)是派生解读——解析失败降级为全文 answer 事件,绝不丢字。 - 元素:CLI 的种类。周期表(
ELEMENTS)里的元素带一条观测配方——激发它以"离散谱线"(结构化 JSON 事件流)而非"连续谱"(纯文本)发光。同位素 = 版本/格式差异。 - 化合物:任务 = 化学键(上一个原子的输出成为下一个原子的输入)连接的原子图;分子式 = 组成配比。
cli-gateway run --observe --task-id fix-login -- cmd //c claude -p "分析这个 diff"
cli-gateway run --observe --task-id fix-login -- codex exec "修复它"
cli-gateway task fix-login # 原子清单 + 分子式(--json 机器可读)
# task fix-login: 2 atoms | formula: claude×1 + codex×1
from cli_gateway import run, read_events, task_atoms, task_formula
run(["claude", "-p", "分析"], observe=True, task_id="fix-login")
events = read_events(log_path + ".events.jsonl") # question → [note…] → answer → exit
atoms = task_atoms("fix-login") # 化合物检索
task_formula(atoms) # 'claude×1 + codex×1'
谱线现状:claude(--output-format stream-json --verbose)与 codex(exec --json)为 jsonl 离散谱,且已类型化——事件 kind 为 system / text / tool_call / tool_result / reasoning / result / error(assistant 消息里的 tool_use 块 → tool_call 事件,对话树可重建)。claude 的 schema 来自真机实测;codex 工具类 item 的字段名为推测实现(宽容兜底,待真任务样本回填)。未收录形态退化为 note 兜底,payload.raw 永远保留;原始字节始终以 raw 日志为准。
边界:answer 事件含输出全文,适合中小输出(超大输出的流式事件化留待后续);spawn 失败的调用不写 sidecar(raw 日志仍有完整记录)。
子智能体编队(agents/)
agents/ 内置 9 个编码 CLI 的 ZCode 子代理操作员提示词——上表 9 个 CLI 各配一个"专属操作员":主会话(工头)负责拆解派工与验收,子代理通过 Bash 调用对应 CLI 真进程干活。方法论(C4/ADR/TDD/变异测试/八维审查等)全部内置在提示词里,开箱即用。
| 文件 | 角色 | 擅长 |
|---|---|---|
| claude.md | 首席架构 | C4/ADR 架构规格、多维评审、技术决策 |
| codex.md | 资深攻坚 | 五 Why 根因、性能剖析、Strangler Fig 重构 |
| opencode.md | 全栈 | React/Next + Node/Python + DB 前后端通吃 |
| mcode.md | 功能实现 | TDD 红绿重构一棒到底 |
| kilo.md | 功能实现 | 补位实现、修测试、环境坑 |
| crush.md | 功能实现 | 快速编码、LSP 语义辅助 |
| goose.md | QA | 测试金字塔、Playwright 真机验证、变异测试防假绿 |
| qoder.md | 代码评审 | 八维审查、异构模型交叉复核 |
| hermes.md | 研究员 | 技术调研、复盘 postmortem、经验库回写 |
安装
# 用户级(所有项目生效)
cp agents/*.md ~/.zcode/agents/
# 或项目级(仅当前项目生效)
cp agents/*.md .zcode/agents/
重启 ZCode 会话后生效,Agent 工具即可按 name 派工(如 subagent_type: "codex")。其他支持自定义子代理的框架同样可用:这些 .md 本质是"角色化操作员提示词 + 调用配方",粘贴为任意 agent 的系统提示词即可复用。
使用
主会话按岗位派工,派单三要素(目标/范围/完成标准)写清,例如:
# 任务
修复 tests/test_api.py 中 3 个失败用例(并发写竞态)
# 完成标准
pytest 全绿 + 亲自运行过验证命令
# 约束
只改 src/worker.py,不动接口签名
子代理按固定三段式汇报(修改了什么 / 如何验证 / 哪些仍未验证);经 cli-gateway 留证调用的,汇报附 log= 路径,验收方 cli-gateway replay <log> 逐字回放复核——验收不轻信汇报文字。
提示词结构(自定义时照此改)
每个文件的固定骨架:frontmatter(name 派工标识 / description 主会话路由依据 / tools 工具白名单 / injectAgentsMd)+ 正文(岗位定位与方法论 / 完成条件 / 预检三问 / 固定汇报格式 / 退化路径 / 调用方式含 cli-gateway 留证配方 / 红线 / 实测档案)。
定制指引
- CLI 名称/版本不同:改「调用方式」节的命令与核对版本即可,方法论不绑定具体 CLI
- 后端不是 GLM:这些操作员原本跑在 GLM 后端,换 provider 只需改 CLI 侧的 provider 配置,提示词无需动
- POSIX 环境:调用配方中
cmd //c包装与 MSYS 注意事项仅适用 Windows + Git Bash,POSIX 直接写命令名 - 开源版说明:
agents/为脱敏模板(团队名/内网配置/实测记录已替换为占位或通用表述),各文件末尾的「实测档案」留作你的团队自行回写
超时与进程组
超时(timeout 秒,默认无限等)会按进程组/进程树终止:POSIX 用 start_new_session + os.killpg(先 SIGTERM,宽限 2s 后 SIGKILL);Windows 用 CREATE_NEW_PROCESS_GROUP + taskkill /F /T。这会大幅降低孤儿孙进程残留的概率,但不是绝对保证——极端场景(第三方进程自行脱离进程组/进程树、或孙进程继承管道写句柄导致输出流不关闭)可能有残留或输出不完整,此时结果会置 truncated=True 并在日志尾部追加 # warning: 行,由调用方按需兜底。
POSIX 下进程被信号杀死时 state="killed"、exit_code 为负信号值;Windows 正常退出(含非零退出码)均为 completed。
输出读取统一按 UTF-8(errors="replace"),日志始终是合法 UTF-8。gateway 默认向子进程注入 PYTHONUTF8=1(调用方或父环境已显式设置该变量时不覆盖),使 python 类子进程在 Windows 传统代码页(CP936 等)环境下也输出 UTF-8——注意子进程会看到这个变量。
设计边界(v1)
- 只做本地 subprocess,不发起 URL/网络请求——未来 v2 若加远程/HTTP 能力,服务端请求 URL 时仅允许 http/https,且发请求前校验 host、拒绝 localhost/环回/私有/保留地址;
- 不做沙箱(用 Docker 的 MCP 网关管沙箱,见下);不做 Web UI;不做多租户。
与 docker/mcp-gateway 的关系(不同层)
| docker/mcp-gateway | cli-gateway | |
|---|---|---|
| 管 | MCP 服务器(外部工具数据源,Docker 容器) | CLI 进程调用(本地 subprocess) |
| 解决 | AI 统一访问 MCP 服务器 + 认证/密钥 | CLI 调用日志落盘/回放/结构化返回 |
| 层 | 协议层(MCP) | 进程层(subprocess) |
两者互补不冲突:MCP 网关管"AI 连外部工具",cli-gateway 管"AI 调本地 CLI"。
License
MIT
安全约束(Mimosa)
本仓库 v1 不发起任何网络请求。若未来扩展远程调用,必须遵守:仅允许 http/https;发请求前校验 host,拒绝 localhost、环回(127.0.0.0/8, ::1)、私有(RFC1918)与保留地址。
Metadata
Release files for ai-cli-gateway 1.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ai_cli_gateway-1.7.0.tar.gz | 54.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ai_cli_gateway-1.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 99.9 kB
Release files / ai_cli_gateway-1.7.0.tar.gz
| Download URL | ai_cli_gateway-1.7.0.tar.gz |
|---|---|
| Size | 54.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
22829538b70f4807aebdff9c5b44eb744d53076ba92ae90054c3ba5c8a78baf0
|
|
BLAKE2b-256 checksum How to use checksums |
5ad6db75d1989daf64b8dd88699eba0e0ca883b54173de0818cfee32783a1294
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / ai_cli_gateway-1.7.0-py3-none-any.whl
| Download URL | ai_cli_gateway-1.7.0-py3-none-any.whl |
|---|---|
| Size | 45.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf40fcd3eb6d68349ebfe6471850e9486b799d2d8e1b24e89a5e98e46d989243
|
|
BLAKE2b-256 checksum How to use checksums |
44189ceab69c146f1fe69d13ade72abc9c72a87c298ad835c3858671f55bfbf5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for ai-cli-gateway 1.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ai_cli_gateway-1.7.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.