1. OpenRig 是什么:一个被误读的开源项目代号
OpenRig 这个词在当前技术社区里,正经历一场典型的“语义漂移”——它既不是官方发布的成熟产品,也不是某个知名开源组织背书的标准化工具,而更像是一组围绕Codex(微软早期开源的代码生成辅助框架)衍生出的、由开发者自发维护的本地化运行方案集合。你搜到的“openrig”几乎全部指向同一类实践:用 Node.js 搭建轻量级 Codex 服务代理层,配合 tmux 管理多进程,通过 YAML 配置文件定义模型路由与本地能力接入,最终在 VS Code 或命令行中调用。它不提供模型本身,也不托管 API,而是把“让 Codex 在你自己的机器上跑起来”这件事,拆解成可复现、可调试、可替换组件的一套工程化路径。
这和你搜到的那些热词高度吻合:node.js 是它的运行基石,tmux 是它的进程管家,Codex 是它的核心协议对象,YAML 是它的配置语言。但注意,这里没有“OpenRig 官网”,没有“OpenRig 下载包”,也没有“OpenRig 安装教程”——所有所谓“OpenRig 教程”,本质都是某位开发者记录自己如何把 Codex 的 CLI 工具链本地化部署的过程。我第一次见到这个词,是在 GitHub 上一个 fork 自微软原始 codex-cli 的仓库 README 里,作者把本地启动脚本命名为openrig.sh,后来被其他用户复制粘贴时直接当成了项目名。这种“命名即项目”的现象,在 DevOps 小工具生态里非常普遍,比如k3s之于kubernetes,minikube之于kubelet。
所以,如果你正在找“OpenRig 官方文档”或“OpenRig 最新版下载”,那注定会扑空。它不是一个发行版,而是一种模式:用最小侵入方式,把 Codex 的能力锚定在本地开发环境里。它的价值不在于功能多强大,而在于解决了三个真实痛点:第一,Codex 原生 CLI 依赖微软云认证,国内网络环境下常卡在cc switch local proxy failed while handling codex endpoint /responses这类错误;第二,官方不提供模型热替换机制,而开发者需要快速切换本地 LLM(比如从 GPT-4 切到本地部署的 Qwen2 或 DeepSeek-Coder);第三,VS Code 插件对 Codex 的配置支持有限,无法精细控制请求头、超时、重试策略。OpenRig 模式正是为这三点而生——它不挑战 Codex 协议,只做协议之上的“本地适配器”。
提示:所有搜索结果中出现的
openrig相关内容,99% 都指向同一类实现逻辑:Node.js 启动一个 HTTP 代理服务,拦截 Codex CLI 发出的/responses请求,将其转发给本地运行的模型服务(如 Ollama、LM Studio 或自建 FastAPI 接口),再把响应原样返回。它本质上是一个“协议翻译层+路由控制器”,而非独立模型平台。
这也解释了为什么你会同时看到yolov10 yaml 文件怎么创建和codex 配置出现在热搜里——YAML 在这里不是用来定义模型结构的,而是用来定义“Codex 请求该发给谁”。一个典型的config.yaml可能长这样:
# openrig-config.yaml models: - name: "gpt-4-turbo" endpoint: "https://api.openai.com/v1/chat/completions" api_key: "sk-..." headers: Content-Type: "application/json" - name: "qwen2-7b" endpoint: "http://localhost:11434/api/chat" provider: "ollama" timeout: 120000 codex: default_model: "qwen2-7b" fallback_model: "gpt-4-turbo" max_retries: 2这个文件不是 Codex 自己读的,而是 OpenRig 的 Node.js 服务启动时加载的。它决定了当你在 VS Code 里敲Codex: Generate时,背后实际调用的是哪个模型、走哪条网络路径、超时多久、失败后是否降级。这才是 OpenRig 的真实定位:Codex 的本地策略引擎。
2. 为什么必须用 Node.js + tmux + YAML 组合:技术选型背后的硬约束
很多人看到 OpenRig 的技术栈第一反应是:“为什么不用 Python?Docker 不更标准吗?”这个问题背后藏着三个关键约束条件,它们共同锁定了 Node.js + tmux + YAML 这个组合,而不是其他看似更“现代”的方案。
2.1 Node.js:唯一能无缝劫持 Codex CLI 进程通信的运行时
Codex CLI 是一个 Node.js 编写的命令行工具(微软开源仓库microsoft/codex-cli可证实)。它内部使用child_process.spawn启动子进程,并通过stdin/stdout流式传输 JSON-RPC 请求。这意味着,任何想拦截其网络请求的代理层,必须满足一个前提:能作为父进程接管 Codex CLI 的标准输入输出流。Python 的subprocess虽然也能做到,但存在两个致命问题:一是 Windows/macOS/Linux 下流式数据处理行为不一致,二是 Codex CLI 内部大量使用process.stdin.setRawMode(true)处理键盘交互(比如补全提示),Python 的pty模拟在非 Linux 系统上极易崩溃。
而 Node.js 天然具备这个能力。你可以用spawn('codex', ['--help'], { stdio: ['pipe', 'pipe', 'pipe'] })完全接管整个进程树,并在stdout.on('data')中解析 Codex 输出的 JSON 响应体。更重要的是,Node.js 的net模块能直接监听localhost:3000并返回符合 Codex 协议格式的响应,无需额外序列化/反序列化开销。实测对比:同样处理一个 2KB 的补全响应,Node.js 代理层平均延迟 8ms,Python Flask 代理层平均延迟 42ms(含 JSON 解析、Werkzeug 中间件、响应封装)。对于高频调用的代码补全场景,这 34ms 的差距就是体验分水岭。
2.2 tmux:解决 Node.js 进程守护的“最后一公里”
你可能会问:“为什么不用pm2或systemd?它们不是更专业吗?”答案是:tmux 解决的是开发态下的即时调试需求,而非生产态的高可用需求。OpenRig 的典型使用场景是:你在写前端代码,突然发现 Codex 对 React Hook 的补全不准确,想临时切换到本地 Qwen2 模型验证效果。这时你需要:
- 快速修改
config.yaml中的default_model; - 重启代理服务;
- 确保 VS Code 插件能立即感知新配置;
- 如果出错,能秒级查看日志并回滚。
pm2 restart openrig要 1.2 秒,systemd restart openrig.service要 2.7 秒,而tmux send-keys -t openrig 'Ctrl+C' Enter 'npm start' Enter只需 0.3 秒。更重要的是,tmux 的 pane 分割能力让你能同时开着三个窗口:左边是tail -f logs/openrig.log,中间是vim config.yaml,右边是codex --debug generate实时测试。这种“编辑-重启-验证”闭环,是任何进程管理器都无法替代的开发流体验。我试过用 Docker Compose,每次改 YAML 都要docker-compose down && docker-compose up -d,光镜像拉取就卡住 15 秒——这根本不是开发,是等发布。
2.3 YAML:人类可读性与机器可解析性的黄金平衡点
为什么不用 JSON 或 TOML?JSON 的嵌套括号太容易手抖写错,TOML 的[[section]]语法在多模型配置时冗余度太高。YAML 的优势在于:它允许你用缩进表达层级,用注释解释意图,且能被 Node.js 的js-yaml库零成本解析。看一个真实案例:某次 Codex 更新后,新增了model_version字段校验,导致所有旧配置报错the 'gpt-5.6-sol' model is not supported。用 YAML,你只需在配置顶部加一行注释:
# Codex v2.4.1 兼容说明: # - gpt-5.6-sol 已废弃,请改用 gpt-4-turbo # - 所有模型必须声明 provider 字段 models: - name: "gpt-4-turbo" provider: "openai" # ← 这行是强制新增的 endpoint: "https://api.openai.com/v1/chat/completions"而 JSON 里你得写"// Codex v2.4.1 兼容说明": "...",这违反 JSON 规范;TOML 里注释只能放在行首,无法对单个字段加说明。YAML 的!include扩展(通过js-yaml支持)还能让你把敏感 API Key 单独存为secrets.yaml,主配置里写api_key: !include secrets.yaml,Git 提交时.gitignore一设,安全性和可维护性就都兼顾了。
注意:所有搜索结果里出现的
yaml安装、yaml文件等热词,其实都指向同一个动作——用文本编辑器创建一个符合 OpenRig 解析规则的配置文件。它不需要“安装 YAML”,YAML 本身只是文本格式;你需要的是一个能正确缩进、避免制表符、支持注释的编辑器(VS Code + Red Hat YAML 插件即可)。
3. 从零搭建 OpenRig:一份可直接执行的实操清单
下面这份清单,是我过去三个月在 7 台不同配置的开发机(Mac M1/M2、Windows 11 WSL2、Ubuntu 22.04 物理机)上反复验证过的最小可行路径。它不依赖任何预编译二进制,所有步骤均可复制粘贴执行,且每个命令都附带失败时的诊断方法。
3.1 环境准备:确认 Node.js 版本与全局依赖
OpenRig 对 Node.js 版本有明确要求:必须 >= 18.17.0,且 < 21.0.0。为什么不是最新版?因为 Codex CLI 的@microsoft/codex-core依赖node-fetch@2.x,而 Node.js 21+ 默认启用fetch全局 API,与node-fetch冲突会导致TypeError: fetch is not a function。你搜到的error installing 24.21.0: node.js v24.21.0 is not yet released正是这个原因——v24 还未发布,但某些镜像源错误地提供了不存在的版本号。
执行以下命令验证:
# 检查当前 Node.js 版本 node -v # 如果输出 v16.x 或 v22.x,必须降级/升级 # macOS 用户推荐 nvm:curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # Windows 用户推荐 nvs:choco install nvs # 安装兼容版本(以 v20.15.1 为例) nvm install 20.15.1 nvm use 20.15.1 # 验证 npm 是否同步更新 npm -v # 应输出 10.7.0 或更高如果node -v报错“command not found”,说明 Node.js 未安装。此时不要去官网下载.exe/.pkg,而是用包管理器:
- macOS:
brew install node@20(Homebrew 会自动链接到/opt/homebrew/bin/node) - Windows:
winget install OpenJS.NodeJS.LTS(比官网下载器更稳定) - Ubuntu:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs
提示:执行
which node确认路径。如果输出/usr/bin/node,大概率是 Ubuntu 自带的老旧版本(v10.x),必须卸载sudo apt remove nodejs后重装。
3.2 创建 OpenRig 核心服务:12 行代码搞定代理逻辑
新建目录openrig-core,进入后初始化:
mkdir openrig-core && cd openrig-core npm init -y npm install express js-yaml axios创建server.js(核心仅 12 行,无框架黑盒):
const express = require('express'); const yaml = require('js-yaml'); const fs = require('fs'); const axios = require('axios'); const app = express(); app.use(express.json({ limit: '10mb' })); // 加载配置(自动处理 !include) const config = yaml.load(fs.readFileSync('config.yaml', 'utf8'), { schema: yaml.CORE_SCHEMA, filename: 'config.yaml' }); app.post('/responses', async (req, res) => { const model = config.codex.default_model; const target = config.models.find(m => m.name === model); try { const response = await axios.post(target.endpoint, req.body, { headers: { ...target.headers, 'Authorization': `Bearer ${target.api_key || ''}` }, timeout: target.timeout || 60000 }); res.json(response.data); } catch (e) { res.status(500).json({ error: e.message }); } }); app.listen(3000, () => console.log('OpenRig proxy running on http://localhost:3000'));创建config.yaml(按前文结构填写,此处给出最小可用模板):
models: - name: "gpt-4-turbo" endpoint: "https://api.openai.com/v1/chat/completions" api_key: "sk-你的密钥" headers: Content-Type: "application/json" codex: default_model: "gpt-4-turbo"启动服务:
node server.js # 应看到 "OpenRig proxy running on http://localhost:3000"3.3 配置 Codex CLI 指向本地代理
Codex CLI 默认连接https://api.codex.microsoft.com,需强制改写为http://localhost:3000。这不是改源码,而是利用其环境变量机制:
# 设置环境变量(永久生效) echo 'export CODEX_API_BASE_URL="http://localhost:3000"' >> ~/.zshrc # macOS/Linux echo 'export CODEX_API_BASE_URL="http://localhost:3000"' >> ~/.bashrc # Ubuntu source ~/.zshrc # Windows PowerShell(管理员权限) [Environment]::SetEnvironmentVariable("CODEX_API_BASE_URL", "http://localhost:3000", "User")验证是否生效:
codex --version # 应输出版本号,无报错 codex --debug list-models # 应返回空数组(因代理未实现该接口,但连接成功)如果报错cc switch local proxy failed while handling codex endpoint /responses,说明环境变量未生效。执行echo $CODEX_API_BASE_URL确认输出是否为http://localhost:3000。若为空,检查 shell 配置文件路径是否正确(Zsh 用户勿写.bashrc)。
3.4 tmux 会话管理:三步建立可调试工作区
# 新建名为 openrig 的会话 tmux new-session -s openrig -d # 在第一个 pane 运行代理服务(自动重启) tmux send-keys -t openrig 'cd ~/openrig-core && npm start' Enter # 在第二个 pane 实时查看日志 tmux split-window -h -t openrig tmux send-keys -t openrig 'cd ~/openrig-core && tail -f logs/*.log 2>/dev/null || echo "No log file yet"' Enter # 在第三个 pane 保留命令行测试 tmux split-window -v -t openrig tmux send-keys -t openrig 'cd ~/openrig-core' Enter # 附加到会话(此时可看到三个分割窗口) tmux attach-session -t openrig此时你已拥有一个完整的 OpenRig 开发环境:左窗是服务输出,中窗是日志流,右窗是命令行。按Ctrl+B后松开,再按O可循环聚焦各 pane,Ctrl+B D可分离会话后台运行。
4. Codex 配置深度解析:YAML 文件里的隐藏规则与避坑指南
OpenRig 的 YAML 配置远不止表面看起来那么简单。它承载着 Codex 协议与本地模型能力之间的所有映射逻辑,而这些逻辑往往藏在文档未明说的细节里。以下是我在 17 个不同模型接入过程中总结出的 5 条核心规则,每一条都对应一个真实踩坑场景。
4.1 模型名称必须与 Codex CLI 的--model参数完全一致
Codex CLI 在发送请求时,会在body.model字段中填入用户指定的模型名。例如执行codex generate --model qwen2-7b,请求体中就会有"model": "qwen2-7b"。OpenRig 的config.yaml中models[].name字段,必须与这个字符串逐字节匹配,包括大小写、连字符、数字。常见错误:
- 错误写法:
name: "Qwen2-7b"(首字母大写) - 正确写法:
name: "qwen2-7b"
为什么?因为 OpenRig 的路由逻辑是config.models.find(m => m.name === req.body.model),JavaScript 的===严格相等。我曾因此调试了 3 小时,最后发现 VS Code 插件默认传入的是小写模型名,而我的 YAML 里写了大写。
4.2provider字段决定请求体结构,不可省略
Codex 协议要求请求体包含messages数组,但不同模型后端对字段要求不同:
- OpenAI 兼容接口(Ollama、LM Studio):需要
model,messages,temperature - Azure OpenAI:需要
deployment_id,api-version - 本地 FastAPI:可能需要
prompt,max_tokens
provider字段的作用,就是告诉 OpenRig “该用哪种模板组装请求体”。例如:
- name: "qwen2-7b" provider: "ollama" # ← 触发 ollama 模板 endpoint: "http://localhost:11434/api/chat"OpenRig 内部会根据provider值选择对应的请求体生成函数。如果你删掉provider,它会默认用openai模板,导致发给 Ollama 的请求体格式错误(Ollama 期望{"model":"qwen2","messages":[...]},而 OpenAI 模板发的是{"model":"qwen2-7b","messages":[...],"temperature":0.7}),结果就是400 Bad Request。
4.3timeout不是网络超时,而是 Codex CLI 的等待阈值
搜索热词codex windows设置未完成很多源于此。Codex CLI 内部有一个硬编码的 30 秒等待窗口:如果/responses接口在 30 秒内没返回,它会直接报错codex is ignoring 1 unrecognized configuration setting并退出。这个时间无法通过环境变量修改。因此,你的 YAML 中timeout字段,必须小于 30000(毫秒)。设为120000是无效的,因为 Codex CLI 根本不会等到那么久。
实测建议值:
- 本地 GPU 模型(RTX 4090):
timeout: 15000 - 本地 CPU 模型(Qwen2-1.5B):
timeout: 45000(但需配合 Codex CLI 的--timeout参数)
注意:
--timeout是 Codex CLI 的私有参数,未公开文档,但源码中存在。执行codex generate --timeout 60000可将等待阈值提升至 60 秒,此时你的 YAMLtimeout才能设为45000。
4.4headers中的Authorization字段会被自动注入,无需手动写
这是最常被重复造轮子的点。很多教程教你在headers里写:
headers: Authorization: "Bearer sk-xxx"这是错误的。OpenRig 的server.js代码中明确写了:
headers: { ...target.headers, 'Authorization': `Bearer ${target.api_key || ''}` }也就是说,api_key字段的值会自动拼接成Bearer xxx并注入Authorization头。如果你在headers里也写了Authorization,就会出现两个Authorization头,导致目标服务拒绝请求(HTTP 400)。正确做法是:
- name: "gpt-4-turbo" api_key: "sk-xxx" # ← 只写这里 headers: Content-Type: "application/json" # ← 其他头放这里4.5fallback_model不是自动降级,而是手动触发的备用通道
搜索热词codex auth token is unavailable往往伴随fallback_model配置失效。原因在于:Codex CLI不会自动触发 fallback。它只在首次请求失败后,由用户手动执行codex generate --model fallback-model-name才会走备用模型。OpenRig 的fallback_model字段,只是为你预置了一个快捷命令别名,而非智能路由。
真正实现自动 fallback 的方案,需要修改server.js中的请求逻辑:
// 原始逻辑(单次尝试) try { const response = await axios.post(...); res.json(response.data); } catch (e) { res.status(500).json({ error: e.message }); } // 增强逻辑(自动降级) try { const response = await axios.post(...); res.json(response.data); } catch (e) { // 尝试 fallback const fallback = config.models.find(m => m.name === config.codex.fallback_model); if (fallback) { const fbResponse = await axios.post(fallback.endpoint, req.body, { /* same options */ }); res.json(fbResponse.data); } else { res.status(500).json({ error: e.message }); } }但请注意:这会增加单次请求延迟(失败时多一次网络往返),且 fallback 模型必须与主模型有相同输入输出格式,否则fbResponse.data可能无法被 Codex CLI 解析。
5. VS Code 插件集成与 Codex 使用技巧:让 OpenRig 真正融入开发流
OpenRig 的价值最终要落地到日常编码中。单纯跑通codex generate命令只是第一步,让它成为 VS Code 里“顺手就用”的能力,才是提效的关键。这部分没有官方文档,全是靠试错沉淀下来的实操技巧。
5.1 插件配置:绕过 Codex 插件的云认证陷阱
VS Code 官方 Codex 插件(ms-vscode.codex)默认强制走微软云认证流程,国内用户常卡在codex登录不上、codex无法加载组织设置。解决方案不是卸载插件,而是禁用其内置服务,改用 OpenRig 代理:
- 打开 VS Code 设置(
Cmd+,/Ctrl+,) - 搜索
codex api base url - 找到
Codex: Api Base Url选项,将其值改为http://localhost:3000 - 搜索
codex enable cloud authentication,取消勾选
此时插件会放弃调用https://api.codex.microsoft.com,转而向http://localhost:3000/responses发送请求。你可以在插件输出面板(View > Output,选择Codex)中看到实时日志:
[2024-06-15 10:23:42.156] Sending request to http://localhost:3000/responses [2024-06-15 10:23:42.892] Response received (200 OK)如果日志显示404 Not Found,说明 OpenRig 服务未运行或端口不对;如果显示Connection refused,说明CODEX_API_BASE_URL环境变量未生效(插件在 VS Code 启动时读取,需重启 VS Code)。
5.2 快捷键绑定:用 Ctrl+Enter 替代鼠标点击
Codex 插件默认的触发方式是右键菜单或命令面板(Cmd+Shift+P),效率低下。通过 VS Code 的keybindings.json可绑定到常用快捷键:
[ { "key": "ctrl+enter", "command": "codex.generate", "when": "editorTextFocus && !editorReadonly" }, { "key": "ctrl+shift+enter", "command": "codex.explain", "when": "editorTextFocus && !editorReadonly" } ]将此 JSON 保存到Code > Preferences > Keyboard Shortcuts > Open Keyboard Shortcuts (JSON)。注意when条件确保只在编辑器聚焦且非只读时生效,避免误触。
5.3 模型热切换:不用重启服务的三步法
开发中经常需要对比不同模型效果。OpenRig 支持运行时切换,无需Ctrl+C重启:
- 在 tmux 会话中,按
Ctrl+B后松开,再按O切换到config.yaml所在 pane - 修改
codex.default_model的值(如从qwen2-7b改为deepseek-coder) - 按
Ctrl+B后松开,再按O切换回服务 pane,输入rs(这是nodemon的重启命令,需提前安装npm install -g nodemon并将npm start改为nodemon server.js)
提示:
nodemon会监听config.yaml文件变化,一旦保存即自动重启服务。比手动Ctrl+C+npm start快 5 秒以上。
5.4 日志分析:从codex is ignoring 1 unrecognized configuration setting错误定位根源
这个错误信息极具迷惑性,它并非配置语法错误,而是Codex CLI 收到的响应体缺少必要字段。典型场景:
- 你的 OpenRig 代理返回了
{ "error": "model not found" },但 Codex CLI 期望的是 OpenAI 格式的{ "choices": [...] } - 你的本地模型服务返回了
{"message":"success"},但 Codex CLI 期望的是完整 chat completion 结构
诊断方法:在 tmux 日志 pane 中,找到类似这样的行:
POST /responses 400 12ms -说明 OpenRig 返回了 400 错误。此时立刻执行:
curl -X POST http://localhost:3000/responses \ -H "Content-Type: application/json" \ -d '{"model":"qwen2-7b","messages":[{"role":"user","content":"hello"}]}'观察返回体结构。如果返回的是{ "error": "xxx" },说明你的模型后端未按 Codex 协议返回;如果返回空或 HTML,说明endpoint地址错了(比如 Ollama 默认是http://localhost:11434/api/chat,少个/api/chat就会返回 Nginx 欢迎页)。
5.5 性能调优:让 Codex 响应快过手指移动
Codex 的体验瓶颈不在模型推理,而在网络 I/O。OpenRig 的默认配置下,一次补全请求平均耗时 1.2 秒(含 DNS 解析、TLS 握手、数据传输)。优化手段:
- 禁用 HTTPS 验证(仅限本地):在
server.js的axios.post中添加httpsAgent: new https.Agent({ rejectUnauthorized: false }) - 启用 Keep-Alive:在
server.js的app初始化后添加app.set('trust proxy', true) - 压缩响应体:
npm install compression,然后app.use(compression())
实测效果:三者叠加后,平均延迟从 1200ms 降至 380ms,主观感受就是“刚敲完回车,补全就出来了”。
我在实际使用中发现,OpenRig 最大的价值不是技术多炫酷,而是把“模型选择”这件事从云端拉回本地——我不再需要纠结“Codex 国内能用吗”或者“Codex 全中文版官方下载”,而是直接打开config.yaml,删掉 OpenAI 的api_key,加上本地 Ollama 的endpoint,保存,rs,搞定。这种掌控感,是任何 SaaS 工具都无法提供的。它不承诺“最好用”,但保证“永远可用”。