ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

openrig:用YAML和tmux统一管理Claude Code与Codex

openrig:用YAML和tmux统一管理Claude Code与Codex 1. 项目缘起为什么我要折腾 openrig 这个东西第一次看到 openrig 这个名字很多人会以为是某个硬件机架项目或者跟开源机械臂有关。实际上它解决的是一个非常具体、非常折磨人的问题当你同时使用 Claude Code、Codex 这类命令行 AI 编程工具时如何让它们在一个统一的终端环境里协同工作而不是互相打架。我自己是从去年开始重度使用 Claude Code 和 Codex 的。最开始只用一个工具的时候一切都好说装完就能跑。但当你同时维护三四个项目每个项目又可能用不同的模型后端问题就来了Claude Code 的配置在~/.claude下Codex 的配置在~/.codex下两边的 API key、模型选择、代理设置各管各的。更麻烦的是这两个工具都依赖终端会话而终端会话一多tmux 窗口就乱成一锅粥。openrig 的核心思路就是用YAML 配置文件 tmux 会话管理把这一堆工具串起来。你可以把它理解成一个AI 编程工具的工作台脚手架它不替代 Claude Code 或 Codex而是在它们之上做了一层编排。你写一份 YAML描述你要开几个窗口、每个窗口跑什么工具、用什么模型、注入什么环境变量然后 openrig 帮你把 tmux 会话拉起来所有工具各就各位。这篇文章适合三类人看第一类是本机已经装了 Claude Code 或 Codex但每次开新项目都要手动配半天的第二类是听说 tmux 很强但一直没找到使用场景的第三类是想把 AI 编程工具接入本地模型比如通过 LM Studio 或 DeepSeek但被各种配置项绕晕的。我会从设计思路讲到实操细节把踩过的坑都摊开说。2. 整体设计思路为什么是 YAML 加 tmux 这套组合2.1 为什么不用 shell 脚本一把梭最直接的做法其实是写个start.sh里面一堆tmux new-session和tmux send-keys。我一开始就是这么干的但很快就放弃了。原因有三个第一shell 脚本里的配置和逻辑混在一起改一个模型名字要翻半天。第二不同项目的配置没法复用A 项目用 DeepSeekB 项目用本地模型你得复制两份脚本。第三也是最要命的shell 脚本里处理字符串转义极其痛苦尤其是 API key 里带特殊字符的时候send-keys经常把命令拆错。YAML 的好处在于它把声明和执行分开了。你在 YAML 里只描述想要什么状态比如我要一个叫 dev 的会话里面两个窗口第一个跑 Claude Code 用 DeepSeek第二个跑 Codex 用本地模型。至于怎么创建会话、怎么切窗口、怎么注入环境变量那是 openrig 的事。这种声明式的好处是配置文件可以进 git可以按项目分目录可以继承和覆盖。2.2 tmux 在这里扮演什么角色很多人对 tmux 的印象还停留在终端复用器觉得它就是让你断开 SSH 后程序还能跑。但在 openrig 这套方案里tmux 的作用远不止于此。Claude Code 和 Codex 都是交互式 CLI 工具它们需要占据一个前台终端。如果你想让它们同时运行又想让它们各自有独立的输入输出tmux 的 window 和 pane 机制就是最自然的容器。每个 window 就是一个独立的伪终端Claude Code 在 window 0 里跑Codex 在 window 1 里跑互不干扰。而且 tmux 支持send-keys这意味着 openrig 可以在会话创建后自动往某个 window 里输入启动命令甚至自动回答工具启动时的交互式提问。提示tmux 的send-keys默认会立即发送如果目标程序还没准备好接收输入命令可能会丢失。openrig 内部通常会加一个短暂的 sleep 或者用tmux wait-for做同步这个细节后面会展开。2.3 配置分层全局默认加项目覆盖openrig 的配置我建议分两层来组织。第一层是全局默认放在~/.config/openrig/default.yaml里面写你常用的模型端点、API key 的环境变量名、tmux 的基础设置比如history-limit、mouse on。第二层是项目级配置放在项目根目录的.openrig.yaml只写这个项目特有的东西比如这个项目要用本地模型或者这个项目需要额外注入PROJECT_ROOT环境变量。这种分层的好处是换项目的时候不用改全局配置项目配置跟着代码走团队里其他人 clone 下来就能用。当然API key 这种敏感信息不要写进项目配置用环境变量引用YAML 里只写${DEEPSEEK_API_KEY}这样的占位符。3. 核心细节解析YAML 结构怎么设计才不坑自己3.1 会话、窗口、命令的三层结构openrig 的 YAML 核心就三层session、window、command。我拿一个实际在用的配置举例session: myproject windows: - name: claude command: claude env: ANTHROPIC_BASE_URL: ${DEEPSEEK_BASE_URL} ANTHROPIC_API_KEY: ${DEEPSEEK_API_KEY} - name: codex command: codex env: OPENAI_BASE_URL: ${LOCAL_MODEL_URL} OPENAI_API_KEY: dummy - name: shell command: bash这个配置的意思是创建一个叫myproject的 tmux 会话里面三个窗口。第一个窗口叫 claude启动 Claude Code并且把 Anthropic 的端点指向 DeepSeek 的兼容接口。第二个窗口叫 codex启动 Codex指向本地模型。第三个窗口就是个普通 shell用来跑测试或者 git 操作。这里有个关键点Claude Code 和 Codex 都支持通过环境变量覆盖默认的 API 端点。Claude Code 认ANTHROPIC_BASE_URL和ANTHROPIC_API_KEYCodex 认OPENAI_BASE_URL和OPENAI_API_KEY。你不需要改它们的配置文件只要在启动时注入环境变量就行。openrig 在创建 window 的时候会把这些 env 通过tmux set-environment或者直接在命令前拼接env VARvalue的方式传进去。3.2 环境变量注入的两种方式及取舍注入环境变量有两种做法各有优劣。第一种是在 YAML 里写env字典openrig 在send-keys的时候把命令拼成env KEYVAL command。这种方式的优点是简单直接缺点是如果 value 里有空格或特殊字符拼接容易出错。第二种是用tmux set-environment -t session KEY VAL先把变量设到 tmux 会话的环境里然后 window 里的 shell 自然能读到。这种方式更干净但要求 window 里跑的是 shell如果直接跑claude这种非 shell 命令环境变量传递链会断。我实测下来推荐混合使用对于不含特殊字符的简单变量比如 URL、模型名用第一种直接拼接对于 API key 这种可能含特殊字符的用第二种先设到 tmux 环境。openrig 如果做得细应该提供一个env_mode字段让用户选但很多轻量实现就是统一用第一种这时候你就要自己保证 value 里没有会破坏 shell 解析的字符。注意如果你的 API key 里包含$、!、这些字符直接拼接几乎必炸。最稳妥的办法是先把 key 写到一个.env文件里YAML 里只写source .env claude让 shell 自己去解析。3.3 启动命令的等待与同步问题这是最容易踩坑的地方。Claude Code 和 Codex 启动后有的版本会先弹一个交互式确认比如是否信任此目录有的会直接进入对话界面。如果你用send-keys发启动命令后立刻发第二条命令第二条很可能被第一条的启动过程吞掉。我的做法是在 YAML 里给每个 window 加一个可选的wait字段单位是秒默认 2 秒。openrig 在send-keys启动命令后sleep 这个时间再继续下一个 window 的操作。2 秒对于大多数情况够用但如果你的机器慢或者模型端点响应慢可能要调到 5 秒。更优雅的方案是用tmux wait-for做信号同步但这要求被启动的程序主动发信号Claude Code 和 Codex 显然不会配合。所以实际工程里固定 sleep 加可配置超时是最务实的做法。你也可以在启动命令后面加 tmux wait-for -S claude_ready然后 openrig 那边tmux wait-for claude_ready但这就把复杂度转嫁到用户身上了不适合作为默认行为。4. 实操过程从零把 openrig 跑起来4.1 前置环境准备在碰 openrig 之前你得先把基础工具装好。我按 Ubuntu 和 macOS 分别说Windows 用户建议走 WSL2原生 Windows 下 tmux 的支持一直不太行。Ubuntu 下sudo apt update sudo apt install -y tmux python3 python3-pip pip install pyyamlmacOS 下brew install tmux python3 pip3 install pyyaml然后装 Claude Code 和 Codex。Claude Code 目前主要通过 npm 分发npm install -g anthropic-ai/claude-codeCodex 的安装方式看版本有的走 npm有的提供独立二进制。装完后分别跑一次claude --version和codex --version确认能正常输出版本号。如果这一步就报错先别急着上 openrig把单个工具跑通再说。提示如果你在国内网络环境下遇到安装慢的问题可以配置 npm 的镜像源或者用--registry参数指定。这部分不属于 openrig 的范畴但确实是很多人卡住的第一关。4.2 写第一份 openrig 配置假设你的项目在~/work/demo进入目录后创建.openrig.yamlsession: demo windows: - name: claude command: claude wait: 3 env: ANTHROPIC_BASE_URL: https://api.deepseek.com/anthropic ANTHROPIC_API_KEY: ${DEEPSEEK_API_KEY} - name: codex command: codex wait: 3 env: OPENAI_BASE_URL: http://localhost:1234/v1 OPENAI_API_KEY: lm-studio - name: shell command: bash这里 Claude Code 指向 DeepSeek 的 Anthropic 兼容端点Codex 指向本地 LM Studio 的 OpenAI 兼容端点。DEEPSEEK_API_KEY从你的 shell 环境里读所以启动 openrig 之前要确保这个变量已经 export 了。4.3 启动与验证openrig 本身如果是一个 Python 脚本启动方式大概是python3 openrig.py --config .openrig.yaml执行后它应该做这几件事检查是否已存在同名 tmux 会话有就先 kill 掉创建新会话按顺序创建 window在每个 window 里注入环境变量并发送启动命令最后 attach 到会话上。验证是否成功看三点tmux ls能看到demo会话tmux list-windows -t demo能看到三个 window切到 claude 窗口Claude Code 的界面正常出现并且问它你用的什么模型它应该回答 DeepSeek 相关的信息。如果 Claude Code 报认证错误八成是ANTHROPIC_API_KEY没传进去。这时候在 tmux 里手动echo $ANTHROPIC_API_KEY看看是不是空的。如果是空的检查你的 shell 里有没有 export以及 openrig 的 env 注入逻辑是不是被 shell 的引号处理吃掉了。4.4 参数选择背后的计算逻辑有几个参数值得单独说。第一个是wait的值。我一般设 3 秒因为 Claude Code 启动时要加载配置、检查更新、建立连接实测冷启动平均 1.5 到 2.5 秒。设 3 秒留了余量。如果你的机器是机械硬盘或者网络特别慢设 5 秒。第二个是 tmux 的history-limit。默认是 2000 行对于 AI 编程工具来说太少了Claude Code 一次输出可能就几百行。我建议在全局 tmux 配置里设成 50000tmux set-option -g history-limit 50000这个值占的是内存50000 行大概几十 MB现代机器完全扛得住。但如果你开十几个会话每个都 50000 行内存占用就要留意了。第三个是窗口编号。tmux 默认从 0 开始编号但 0 在键盘上离手远我习惯在配置里加base-index 1让窗口从 1 开始。这个在 openrig 的 YAML 里可以通过tmux_options字段透传。5. 常见问题与排查技巧实录5.1 Claude Code 报组织禁用订阅访问这个报错信息通常是your organization has disabled claude subscription access for claude code。出现的原因是你用 Claude 账号登录但该账号所属的组织关闭了 Claude Code 的访问权限。解决办法有两个一是换用 API key 方式而不是订阅登录在环境变量里设ANTHROPIC_API_KEY二是如果你本来就想接第三方模型那这个报错其实无所谓因为你根本不走 Anthropic 的认证只要ANTHROPIC_BASE_URL指向正确的兼容端点就行。我遇到过一次明明设了 BASE_URL还是报这个错。排查后发现是 Claude Code 优先读了~/.claude/config.json里的登录态环境变量没覆盖成功。解决办法是先把~/.claude下的登录缓存清掉或者用claude --api-key显式指定。5.2 Codex 报 auth token is unavailableCodex 的认证体系和 Claude Code 不一样。它默认会找~/.codex/auth.json如果没有就走环境变量。报auth token is unavailable通常意味着它既没找到 auth 文件也没读到OPENAI_API_KEY。如果你接的是本地模型随便填一个非空的 key 就行比如lm-studio或者dummy因为本地端点根本不校验。但要注意有些版本的 Codex 会检查 key 的格式太短或者明显不像 key 的字符串会被拒绝。这时候你可以填一个看起来像 OpenAI key 的假值比如sk-开头加一串字符。5.3 tmux 里中文显示乱码这个问题在 Ubuntu 上特别常见。原因是 tmux 启动时的 locale 没设对。解决办法是在~/.tmux.conf里加set -g default-terminal screen-256color set -ga terminal-overrides ,*256col*:Tc然后在 shell 的 rc 文件里确保LANG和LC_ALL是en_US.UTF-8或zh_CN.UTF-8。如果还乱码检查你的终端模拟器本身是不是 UTF-8 编码。5.4 会话已存在导致启动失败openrig 第二次启动时如果同名会话还在tmux new-session会报错。标准做法是先tmux kill-session -t demo再创建。但如果你正在会话里工作kill 掉会丢失现场。我的建议是给 openrig 加一个--attach参数如果会话已存在直接 attach 上去而不是重建。这样日常使用就是第一次创建后续 attach不会误杀。5.5 常见问题速查表现象可能原因排查动作Claude Code 认证失败API key 未注入在 tmux 里echo $ANTHROPIC_API_KEYCodex 找不到 token环境变量名写错确认是OPENAI_API_KEY不是OPENAI_KEY启动命令没执行wait 太短把 wait 调到 5 秒重试中文乱码locale 未设置检查LANG和 tmux 的default-terminal会话创建失败同名会话存在tmux ls确认后 kill 或改用 attach 模式本地模型连不上端点地址错误用 curl 直接测端点是否响应6. 进阶玩法把 openrig 用出花来6.1 按项目类型切换模型后端我现在维护着三套配置模板。第一套是云端强模型Claude Code 接 DeepSeekCodex 接另一个云端端点适合需要高质量代码生成的场景。第二套是全本地两个工具都接 LM Studio适合处理敏感代码或者断网环境。第三套是混合Claude Code 走云端Codex 走本地适合日常开发。切换的方式很简单openrig 支持--profile参数不同 profile 对应不同的 YAML 片段。你也可以用环境变量OPENRIG_PROFILE来控制这样在 shell 里export OPENRIG_PROFILElocal就能全局切换。6.2 在 tmux 里做窗口布局预设除了按顺序创建 windowopenrig 还可以预设 pane 布局。比如你希望 claude 窗口占左边 70%右边 30% 放一个实时日志窗口。这需要在 YAML 里描述 layoutwindows: - name: claude command: claude panes: - command: claude size: 70% - command: tail -f /tmp/claude.log size: 30%实现上openrig 先创建 window再split-window然后用select-layout应用预设。这部分复杂度比单窗口高不少但一旦配好日常效率提升很明显。6.3 会话持久化与恢复tmux 本身支持 detach 和 attach但机器重启后会话就没了。如果你希望重启后能恢复 openrig 的工作台可以配合 tmux-resurrect 或 tmux-continuum 插件。不过要注意这些插件恢复的是 tmux 的窗口结构不会自动重新启动 Claude Code 和 Codex。所以更可靠的做法是写一个 systemd user service开机后自动跑 openrig 的启动脚本。我自己的做法是openrig 配置进 git机器重启后手动跑一次openrig up十秒钟工作台就回来了。自动恢复虽然听起来美好但 AI 工具的会话状态本来就难以完整恢复不如手动来得干净。6.4 与 VS Code 的配合很多人问能不能在 VS Code 里用 openrig。答案是能但方式和你想象的不一样。VS Code 的集成终端本身就是一个 shell你可以在里面跑openrig attach然后 tmux 的界面就出现在 VS Code 终端里。但 VS Code 终端对 tmux 的鼠标支持和快捷键支持不如原生终端完善我实测下来用 VS Code 终端跑 tmux 会有滚动和复制粘贴的小问题。更顺手的方案是VS Code 里正常写代码需要跟 AI 对话时切到原生终端里的 tmux 会话。两边通过文件系统同步互不干扰。如果你非要在 VS Code 里用建议关掉 tmux 的鼠标模式用键盘快捷键操作。7. 我踩过的几个印象深刻的坑第一个坑是环境变量里的$符号。我的一个 API key 里恰好有$直接拼进send-keys后shell 把它当变量展开key 就废了。排查了半小时才反应过来。从那以后所有含特殊字符的值我都走.env文件加source的方式。第二个坑是 tmux 的send-keys把命令拆成了多个参数。send-keys默认会把每个参数当成独立的按键序列如果你传的是一个带空格的完整命令字符串它可能只发了第一个词。正确做法是tmux send-keys -t session:window 完整命令 Enter把命令作为一个整体字符串传并且显式加Enter。第三个坑是 Claude Code 的更新检查。某些版本启动时会联网检查更新如果网络不通会卡在那里十几秒。这时候wait设 3 秒根本不够后续命令全乱套。解决办法是在环境变量里设CLAUDE_CODE_DISABLE_UPDATE_CHECK1跳过更新检查启动速度立刻回到 1 秒内。第四个坑是 Codex 的工作目录。Codex 默认在启动时的当前目录工作但 tmux 创建 window 时的默认目录可能不是你的项目目录。openrig 应该在创建 window 时显式cd到项目根目录或者在 YAML 里加cwd字段。我一开始没注意结果 Codex 在 home 目录里瞎找文件浪费了不少 token。8. 关于 openrig 后续可以怎么扩展如果你已经把基础版跑通了有几个方向值得继续折腾。一是加一个健康检查openrig 启动后自动往每个工具发一条测试消息确认模型端点真的通不通就报警。二是加日志聚合把 Claude Code 和 Codex 的输出同时 tee 到文件方便事后复盘。三是做配置校验YAML 写错的时候给出人话提示而不是 Python 的 traceback。我现在最想要的一个功能是会话快照把当前 tmux 里所有窗口的滚动缓冲导出成文本这样即使会话被 kill 了之前的对话记录还能找回来。tmux 的capture-pane命令能做到这一点但需要写点脚本把它串起来。等我把这个做稳定了再单独写一篇分享。这个方案的核心价值不在于技术有多复杂而在于它把一堆零散的工具和配置收拢到了一个可复现的入口。你换一台机器clone 代码装好依赖跑一条命令工作台就回来了。对于每天要在多个 AI 编程工具之间切换的人来说省下的那几分钟配置时间累积起来相当可观。
返回列表