把 AI Coding Agent 和真实项目放在两台机器上。
Agent Node 跑官方 Claude Code / Codex CLI,AI 登录只在这台;Runtime Node 放源码、Git、构建和工具链,模型的读、搜、改、跑命令经一条持久的 SSH stdio MCP 通道在这里执行,而且可以用一个专用的低权限账号执行。
Runtime Node Agent Node
┌────────────────────────┐ ┌─────────────────────────┐
│ workspace / Git │ │ Claude Code / Codex CLI │
│ build / test / tools │◀── SSH stdio ─▶│ login + controller/tmux │
│ ccnm MCP runtime │ MCP │ session supervisor │
└────────────────────────┘ └─────────────────────────┘
Keep the AI coding agent and the real project runtime on separate machines. The agent runs on an Agent Node (macOS only) and reaches the project on a Runtime Node (macOS or Debian 13 / x86_64) over a persistent SSH stdio MCP transport. Docs are in Chinese.
它不做的事:不整库同步;不把 AI 登录下放到 Runtime;不实现私有模型客户端;不做任务规划、审查、发布或多 Agent 调度(那是编排项目的事,见执行接口交接)。模型需要的源码片段仍会经工具结果进入 Agent 和模型服务——分开机器不等于"源码永不离开 Runtime"。
| 最新发布 | v0.10.1(2026-10-04)。v0.10.0 的 tag 打了,但发布流程在 macOS 门禁上被一条测试的时序问题挡住、没有产出,修好测试后直接发了 v0.10.1,两者只差那条测试和版本号 |
| v0.10.1 比 v0.9.0 多了什么 | 精确停止、完整结果分页、写锁预检、跨账号清理(P58–P61);Machine API 断开后任务照跑、停止标志不丢、ssh 连不上记 failed(P63);交互会话 stop 等通道退出再确认、ccnm log 把被停掉的会话记成"被停止"、doctor 能认出版本号相同的不同构建(P64);Operator 没权限看项目目录时不再误报"不存在"、Machine API 的 session.result 给出会话没起来的原因、doctor 写明 Codex 登录只看了本地(P65);ccnm status 指出项目终端在别的实例手里、doctor 带回 Agent 拒绝所选实例的原因、ccnm controller install 带上非默认的配置和状态位置、中文帮助补全(P66);apply_patch 两处并发误判(P67 与同期修复)。内部协议和 v0.9.0 不兼容:两台机器要一起升,混装时起会话报 CCNM_E_VERSION |
| 真机验收 | P62(macOS Agent → Debian 13 Runtime)分两轮:2026-09-30 Claude 那一半跑通、Codex 被账号额度挡住(第一轮);2026-10-04 用 v0.10.1 续跑,Codex 那一半全部跑通,P63–P67 修的各条在真机上复验通过(续跑记录)。阶段还没完成:失败矩阵里"Agent 上的监督进程丢了"和"Agent 上的原始输出丢了"两项结果不对(F22、F23),另查出 F20、F21、F24,P69 已离线修好 |
每一项能力验到了哪一步、明确没验过什么,逐条在支持矩阵里。
- 项目在一台机器上,AI 订阅登录在另一台上,两边都不想挪。 比如源码和工具链在服务器上,不想在那里登录 Claude;登录着 Claude 的 Mac 上又不该出现源码。
- 不想让模型用你自己的账号跑命令。 Runtime 那边用专用低权限账号执行,模型碰不到你的 SSH 私钥和 AI 登录;还可以给每条命令再套一层 OS 沙箱。
- Claude Code / Codex 已经在你本机开着,项目在远端。 不用 ccnm 启动 Agent,只把远端项目作为一组 MCP 工具交给它。
- 要让别的程序驱动这一切(编排器、批处理):stdio 上的 JSON-RPC,不开网络端口。
| 角色 | macOS | Linux | Windows |
|---|---|---|---|
| Agent Node(跑 Claude/Codex) | 支持 | 没实现(Controller 是 launchd LaunchAgent) | 没实现 |
| Runtime Node(放项目) | 支持 | 只验过 Debian 13 / x86_64 | 没实现 |
Codex 受管会话只认实测过的 Codex 0.154.0;Claude Code 用 Agent 上装的那个。
两台机器装同一个构建。去 Releases 拿包:macOS 取 macos-universal,Linux 取 linux-x86_64(只有 Runtime 那一半)。
tar -xzf ccnm-<版本>-macos-universal.tar.gz
mkdir -p ~/.local/bin
mv ccnm ~/.local/bin/ccnm.new && mv ~/.local/bin/ccnm.new ~/.local/bin/ccnm三个会咬人的地方:
- 别用
cp覆盖跑过的 ccnm。 Apple Silicon 上写进已执行过的 Mach-O 会让代码签名失效,之后每次执行都Killed: 9,而老进程还在用老代码跑。上面"新文件 + 改名"就是为了避开它。 - 浏览器下载的包带隔离属性,macOS 拒绝执行:
xattr -d com.apple.quarantine ccnm。用curl下载不会带。 - 版本号一样不代表是同一个构建。 两次发版之间从 main 编的构建都叫上一个发布的号(比如 v0.10.1 之后自己编的也叫 0.10.1)。v0.10.0 起 doctor 在版本号相同时再比内部协议最高号,对不上就报
not the same build——但只有新的那一端会说,旧构建的 doctor 照样全绿,起会话时才报message is not valid for protocol 1。所以两台都跑一遍 doctor,以新的那台为准。
前提:非交互 SSH 两个方向都通(Runtime 上敲命令的人 → Agent;Agent → Runtime 的执行账号,执行账号不需要任何出站私钥);Agent 上官方 CLI 已登录;Runtime 上装好 git、ripgrep 和项目工具链。
在 Runtime Node(放项目那台):
ccnm init --agent <agent 的 ssh alias>
cd /path/to/project
ccnm workspace add my-project在 Agent Node(跑 Claude 那台):
ccnm init --runtime <runtime 的 ssh alias>
ccnm controller install回到 Runtime Node:
ccnm doctor my-project # 只读检查,先把红的处理掉
ccnm my-project # 开始init 给哪个 flag 就说明这台机器是谁:放项目的给 --agent,跑 Claude 的给 --runtime,两个一起给会被拒。SSH alias 只在定义它的机器上有意义,所以两台各写各的。
日常命令(两台机器上都能敲):
ccnm my-project # 起会话并接上
ccnm attach my-project # 接回已有会话(简写 ccnm a)
ccnm ls # 所有项目:在不在跑、跑了多久、工具通不通
ccnm status # 同上,更细(简写 ccnm st)
ccnm log # 跑过的会话,最新的在前
ccnm stop my-project
ccnm my-project --print "修复 parser 测试" # 一问一答,不进 tmux
ccnm cleanup my-project # 在 Runtime 上:预览会话残留,再按提示加 --apply每一步的完整说明和 doctor 红了怎么办见快速开始;Codex、Agent Instance、多行 prompt 见使用说明。默认说中文,要英文加 --lang en。
默认配置没有替你建隔离账号。模型命令如果以你自己的账号跑,ccnm 只分开了机器,没分开权限。真实项目先在 Runtime Node 建一个低权限账号,并写进配置:
[nodes.runtime]
runtime_user = "ccrun"意思是"Agent 连进来之后,项目命令以 ccrun 的身份跑",不是"你要用 ccrun 敲 ccnm"。做法见生产安全。
- Linux 上项目可以放在执行账号的家目录里,哪怕你(Operator)进不去(Debian 12 起家目录默认 0700):登记时写绝对路径,
ccnm doctor的Runtime 上的项目一行会是"没查",执行账号的回答在workspace 根目录那一行。v0.9.0 还会把它误报成"不是这台机器上的目录",那一版上把项目放到/srv/...这类你能进入父目录的地方,见排错手册。 - 项目和 Claude 登录本来就在同一个账号下时没有东西可隔离,ccnm 默认在 MCP 握手之前就拒绝,doctor 红在
Claude 凭据那一行。两条出路见快速开始。
Runtime 有 12 个工具,实际给哪些取决于读写模式和配置,以 tools/list 为准:外部 read 模式 7 个只读工具,coding 会话 11 个,Runtime 上有可转接的 MCP server 时加 call_mcp_tool。
workspace_info read_file list_files search_text
apply_patch exec_command read_output load_skill
view_image read_notebook stop_command call_mcp_tool
- skills:项目自带的(
.claude/skills/等)和两台机器上装好的,经load_skill交给模型——官方 CLI 的当前目录不在项目机器上,自己发现不了。见使用说明。 - MCP server:项目那台机器上的经
call_mcp_tool,和exec_command过同一道门;Agent 机器上装的经ccnm_agent下的同名工具,默认只给远端地址,本机跑的要在 Agent 配置里点名——那是额外信任,不是只读白名单。见使用说明。 - Agent 那一侧不是一无所有。 官方 CLI 的原生 Read/Bash 被关掉,但
ccnm_agent的工具以 Agent 账号运行;受管会话默认还开着web_search和mcp_servers,抓网页、子代理、待办清单要另开(agent_tools,见配置说明)。 - 后台命令能启动、分页读、停止,但活不过 MCP 连接。关掉终端只是离开 tmux;MCP 断线才会收掉命令。
- 两个按 workspace 打开的开关(默认关):
exec_sandbox = "codex"把 Runtime 命令包进 OS 沙箱(限制工作区外写入、.git写入和网络,不覆盖 Agent 上的 MCP,见配置说明);codex_exec_server = true已于 2026-09-17 封存,新项目别用。
- 本机已经开着 Claude Code / Codex:
ccnm mcp bridge <workspace>把远端项目作为一组 MCP 工具交给它,权限由 Runtime 侧的external_mcp决定(默认关)。契约ccnm.workspace-mcp/1已冻结,见使用说明。 - 让程序驱动 ccnm:
ccnm rpc,stdio 上的 JSON-RPC 2.0。契约ccnm.machine/1已冻结;当前只有非交互print,结果可倒序分页读回(每个流保留最后 32 MiB),启动前问一次写锁、被占回 busy(只是观察,不是预留)。schema、fixture 和可以直接抄走的 Python 客户端见协议说明。
- 不声明任何网络出口边界。 模型的命令能连到哪里没有逐项验证;关掉
web_fetch也不等于禁止 MCP 或命令外发数据。 - 写互斥要求各入口用同一个 state 目录。 同一棵树配两个
XDG_STATE_HOME就是两把互不知晓的锁。 - 离开进程组的后代够不着。 Runtime MCP server 派生的
setsid/ 守护进程、以及mcp-serve被kill -9后留下的后台命令,ccnm 停不掉;写锁会因此保持 unknown,按运维手册人工收。 - 项目和 Agent 同机(colocated)没有真实验收,明确拒绝,不静默降级。
- 受管 Codex 会话执行命令前不问你。 Claude 会话每条
exec_command都会停下来问,Codex 不会:那道闸靠的是只有 Claude Code 认的键(配置说明)。doctor 的Command approval行对 Codex 是 WARN,写明不问(P69 起;之前的构建说"会问",那句不对)。 - doctor 验不了 Codex 的令牌还有没有效,只能看到"登录过"(P65 起那一行自己会说);令牌被吊销要到会话的第一条消息才知道。P62 续跑查出的 F20–F24 列在续跑记录第 7 节,现象和绕法在排错手册。
阶段完成、Agent 退出成功和项目验收通过是三个不同的结论。
| 用途 | 文档 |
|---|---|
| 上手与使用 | 快速开始 · 使用 · 配置 |
| 安全部署与恢复 | 生产安全 · 运维 · 排错 |
| 能力与集成 | 支持矩阵 · 架构 · 公开协议 |
| 维护与演进 | 生命周期与职责 · 执行接口交接 · 开发与发布 · 计划与进度 · 研究记录 |
手机或浏览器接入直接用 PocketShell 这类第三方终端连到机器上敲同样的命令,ccnm 不另做移动端(使用说明)。旧设计文档里的 home/work 是历史叫法,对应关系见架构说明。
MIT,见 LICENSE。