1. 从 openrig 说起:一个把 Claude Code 和 Codex 装进 tmux 的编排器
第一次看到openrig这个名字,我下意识把它拆成了 "open" + "rig"——"rig" 在工程语境里就是"装配台、机架"的意思,把一堆零散的工具、模型、会话像搭机架一样组装起来。这个直觉基本没错。openrig 要解决的核心问题,是当下越来越多开发者同时使用 Claude Code、Codex 这类命令行 AI 编程助手时遇到的一个真实痛点:多个 AI 会话散落在不同终端窗口里,上下文互相隔离,切换成本高,长时间任务没人管,本地模型和云端模型混用时配置又各管各的。
如果你最近在折腾 Claude Code 安装、Codex 安装、Node.js 安装这些事,大概率已经踩过一圈坑了:Node.js 版本对不上、error installing 24.21.0: node.js v24.21.0 is not yet released这种报错、Codex 登录卡住、codex is ignoring 1 unrecognized configuration setting这类配置警告、VS Code 里接 Claude Code 又接不明白。openrig 的价值就在于,它把这些碎片化的操作收敛到一个统一的编排层里,底层用 tmux 做会话管理,用 Node.js 生态做胶水,把 Claude Code、Codex 以及本地模型(比如通过 LM Studio 暴露的接口)统一调度起来。
这篇文章适合三类人看:一是刚接触 Claude Code、Codex,还在纠结怎么装、怎么配的新手;二是已经在用但被多会话管理折磨的中级用户;三是想把本地模型和云端模型混着用、需要一套稳定编排方案的老手。我会从设计思路、核心细节、实操流程到问题排查,把 openrig 这套东西讲透,中间穿插我自己踩过的坑和实测有效的参数配置。全文不涉及任何网络访问工具,只聊本地编排和模型接入的正经用法。
2. 整体设计思路:为什么是 tmux + Node.js 这套组合
2.1 多 AI 会话管理的真实痛点拆解
先说清楚问题本身。Claude Code 和 Codex 都是典型的 CLI 形态 AI 编程助手,你在终端里敲命令,它读你的项目文件、执行命令、改代码。单个会话用起来很爽,但一旦你同时开三四个任务——比如一个会话在重构模块 A,一个在写模块 B 的测试,一个在跑长任务做代码审查——问题就来了。
传统做法是开多个终端标签页,每个标签页跑一个会话。这有几个硬伤:第一,终端标签页在 SSH 断开或电脑休眠后容易丢,长任务直接中断;第二,你没法在一个界面里同时看到多个会话的输出,得来回切;第三,会话之间的工作目录、环境变量、模型配置是割裂的,想统一管理几乎不可能。我试过用 VS Code 的多终端面板凑合,但 VS Code 配置 Claude Code 本身又是一堆事,而且它管不了 Codex 的会话生命周期。
openrig 的思路很直接:用 tmux 做会话底座,用 Node.js 做编排大脑。tmux 天生就是干这个的——它是终端复用器,会话可以 detach(脱离)后继续在后台跑,你随时 attach(接入)回来,SSH 断了也不影响。这正好解决了长任务中断的问题。而 Node.js 作为编排层,是因为 Claude Code 和 Codex 本身都跑在 Node.js 生态里,用同一套运行时做进程管理、配置注入、日志聚合,依赖最少,心智负担最低。
2.2 tmux 作为会话底座的取舍逻辑
为什么非得是 tmux,不能用 screen 或者自己写个进程守护?我实测下来,tmux 有三个不可替代的优势。
第一是窗口和面板的层级模型。tmux 有 session、window、pane 三层结构,一个 session 可以挂多个 window,一个 window 可以切多个 pane。openrig 可以给每个 AI 任务分配一个 pane,把同一项目的多个任务放在一个 window 里,不同项目放不同 session。这种组织方式比"一堆平铺的终端标签"清晰太多。
第二是可编程性。tmux 支持通过命令行发送按键、捕获面板输出、查询会话状态。这意味着 openrig 可以用 Node.js 脚本去tmux send-keys给某个会话发指令,用tmux capture-pane抓取输出做日志,用tmux list-sessions查询存活状态。这套接口稳定且文档齐全,比自己写 PTY 管理靠谱得多。
第三是脱离与恢复。这是 tmux 的看家本领。你tmux detach之后,所有会话在后台继续跑,第二天回来tmux attach一切照旧。对于动辄跑几十分钟的代码生成任务,这个特性是刚需。
提示:tmux 默认的 scrollback(回滚缓冲)只有 2000 行,跑长任务时日志很容易被冲掉。建议在
~/.tmux.conf里设set -g history-limit 50000,否则排查问题时你会后悔。
2.3 Node.js 编排层的职责边界
Node.js 在 openrig 里不是可有可无的胶水,它承担了四件核心事。
一是进程生命周期管理。它负责启动 tmux 会话、在会话里拉起 Claude Code 或 Codex 进程、监控进程是否存活、异常退出时决定是否重启。这里要注意,Node.js 本身不直接 fork AI 进程,而是通过 tmux 间接管理,这样 AI 进程的父进程是 tmux server,Node.js 脚本崩了也不影响正在跑的会话。
二是配置注入。Claude Code 和 Codex 各有各的配置文件和环境变量。openrig 在拉起会话前,会把对应的 API 端点、模型名、工作目录等参数写进环境变量或临时配置文件,避免手动改配置。比如你想让某个会话走本地 LM Studio 的模型,另一个走云端,openrig 可以在启动时分别注入不同的base_url。
三是日志聚合。通过tmux capture-pane定期抓取各面板输出,汇总到一个统一的日志文件或输出流里,方便你一眼看到所有会话在干什么。
四是任务编排。比如"等会话 A 跑完再启动会话 B"这种依赖关系,或者"每 10 分钟检查一次长任务是否卡死"这种巡检逻辑,都放在 Node.js 层实现。
2.4 与 Claude Code、Codex 的协作模式
openrig 和这两个工具不是替代关系,是编排关系。Claude Code 和 Codex 各自还是独立进程,openrig 只是决定它们在哪跑、用什么配置跑、怎么被监控。
这里有个关键设计点:openrig 不碰 AI 的对话逻辑。它不解析 Claude Code 的输出语义,不替你做决策,只做进程和会话层面的管理。这个边界划清楚很重要,因为一旦编排层试图理解 AI 输出,复杂度会爆炸,而且 AI 输出格式一变就全崩。保持"只管进程、不管内容"的克制,是这套方案能稳定的根本原因。
3. 核心细节解析:环境、配置与关键参数
3.1 Node.js 版本选择与安装避坑
openrig 跑在 Node.js 上,所以 Node.js 装不对,后面全白搭。热搜里那个error installing 24.21.0: node.js v24.21.0 is not yet released or is not available就是典型的版本号写错——你填了一个根本不存在的版本。Node.js 的版本号是严格递增的,偶数大版本是 LTS(长期支持),奇数大版本是 Current(尝鲜)。生产环境一律选 LTS。
截至我写这篇时的稳定 LTS 是 20.x 和 22.x 系列。我的建议是用 22.x LTS,因为 Claude Code 和 Codex 对 Node.js 版本有最低要求,太老的版本(比如 16.x)会报各种语法或 API 不兼容。安装方式上,别去官网下那个.pkg或.msi安装包手动装,版本管理会很痛苦。用版本管理器:
# macOS / Linux 用 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # 重开终端后 nvm install 22 nvm use 22 nvm alias default 22 # Windows 用 nvm-windows,或者直接 fnm winget install Schniz.fnm fnm install 22 fnm use 22装完验证:
node -v # 应输出 v22.x.x npm -v # 应输出 10.x 以上注意:如果你之前用安装包装过 Node.js,先卸载干净再上版本管理器,否则 PATH 里会有两个 node,
which node指向哪个全看运气,这种问题排查起来极其恶心。
3.2 tmux 安装与基础配置
tmux 在主流系统上都能直接装:
# Ubuntu / Debian sudo apt update && sudo apt install -y tmux # macOS brew install tmux # Windows 建议在 WSL2 里跑,原生 Windows 的 tmux 体验很差装完别急着用,先写一份基础配置。我常用的~/.tmux.conf核心几行:
# 提高回滚缓冲,长任务必备 set -g history-limit 50000 # 用 Ctrl+a 做前缀键,比默认的 Ctrl+b 顺手 set -g prefix C-a unbind C-b bind C-a send-prefix # 开启鼠标支持,方便点选面板 set -g mouse on # 窗口从 1 开始编号,符合直觉 set -g base-index 1 setw -g pane-base-index 1 # 减少 ESC 延迟,vim 用户会感谢我 set -sg escape-time 10改完tmux source-file ~/.tmux.conf生效。这几行看着简单,但history-limit和escape-time这两个是实打实影响体验的,前者决定你能不能翻到早先的日志,后者决定你在 AI 会话里按 ESC 时会不会有半秒卡顿。
3.3 Claude Code 与 Codex 的安装要点
Claude Code 和 Codex 的安装方式类似,都是 npm 全局包。但这里有个坑:全局安装的包用的是哪个 Node.js 版本,取决于你当前 shell 的 PATH。如果你用 nvm 切了版本,但 npm 全局目录还指向旧版本,装完可能跑不起来。
# 确认 npm 全局目录 npm config get prefix # 安装 Claude Code(以官方包名为准,具体以官方文档为准) npm install -g @anthropic-ai/claude-code # 安装 Codex(同样以官方发布为准) npm install -g @openai/codex装完claude --version和codex --version验证。如果报command not found,八成是 npm 全局 bin 目录不在 PATH 里,把它加进去:
# 假设 prefix 是 /home/user/.nvm/versions/node/v22.x.x export PATH="$PATH:$(npm config get prefix)/bin"提示:热搜里那个
your organization has disabled claude subscription access for claude code是账号权限问题,不是安装问题。这种情况要么换账号,要么用 API key 方式接入,跟 openrig 的编排逻辑无关,但会直接影响会话能不能起来,排查时要先分清是"装不上"还是"登不上"。
3.4 本地模型接入:LM Studio 与自定义端点
openrig 一个很实用的场景是把 Claude Code 或 Codex 指向本地模型。比如你在 LM Studio 里加载了一个模型,它会在本地起一个兼容 OpenAI 格式的接口,默认地址通常是http://localhost:1234/v1。你要做的是让 AI 工具走这个端点。
以环境变量方式注入(具体变量名以各工具官方文档为准,这里给的是通用思路):
export OPENAI_BASE_URL="http://localhost:1234/v1" export OPENAI_API_KEY="lm-studio" # 本地模型通常不校验,随便填然后在 openrig 启动会话时,把这些变量传进 tmux 的环境里。这里的关键是tmux 会话的环境变量是启动时快照的,你在外面 export 完再 attach 进去,里面可能还是旧值。正确做法是用tmux new-session -e KEY=VALUE显式传入,或者先设好再启动会话。
4. 实操过程:从零搭起一套 openrig 编排
4.1 目录结构与初始化脚本
我习惯给 openrig 一个独立的工作目录,结构大概这样:
~/openrig/ ├── config/ │ ├── sessions.json # 会话定义 │ └── models.json # 模型端点配置 ├── scripts/ │ ├── launch.js # 启动编排 │ ├── monitor.js # 状态巡检 │ └── capture.js # 日志抓取 └── logs/ └── (各会话日志)config/sessions.json定义你要跑哪些会话:
{ "sessions": [ { "name": "refactor-core", "tool": "claude", "cwd": "/home/user/project-a", "model": "local-qwen", "window": "project-a" }, { "name": "test-gen", "tool": "codex", "cwd": "/home/user/project-a", "model": "cloud-default", "window": "project-a" } ] }config/models.json定义模型端点:
{ "local-qwen": { "baseUrl": "http://localhost:1234/v1", "apiKey": "lm-studio" }, "cloud-default": { "baseUrl": "https://api.example.com/v1", "apiKeyEnv": "CLOUD_API_KEY" } }这种分离设计的好处是,换模型只改models.json,加任务只改sessions.json,编排逻辑不用动。
4.2 用 Node.js 拉起 tmux 会话
核心启动脚本launch.js的逻辑:
const { execSync } = require('child_process'); const fs = require('fs'); const sessions = JSON.parse(fs.readFileSync('./config/sessions.json')).sessions; const models = JSON.parse(fs.readFileSync('./config/models.json')); for (const s of sessions) { const model = models[s.model]; const envArgs = [ `-e OPENAI_BASE_URL=${model.baseUrl}`, `-e OPENAI_API_KEY=${model.apiKey || process.env[model.apiKeyEnv]}` ].join(' '); // 创建或复用 window const cmd = `tmux new-window -t openrig -n ${s.window} -c ${s.cwd} ${envArgs} "${s.tool}"`; try { execSync(cmd, { stdio: 'inherit' }); console.log(`[ok] 启动会话 ${s.name}`); } catch (e) { console.error(`[fail] ${s.name}: ${e.message}`); } }这段代码有几个细节值得说。第一,-c ${s.cwd}让 tmux 在新窗口里直接切到项目目录,省得 AI 工具在错误目录里瞎找文件。第二,-e传环境变量,确保模型配置在会话内部生效。第三,用new-window而不是new-session,是因为我倾向于把所有任务挂在同一个名为openrig的 session 下,用 window 区分项目,管理起来更集中。
第一次跑之前要先建好这个 session:
tmux new-session -d -s openrig -n control node scripts/launch.js4.3 会话状态巡检与日志抓取
monitor.js负责定期检查每个会话是否还活着、有没有卡死:
const { execSync } = require('child_process'); function listWindows() { const out = execSync('tmux list-windows -t openrig -F "#{window_name}"').toString(); return out.trim().split('\n'); } function capturePane(window) { return execSync(`tmux capture-pane -t openrig:${window} -p -S -200`).toString(); } setInterval(() => { const windows = listWindows(); for (const w of windows) { const content = capturePane(w); const lastLines = content.trim().split('\n').slice(-5).join('\n'); console.log(`--- ${w} ---\n${lastLines}\n`); } }, 30000);capture-pane -S -200表示从回滚缓冲往上抓 200 行,这样即使输出刷得快,也能抓到最近的上下文。巡检间隔我设 30 秒,太频繁会拖慢 tmux,太稀疏又发现不了卡死。
注意:
capture-pane抓的是"当前可见内容 + 回滚缓冲",如果某个会话输出量巨大,缓冲被冲掉,你抓到的可能不完整。要完整日志,得在启动 AI 进程时用tee把输出同时写文件,比如claude 2>&1 | tee ~/openrig/logs/refactor-core.log。
4.4 多模型切换与配置注入实战
实际用的时候,最常见的需求是"同一个项目,一个会话用本地模型省钱,一个用云端模型保质量"。openrig 的做法是在sessions.json里给不同会话指定不同 model,启动时分别注入。
但这里有个坑:Claude Code 和 Codex 读取配置的优先级不一样。有的工具环境变量优先级高于配置文件,有的反过来。我实测下来,最稳的做法是"环境变量 + 显式命令行参数"双保险。比如 Codex 支持在启动时指定配置,那就别只靠环境变量。
另外,热搜里提到的cc switch这类工具,本质也是做模型端点切换,思路和 openrig 的models.json一致。区别在于 openrig 把切换和会话管理绑在一起,切模型的同时就决定了这个会话跑在哪个 tmux 窗口里,粒度更细。
5. 常见问题与排查技巧实录
5.1 安装类问题速查
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
node.js v24.21.0 is not yet released | 版本号不存在 | 改用nvm install --lts |
command not found: claude | npm 全局 bin 不在 PATH | 把npm config get prefix/bin 加进 PATH |
codex is ignoring 1 unrecognized configuration setting | 配置文件里有拼写错误或过时字段 | 逐行核对配置,删掉不认识的键 |
codex 无法加载组织设置 | 账号权限或网络问题 | 检查账号状态,确认端点可达 |
| 装完跑起来报模块找不到 | Node.js 版本与包不兼容 | 升到 22.x LTS 重装 |
5.2 会话类问题排查思路
会话起不来,先分三层查:tmux 层、Node.js 层、AI 工具层。
tmux 层:tmux list-sessions看 session 在不在,tmux list-windows -t openrig看 window 建没建。如果 session 都没了,说明 tmux server 挂了,可能是系统重启或 OOM。
Node.js 层:看launch.js的报错输出。常见的是execSync抛异常,多半是命令拼错或路径不存在。把execSync的命令单独在终端跑一遍,能快速定位。
AI 工具层:attach 进那个窗口,看 AI 工具自己的报错。这一步最容易被忽略——很多人以为会话没起来是 openrig 的问题,其实是 Claude Code 或 Codex 自己登录失败或配置错误。
5.3 我踩过的三个真实坑
坑一:tmux 环境变量不刷新。我改了models.json里的端点,重启 openrig 后发现会话还是走旧端点。原因是 tmux server 已经存在,新 window 继承了 server 启动时的环境。解决办法是tmux kill-server彻底重启,或者用-e显式传变量(前面脚本里就是这么做的)。
坑二:capture-pane 抓到的是乱码。某些 AI 工具输出带 ANSI 颜色码,直接抓下来一堆\x1b[32m之类的东西。加-e参数保留转义,或者抓完用正则清洗掉。
坑三:长任务把 tmux 拖死。有个会话输出量极大,几秒钟刷几千行,tmux 渲染跟不上,整个 server 卡顿。后来给那个会话单独设了更小的history-limit,并且用tee把输出重定向到文件,面板里只留摘要。
5.4 性能与稳定性调优建议
跑久了会发现,openrig 的瓶颈通常不在 Node.js,而在 tmux 的渲染和回滚缓冲。几个调优点:一是按会话重要性分配history-limit,控制类会话给 5000 就够,长任务会话给 50000;二是巡检脚本别太频繁,30 秒一次足够;三是日志落盘用追加模式,别每次覆盖,否则出问题想回溯都没数据。
另外,如果你在 WSL2 里跑,注意 WSL 的内存回收机制,长时间跑大量会话可能吃满内存。可以在~/.wslconfig里限制内存上限,避免拖垮宿主机。
6. 关于 openrig 这套编排思路的延伸
openrig 这个名字背后其实是一种很朴素的工程哲学:把复杂的东西拆成可管理的单元,再用一层薄薄的编排把它们串起来。tmux 管会话,Node.js 管逻辑,AI 工具管干活,各司其职,谁也不越界。这套思路不只适用于 Claude Code 和 Codex,任何 CLI 形态的 AI 工具都能这么编排。
我自己用下来最大的体会是,编排层的价值不在于功能多,而在于边界清。openrig 不试图理解 AI 在说什么,不试图替你做决策,它只保证"会话活着、配置对、日志在"。正因为克制,它才稳。反过来,那些试图把 AI 输出解析、任务调度、结果判断全包进来的方案,往往用不了多久就维护不动了。
如果你刚开始折腾,我的建议是先别急着上编排,把 Claude Code 或 Codex 单个跑通,把 Node.js 版本、tmux 配置、模型端点这些基础打牢,再考虑用 openrig 把多会话管起来。基础不牢的时候上编排,出了问题你根本分不清是哪一层的锅。等你单会话用顺了,自然会感受到多会话管理的痛,那时候 openrig 这套东西的价值就体现出来了。