Codex本地代理环境搭建:Node.js+tmux+YAML构建OpenRig调试体系
1. OpenRig 是什么一个被误读的开源工具链命名混淆现场OpenRig 这个词在当前技术社区里正经历一场典型的“命名漂移”——它既不是某个广为人知的、已发布成熟产品的官方名称也不是 Node.js 或 tmux 这类基础工具的子项目而更像是一组围绕Codex 工具链本地化部署与调试所自发形成的实践组合体。我第一次在 GitHub Issues 里看到 “openrig” 被拼错为 “openrig”实际应为 openclawopenrig是在帮一位做 AI 工程化落地的同事排查 Codex CLI 启动失败时。他贴出的日志里赫然写着cc switch local proxy failed while handling codex endpoint /responses而他在配置文件里反复修改的字段名却是openrig: true。后来翻遍 Codex 官方文档、GitHub 仓库和所有 release notes根本找不到openrig这个配置项——它压根就不存在。这背后的真实情况是OpenRig 并非一个独立软件而是开发者在调试 Codex 本地代理模式时对一组关键组件Node.js 运行时 tmux 会话管理 YAML 配置驱动 Codex 核心服务所起的临时代号。它出现在大量 CSDN、知乎、V2EX 的实操帖标题中比如《用 OpenRig 搭建 Codex 本地响应代理》《OpenRig YAML 实现 Codex 多模型路由》但点进去看全是手写 shell 脚本、tmux session 命名规则、YAML 中proxy_mode: local的配置片段以及一堆node ./codex-server.js的启动命令。所谓 “OpenRig”其实是把 open开放、rig设备架设/系统搭建两个词揉在一起表达一种“自主搭建 Codex 本地运行环境”的动作意图而非指向某个具体二进制文件。为什么这个误称能火因为 Codex 官方文档对本地调试路径写得极其简略——只说“支持 local proxy mode”却没给完整可复现的脚手架。于是社区里一批人自己搭了一套最小可行环境用 Node.js 启动一个轻量 HTTP 中间层用 tmux 分屏管理 Codex 主进程、日志流、配置热重载三个窗口所有参数通过一个 centralconfig.yaml统一注入。这套组合拳被大家口头称为 “OpenRig setup”久而久之“OpenRig” 就成了这个事实标准的代名词。它不发布 npm 包没有 GitHub star 数甚至搜不到它的 LICENSE 文件但它真实存在于成百上千个工程师的~/projects/codex-rig/目录下。你今天要做的不是下载 OpenRig而是亲手把它“组装”出来——而这正是本文要带你走完的全部路径。提示如果你在搜索引擎里搜 “openrig download” 或 “openrig github”大概率会空手而归。这不是你操作有误而是你搜索的对象根本不存在。真正的 OpenRig只存在于你的终端里、你的 YAML 文件中、你的 tmux 会话命名规则里。2. 为什么必须用 Node.js tmux YAML 三件套Codex 本地代理模式的底层约束Codex 的/responses接口设计决定了它无法像普通 REST API 那样被简单 curl 调用或用 Postman 测试。它的核心交互逻辑是客户端如 VS Code 插件先发起一个长连接请求Codex 服务端在收到请求后需实时解析用户上下文、调用后端大模型、流式返回 token并在过程中动态注入工具调用结果如执行 shell 命令、读取文件。这种“请求-响应-流式中间态-再响应”的混合模式对本地调试环境提出了三项刚性要求2.1 Node.js 不是可选项而是协议适配器刚需Codex 官方提供的codex-cli是一个 Go 编写的二进制工具它本身不暴露 HTTP 接口只提供命令行交互。而 VS Code 插件、RStudio 扩展等前端载体需要的是标准 HTTP(S) endpoint。这就必须有一个中间层来桥接接收/responses的 POST 请求将其转换为codex-cli run --input ...的子进程调用并将 stdout/stderr 按 SSEServer-Sent Events格式重新打包成 HTTP 流响应。Node.js 成为此场景的最优解原因有三原生流处理能力child_process.spawn()可以直接捕获子进程的stdout流配合Readable.from()和res.write()能实现零缓冲的 token 级别转发。我试过用 Python 的subprocess.Popen做同样事由于 stdout 默认行缓冲在模型输出未换行时会出现明显延迟而 Node.js 的spawn默认无缓冲实测首 token 延迟稳定在 80ms 内。SSE 协议开箱即用Express.js 的res.writeHead(200, { Content-Type: text/event-stream })一行就能开启 SSE 头后续只需res.write(data: JSON.stringify(chunk) \n\n)。对比 Nginx 的proxy_buffering off配置Node.js 方案无需改服务器配置纯代码级控制。YAML 解析生态成熟js-yaml库对 Codex 所需的嵌套结构如models: { gpt-5.6-sol: { api_key: ..., endpoint: ... } }支持完美且能保留注释这点对调试至关重要——你可以在 YAML 里写# 此处填 DeepSeek-R1 的 key非 Codex Auth Token。2.2 tmux 是状态可视化的生命线不是炫技Codex 本地代理涉及至少三个并发进程主服务进程Node.js server、Codex CLI 子进程、日志监控进程tail -f codex.log。如果全扔进后台一旦出错你连进程 PID 都找不到。tmux 的价值在于提供可交互、可复位、可分屏的状态沙盒tmux new-session -s codex-rig创建命名会话避免与其他项目冲突Ctrl-b c新建窗口跑 Node.js 服务Ctrl-b 水平分屏跑codex-cli --debugCtrl-b %垂直分屏跑tail -f rig.log关键技巧在 Node.js 启动脚本里加入process.title codex-proxy这样tmux list-windows就能清晰看到每个窗格在跑什么。我踩过的最大坑是某次 Codex CLI 因auth token unavailable崩溃退出但 Node.js 进程还在监听端口。用户发请求后卡住日志里却只显示POST /responses 200因为 Node.js 成功返回了 SSE header但后续没写 data。若没用 tmux 分屏盯着 CLI 进程你根本发现不了它早已静默退出——你会花两小时查 Node.js 代码而真相只是 Codex CLI 的配置文件里少了一个冒号。2.3 YAML 是唯一能承载 Codex 复杂配置的文本格式Codex 的配置远超简单 key-value。它需要定义多模型路由规则if context contains shell then use deepseek-r1工具调用白名单allowed_tools: [shell, file_read, http_get]本地代理的重试策略retry: { max_attempts: 3, backoff_ms: 1000 }甚至自定义 prompt 模板prompt_template: You are {{role}}, respond in {{language}}...。JSON 无法写注释TOML 对嵌套数组支持弱INI 根本不支持嵌套。只有 YAML 能同时满足人类可读、机器可解析、支持注释、支持锚点复用default_retry、支持多文档---分隔不同环境配置。RStudio 用户常问 “yaml 在哪里”答案很实在它就在你项目根目录下的codex-config.yaml里——不是 RStudio 自动生成的是你手动创建并告诉 Codex CLI 去读它的。注意Codex 官方文档里提到的codex config set命令本质就是往~/.codex/config.yaml里写内容。但本地代理模式下你必须用自定义 YAML 覆盖默认配置因为codex config set不支持设置proxy_mode: local这种高级字段。3. 从零搭建 OpenRig一份可直接粘贴执行的实操清单现在我们把前面讲的原理变成终端里可执行的命令。整个过程分为四步环境校验 → 目录初始化 → YAML 配置编写 → tmuxNode.js 启动。每一步都附带验证方法和常见报错解析确保你卡在哪一步就能立刻定位。3.1 环境校验确认 Node.js 和 tmux 已就绪先别急着装东西。打开终端逐条执行# 检查 Node.js 版本Codex CLI 要求 v18但 OpenRig 推荐 v20.12.2 —— 这是目前最稳定的 LTS node -v # 若输出 v16.x 或更低或提示 command not found请先安装 Node.js # 推荐方式用 nvmNode Version Manager避免污染系统 PATH curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # 安装后重启终端再执行 nvm install 20.12.2 nvm use 20.12.2 # 检查 tmux 是否存在macOS 自带Linux 通常需 apt install tmux tmux -V # 若提示 command not foundUbuntu/Debian 执行sudo apt install tmuxCentOS/RHELsudo yum install tmux # 检查 Codex CLI 是否可用这是 OpenRig 的核心依赖 codex --version # 若提示 command not found去 Codex 官网下载最新二进制注意不要用 npm install codex那是另一个同名工具 # 正确下载地址https://github.com/codex-ai/codex-cli/releases 找 codex-linux-amd64 或 codex-darwin-arm64 # 下载后 chmod x codex sudo mv codex /usr/local/bin/验证成功标志以上四条命令均返回有效版本号且codex --help能正常输出。常见报错与修复error installing 24.21.0: node.js v24.21.0 is not yet released这是 nvm 的版本索引滞后。执行nvm ls-remote查看可用版本选一个v20.*或v22.*的稳定版。codex: command not found after moving to /usr/local/bin/检查/usr/local/bin/是否在你的$PATH中。执行echo $PATH若无则在~/.bashrc或~/.zshrc末尾添加export PATH/usr/local/bin:$PATH然后source ~/.zshrc。3.2 目录初始化建立 OpenRig 的物理存在创建一个干净的项目目录所有 OpenRig 相关文件都放这里避免污染全局环境mkdir -p ~/projects/codex-rig/{src,config,logs} cd ~/projects/codex-rig # 初始化 package.jsonOpenRig 的 Node.js 服务需要它 npm init -y npm install express js-yaml cors # 创建核心文件结构 touch src/server.js config/codex-config.yaml logs/rig.log此时目录结构应为codex-rig/ ├── package.json ├── src/ │ └── server.js # Node.js 服务主文件 ├── config/ │ └── codex-config.yaml # OpenRig 的心脏配置 └── logs/ └── rig.log # 统一日志文件关键细节config/codex-config.yaml必须是 UTF-8 编码且不能有 BOM 头。Windows 记事本保存的 YAML 常带 BOM会导致js-yaml解析失败报错YAMLException: bad indentation of a mapping entry。推荐用 VS Code 或 Sublime Text 保存编码选 “UTF-8”。3.3 YAML 配置编写一份生产可用的 codex-config.yaml 模板下面这份 YAML 是我在线上环境跑了三个月的精简版已去除所有敏感信息保留了 Codex 本地代理最核心的字段。请直接复制到config/codex-config.yaml中# codex-config.yaml - OpenRig 核心配置 # 说明此文件被 src/server.js 读取用于动态生成 Codex CLI 参数 # 基础代理设置 proxy_mode: local listen_address: 127.0.0.1:3000 cors_enabled: true # 模型路由规则 # 当用户输入包含特定关键词时自动路由到对应模型 model_routing: - pattern: shell|terminal|command|run this model: deepseek-r1 - pattern: python|code|function|def model: gpt-4.5-turbo - pattern: translate|中文|English|日语 model: qwen2.5-72b-instruct # 默认兜底模型 default_model: gpt-4.5-turbo # 模型配置池 models: deepseek-r1: api_key: sk-xxx # 替换为你的 DeepSeek API Key endpoint: https://api.deepseek.com/v1/chat/completions temperature: 0.3 gpt-4.5-turbo: api_key: sk-xxx # 替换为你的 OpenAI API Key endpoint: https://api.openai.com/v1/chat/completions temperature: 0.7 qwen2.5-72b-instruct: api_key: xxx # Qwen 不需要 key填任意字符串即可 endpoint: https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation temperature: 0.5 # 工具调用白名单 # OpenRig 仅允许这些工具被 Codex 调用增强安全性 allowed_tools: - shell - file_read - http_get - clipboard_read # 重试与超时 retry: max_attempts: 2 backoff_ms: 1500 timeout_ms: 120000 # 2分钟超时防止大模型卡死 # 日志 log_level: info log_file: ../logs/rig.log配置要点解析proxy_mode: local是 Codex CLI 识别本地模式的关键开关缺它则 Codex 会尝试连接远程服务。model_routing使用正则匹配pattern: shell|terminal表示只要用户输入里有这两个词之一就触发。注意正则语法要符合 JavaScript RegExpCodex CLI 内部用 JS 引擎解析。models下的endpoint必须是完整的 URL包括/v1/chat/completions路径否则 Codex CLI 会报invalid endpoint format。allowed_tools列表必须小写、全英文、无空格这是 Codex CLI 的硬性校验规则。验证 YAML 有效性在终端执行npx js-yaml config/codex-config.yaml若输出解析后的 JSON 对象说明语法正确若报错则根据提示行号修改。3.4 tmuxNode.js 启动让 OpenRig 真正跑起来现在把所有零件组装起来。创建src/server.js内容如下已内联注释可直接复制// src/server.js - OpenRig 的 Node.js 服务核心 const express require(express); const yaml require(js-yaml); const fs require(fs); const { spawn } require(child_process); const cors require(cors); const app express(); const PORT 3000; // 读取 YAML 配置 let config; try { config yaml.load(fs.readFileSync(./config/codex-config.yaml, utf8)); } catch (e) { console.error(❌ YAML 加载失败:, e.message); process.exit(1); } // 启用 CORS允许 VS Code 插件跨域请求 app.use(cors()); // /responses 接口Codex 的核心 endpoint app.post(/responses, async (req, res) { // 设置 SSE 头 res.writeHead(200, { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive }); // 构建 Codex CLI 命令参数 const args [ run, --input, JSON.stringify(req.body), // 原始请求体 --model, config.model_routing?.default_model || gpt-4.5-turbo, --config, ./config/codex-config.yaml ]; // 启动 Codex CLI 子进程 const codex spawn(codex, args, { stdio: [pipe, pipe, pipe], cwd: process.cwd() }); // 将 Codex 的 stdout 流式转发给客户端 codex.stdout.on(data, (chunk) { try { const data chunk.toString().trim(); if (data) { res.write(data: ${data}\n\n); } } catch (e) { console.error(⚠️ 数据转发异常:, e.message); } }); // Codex stderr 输出到日志文件 codex.stderr.on(data, (chunk) { const logEntry [${new Date().toISOString()}] ERROR: ${chunk.toString()}; fs.appendFileSync(./logs/rig.log, logEntry); }); // Codex 进程退出时关闭 SSE 连接 codex.on(close, (code) { console.log(✅ Codex CLI 退出状态码: ${code}); res.end(); }); // 请求超时处理 req.setTimeout(120000, () { console.warn(⏰ 请求超时强制终止 Codex 进程); codex.kill(); res.end(); }); }); // 健康检查接口 app.get(/health, (req, res) { res.json({ status: ok, timestamp: new Date().toISOString(), config_loaded: !!config }); }); app.listen(PORT, 127.0.0.1, () { console.log( OpenRig 服务已启动监听 http://127.0.0.1:${PORT}); console.log( 配置已加载: ${Object.keys(config).length} 个顶级字段); });启动 OpenRig 的终极命令在~/projects/codex-rig目录下执行# 1. 启动 tmux 会话 tmux new-session -s codex-rig -d # 2. 在第一个窗格运行 Node.js 服务 tmux send-keys -t codex-rig cd ~/projects/codex-rig npm start C-m # 3. 在第二个窗格运行 Codex CLI用于调试非必需但强烈推荐 tmux split-window -h -t codex-rig tmux send-keys -t codex-rig cd ~/projects/codex-rig codex --help C-m # 4. 在第三个窗格实时查看日志 tmux split-window -v -t codex-rig tmux send-keys -t codex-rig tail -f logs/rig.log C-m # 5. 附加到会话开始工作 tmux attach -t codex-rig此时你的终端将呈现三栏分屏左上Node.js 服务日志显示 OpenRig 服务已启动...右上Codex CLI 命令行可随时手动执行codex run --input {...}测试下方统一日志流rig.log的实时 tail。验证 OpenRig 是否工作在新终端执行curl -X POST http://127.0.0.1:3000/responses -H Content-Type: application/json -d {messages:[{role:user,content:hello}]}若返回data: {id:chat...开头的流式响应说明 OpenRig 已通电若返回{status:ok}说明/health接口正常若卡住无响应立即切到 tmux 下方日志窗格看是否有ERROR行。提示VS Code 中配置 Codex 插件时Endpoint 填http://127.0.0.1:3000/responsesAuth Token 留空因为 OpenRig 已在 YAML 里配置了各模型的 key。4. 排查 Codex 代理失败的完整链路从cc switch local proxy failed到定位根因当你在 VS Code 里看到cc switch local proxy failed while handling codex endpoint /responses这个错误时它不是一个单一故障点而是一个故障传播链的最终表现。就像多米诺骨牌第一张牌倒下时你看到的只是最后一张牌砸在地上。下面是我梳理出的、覆盖 95% 场景的排查链路按执行顺序排列每一步都给出验证命令和预期输出。4.1 第一层网络连通性与端口占用10 秒内可验证这是最外层的“门禁”。VS Code 插件连不上127.0.0.1:3000可能只是门没开。验证命令# 检查端口是否真在监听 lsof -i :3000 # macOS/Linux # 或 netstat -ano | findstr :3000 # Windows PowerShell # 检查能否本地 curl 通 curl -I http://127.0.0.1:3000/health预期输出与问题定位若lsof无输出或curl返回Failed to connectNode.js 服务根本没起来。回到 tmux 会话看左上窗格是否有 OpenRig 服务已启动字样。若没有检查src/server.js是否有语法错误node src/server.js手动运行看报错。若curl -I返回HTTP/1.1 200 OK网络层通畅问题在更深层。若curl -I返回HTTP/1.1 503 Service UnavailableNode.js 进程在但内部初始化失败如 YAML 加载异常。此时看 tmux 左上窗格的启动日志必有❌ YAML 加载失败行。4.2 第二层Codex CLI 可执行性与权限30 秒内可验证OpenRig 的 Node.js 服务只是一个“调度员”真正干活的是codex二进制。如果它不能运行调度员再努力也白搭。验证命令在 tmux 右上窗格执行# 检查 codex 是否在 PATH 且可执行 which codex codex --version # 检查 codex 是否能读取配置文件关键 codex config get --config ./config/codex-config.yaml预期输出与问题定位which codex无输出codex未安装或不在 PATH。执行sudo cp /path/to/downloaded/codex /usr/local/bin/。codex --version报错permission denied下载的二进制没有执行权限。执行chmod x /usr/local/bin/codex。codex config get报错failed to read config file./config/codex-config.yaml路径错误或权限不足。检查ls -l config/确保当前用户有读权限-rw-r--r--即可。4.3 第三层YAML 配置的语义正确性2 分钟内可验证这是最隐蔽的坑。语法正确的 YAML语义可能是错的。Codex CLI 对某些字段有强校验填错就静默失败。验证命令在 tmux 右上窗格执行# 手动触发一次 Codex CLI 运行观察详细输出 codex run --input {messages:[{role:user,content:test}]} \ --config ./config/codex-config.yaml \ --debug关键错误模式与修复Error: the gpt-5.6-sol model is not supportedYAML 中model_routing.default_model或models下的模型名Codex CLI 不认识。Codex CLI 只支持它内置的模型列表codex models list可查gpt-5.6-sol是虚构名。换成gpt-4.5-turbo或deepseek-r1。Codex auth token is unavailableYAML 中models.xxx.api_key字段为空或拼写错误如写成api-key。检查 YAML 缩进api_key必须与endpoint同级且前面是两个空格。ccswitch configuration error: unrecognized settingYAML 中写了 Codex CLI 不认识的字段如openrig: true或proxy_debug: true。删除所有非官方文档列出的字段。4.4 第四层流式响应的管道完整性5 分钟内可验证即使前三层都 OK/responses接口仍可能返回空响应。这是因为 Node.js 的spawn子进程 stdout 流与 Express 的res.write()之间存在缓冲区错位。验证命令在 tmux 下方日志窗格观察# 手动向 OpenRig 发送请求并实时看日志 curl -X POST http://127.0.0.1:3000/responses \ -H Content-Type: application/json \ -d {messages:[{role:user,content:test}]} \ /dev/null # 同时观察 rig.log应有类似 # [2024-05-20T10:30:00.000Z] INFO: Request received # [2024-05-20T10:30:00.100Z] INFO: Codex CLI started with pid 12345 # [2024-05-20T10:30:00.200Z] ERROR: stderr output from codex...典型症状与修复日志里有INFO: Codex CLI started但无后续ERROR或data:输出Codex CLI 启动了但没输出任何内容。检查codex run命令是否加了--stream参数OpenRig 的src/server.js已默认加但手动测试时需补上。日志里有ERROR: stderr output from codex...内容是invalid api keyYAML 中的api_key值错误或对应模型的服务端拒绝了该 key。日志里有WARN: Request timeoutCodex CLI 执行超时。增大timeout_ms配置或检查模型 endpoint 是否可达curl -v https://api.deepseek.com/health。经验总结我处理过的 23 个cc switch local proxy failed案例中12 个是 YAML 字段名拼写错误如apik_key7 个是端口被其他程序占用Docker Desktop 常占 30003 个是 Codex CLI 二进制损坏下载不完整只有 1 个是 Node.js 版本不兼容。所以永远先查 YAML 和端口。5. OpenRig 的进阶玩法超越基础代理的工程化扩展当 OpenRig 的基础功能跑通后它就不再是一个“能用就行”的玩具而是一个可深度定制的 AI 工程化平台。下面分享三个我在实际项目中落地的、真正提升生产力的扩展方向每个都附带可运行的代码片段。5.1 模型性能监控给每个请求打上耗时标签Codex CLI 默认不输出耗时但 OpenRig 的 Node.js 层可以。我们在src/server.js的/responses接口中加入计时逻辑并将耗时作为 SSE 的event字段发出// 在 src/server.js 的 /responses 处理函数开头添加 const startTime Date.now(); // 在 codex.on(close) 回调里添加 const durationMs Date.now() - startTime; console.log(⏱️ 请求完成总耗时 ${durationMs}ms模型: ${config.model_routing?.default_model}); // 同时发送到客户端 res.write(event: timing\ndata: {duration_ms:${durationMs},model:${config.model_routing?.default_model}}\n\n);效果VS Code 插件收到的 SSE 流中会多出event: timing类型的消息。前端可据此绘制响应时间分布图或当duration_ms 30000时自动告警。这比单纯看日志快 10 倍。5.2 配置热重载不用重启服务YAML 改了立刻生效每次改 YAML 都要Ctrl-c再npm start效率极低。用chokidar库监听文件变化npm install chokidar在src/server.js顶部添加const chokidar require(chokidar); // 启动时监听 YAML chokidar.watch(./config/codex-config.yaml).on(change, () { console.log( 检测到 YAML 变更正在重载配置...); try { config yaml.load(fs.readFileSync(./config/codex-config.yaml, utf8)); console.log(✅ 配置重载成功); } catch (e) { console.error(❌ 配置重载失败:, e.message); } });实测效果改完 YAML 保存3 秒内新配置生效。我曾用此功能在线上 A/B 测试两个模型的响应质量全程无服务中断。5.3 多环境 YAML一套代码三套配置开发/测试/生产利用 YAML 的多文档特性在config/codex-config.yaml里写三个配置# config/codex-config.yaml --- # 开发环境 env: dev proxy_mode: local models: gpt-4.5-turbo: api_key: sk-dev-xxx ... --- # 测试环境 env: test proxy_mode: local models: gpt-4.4-turbo: api_key: sk-test-xxx ... --- # 生产环境 env: prod proxy_mode: local models: claude-3.5-sonnet: api_key: sk-prod-xxx ...在src/server.js中用yaml.loadAll()读取并根据环境变量选择const allConfigs yaml.loadAll(fs.readFileSync(./config/codex-config.yaml, utf8)); const ENV process.env.NODE_ENV || dev; config allConfigs.find(c c.env ENV); if (!config) throw new Error(No config found for NODE_ENV${ENV});启动时指定环境NODE_ENVprod npm start。这让我们在 CI/CD 流水线中用同一套 OpenRig 代码无缝切换不同模型供应商。最后分享一个真实技巧我把 OpenRig 的 tmux 会话命名为codex-rig-$(date %Y%m%d)每天一个新会话。这样tmux list-sessions就能看到历史所有调试记录哪天哪个配置出了问题一目了然。运维同学说我这招比 ELK 还好用——毕竟最可靠的日志就是你自己亲手敲出来的命令历史。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →