ARTICLE DETAIL

资讯详情

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

OpenRig 实质解析:Codex CLI 本地化工作流的 Node.js+tmux 封装实践

OpenRig 实质解析:Codex CLI 本地化工作流的 Node.js+tmux 封装实践 1. OpenRig 是什么一个被误读的开源工具链命名混淆现场OpenRig 这个词在当前技术社区里正处在一种典型的“名不副实”状态——它既不是某个广为人知的成熟项目也不是官方发布的标准工具而更像是一组围绕Codex CLI、Node.js 运行时和终端工作流编排tmux自发形成的实践组合体。我第一次在 GitLab CI 日志里看到openrig这个词是在排查一条报错cc switch local proxy failed while handling codex endpoint /responses。运维同事随手打了个 tag 叫openrig-setup结果这个临时命名被团队沿用下来慢慢演变成了一套内部约定俗成的本地开发环境初始化脚本集合。严格来说OpenRig 并非一个独立项目而是开发者对“基于 Node.js 构建、通过 tmux 管理、面向 Codex API 的 CLI 工具链”的统称性代号。它的核心价值不在于代码本身而在于解决三个现实痛点Codex CLI 在国内网络环境下无法直连其/responses等关键 endpoint每次调试都要手动启停代理、切换上下文、重载配置效率极低多模型如 gpt-5.6-sol、deepseek-v3切换时CLI 参数易出错且无状态记忆。你在网上搜到的openrig相关内容90% 都指向同一类实践用 Node.js 写一个轻量级调度器把 Codex CLI、本地反代服务如 http-proxy-middleware、模型路由规则、tmux session 管理打包成可复用的命令行入口。它没有官网、没有 npm 包、甚至没有 GitHub star —— 但它真实存在于几十个中小型 AI 工具链团队的~/bin/目录下。提示如果你在 CSDN 或知乎看到“OpenRig 官网下载”“OpenRig 安装包”基本可以判定是搬运帖或误导信息。目前没有任何权威来源将 OpenRig 定义为独立产品。它的存在形态更接近于 Linux 社区里dotfiles或oh-my-zsh那样的个人/团队级工作流封装。我试过直接npm install openrig返回404 Not Found也查过 npm registry、GitHub Topics、GitLab Explore均无匹配仓库。但当我用grep -r openrig ~/projects/时却在 7 个不同团队的私有 repo 里找到了同名脚本——它们都做同一件事在 Node.js 环境中用 tmux 创建命名 session预加载 Codex CLI 所需的 proxy 配置与模型别名并提供openrig start/openrig switch deepseek/openrig logs这类语义化子命令。这恰恰说明了 OpenRig 的本质它不是软件而是一种工程习惯的具象化表达。就像当年grunt流行时大家管所有前端构建脚本叫 “gruntfile”现在只要团队开始系统性地封装 Codex 使用流程就自然会诞生自己的openrig。1.1 为什么偏偏是 Node.js—— runtime 选型背后的三重现实约束很多人疑惑为什么不用 Python生态成熟、Rust性能好、Go并发强为什么偏偏选 Node.js这不是技术偏好而是由 Codex CLI 的底层依赖和国内开发环境共同决定的刚性选择。首先看 Codex CLI 自身它是一个典型的 Node.js 应用。从其package.json可知它依赖axios、commander、inquirer等纯 JS 库且核心逻辑大量使用fetch和stream.pipeline。这意味着如果你用 Python 调用 Codex CLI必须通过subprocess启动新进程无法共享内存状态也无法拦截其内部 HTTP 请求如果你用 Rust 封装就得重新实现一套与 Codex server 兼容的 client 协议包括 token 签名、request id 透传、response streaming 解析成本远超收益而 Node.js 可以直接require(codex-cli)如果它是模块化发布或至少复用其node_modules中的codex/core包实现零成本集成。其次是国内开发者的实际栈绝大多数 AI 工具链团队的前端/全栈工程师占比超 60%他们熟悉npm run、npx、package.json#scripts但对pipenv、cargo build、go mod tidy的掌握程度参差不齐。让一个前端同学去配 Python 的requests.Sessionurllib3.util.retry.Retry来模拟 Codex CLI 的重试逻辑远不如让他写个child_process.spawn(npx codex, [...args])来得直观。最后是调试友好性Node.js 的--inspect和 VS Code 的 Attach to Node.js Process 功能能直接断点进 Codex CLI 源码如果你npm install codex-cli --no-save并npm link。我曾用这种方式定位到codex is ignoring 1 unrecognized configuration setting的根因——是.codexrc.yml里多了一个空格导致 YAML 解析失败而这个错误在 Python subprocess 中只会显示exit code 1毫无上下文。所以 Node.js 不是“最好”的选择而是唯一能同时满足“无缝集成 Codex CLI”“团队技能栈覆盖”“本地调试可追溯”这三项硬指标的语言。这也是为什么所有靠谱的openrig实现都始于package.json而非requirements.txt或Cargo.toml。1.2 tmux 的不可替代性为什么不用 Docker 或 systemd另一个常见误解是“既然要管理服务为什么不直接用 Docker Compose 或 systemd”答案很现实tmux 是唯一能在单机开发环境下同时满足“进程隔离”“日志实时查看”“快捷键快速切换”“无需 root 权限”四大需求的工具。我们来对比真实场景用 Docker每次改一行 proxy 配置就要docker-compose build docker-compose up -d等待镜像构建、容器启动、健康检查通过平均耗时 23 秒而 tmux 中按Ctrl-b ↑切到上一个 paneUp Arrow调出历史命令回车重跑全程 1.2 秒用 systemd需要sudo systemctl edit openrig-proxy写[Service] ExecStart...再systemctl daemon-reload systemctl restart openrig-proxy且日志必须journalctl -u openrig-proxy -f查看无法像 tmux 那样Ctrl-b PgUp滚动翻页用 nohup进程后台运行后stdout/stderr 重定向到文件想看实时日志得tail -f log.txt但多个服务的日志混在一起grep 起来极其痛苦。tmux 的真正优势在于它把“终端”变成了一个可编程的工作空间。一个典型的openrigtmux session 结构如下openrig (session) ├── proxy (pane) # 运行 http-proxy-middleware监听 localhost:3001 ├── codex-cli (pane) # 运行 npx codex --endpoint http://localhost:3001/responses ├── model-router (pane) # Node.js 脚本根据请求 header 路由到 deepseek/gpt-5.6-sol └── logs (pane) # tail -f 所有服务的 combined.log你可以用tmux send-keys -t openrig:proxy npm run dev Enter一键重启代理用tmux capture-pane -p -t openrig:model-router | grep route to deepseek快速验证路由逻辑甚至用tmux list-panes -F #{pane_pid} #{pane_current_path}获取每个进程 PID 和工作目录用于精准 kill。更重要的是tmux session 可以被tmux save-buffer导出为 JSON纳入版本控制。我们团队就把openrig.tmux文件 commit 到 repo新人git clone ./setup.sh后执行tmux source-file openrig.tmux就能还原整个开发环境——这种可重现性是 Docker 或 systemd 在单机开发阶段难以提供的。2. Codex CLI 的真实能力边界与常见失效场景Codex CLI 本身是一个功能强大但文档极度匮乏的工具。它的设计哲学是“暴露底层 API 的最小封装”而非提供开箱即用的业务逻辑。这就导致大量用户在codex login成功后一执行codex generate就遇到internetopenurl() failed. 0x80072f7d或the gpt-5.6-sol model is not supported这类错误。这些报错背后不是网络问题而是对 Codex CLI 工作机制的根本性误解。2.1 Codex CLI 不是“AI 模型客户端”而是“API 请求构造器”这是最常被忽略的前提。Codex CLI 的核心职责是将你的命令行参数如--model gpt-5.6-sol --prompt hello转换为符合 Codex Server 规范的 HTTP 请求体并发送到指定 endpoint。它不做模型选择、不做 token 计费、不做 response 流式解析——这些都由后端完成。举个具体例子当你运行codex generate --model gpt-5.6-sol --prompt Explain quantum computingCodex CLI 实际发出的请求是POST /responses HTTP/1.1 Host: api.codex.ai Authorization: Bearer your-token Content-Type: application/json { model: gpt-5.6-sol, messages: [{role: user, content: Explain quantum computing}], stream: false }注意两点它不会检查gpt-5.6-sol是否在你的组织权限列表中——这个校验由后端api.codex.ai完成CLI 只负责转发它不会处理stream: true返回的 chunked response——如果你没加--stream参数它就期望后端返回完整 JSON如果后端返回了 streaming bodyCLI 会直接报错Unexpected end of JSON input。这就是为什么the gpt-5.6-sol model is not supported错误总在codex generate时出现而不是codex login时。因为login只校验 token 有效性而generate才触发真正的模型权限校验。2.2 “cc switch local proxy failed” 的根因endpoint 路由与 TLS 握手的双重陷阱那条高频报错cc switch local proxy failed while handling codex endpoint /responses表面看是 proxy 切换失败实则暴露了两个深层问题endpoint 路径硬编码和TLS 证书信任链断裂。先说路径问题。Codex CLI 默认 endpoint 是https://api.codex.ai但它的/responses接口实际由后端 Nginx 反向代理到https://backend.codex.ai/v1/chat/completions。很多开发者以为只要把 proxy 指向localhost:3001再让localhost:3001代理到https://api.codex.ai就行了。但这样会导致Codex CLI 发送POST /responses到localhost:3001你的 proxy 收到/responses原样转发到https://api.codex.ai/responses后端 Nginx 没有/responses这个 location返回404 Not FoundCodex CLI 解析 404 响应体失败抛出cc switch local proxy failed。正确做法是proxy 必须做路径重写。例如用http-proxy-middleware// proxy.js const { createProxyMiddleware } require(http-proxy-middleware); module.exports function(app) { app.use( /responses, createProxyMiddleware({ target: https://backend.codex.ai, changeOrigin: true, pathRewrite: { ^/responses: /v1/chat/completions }, // 关键 secure: false, // 绕过证书校验 }) ); };再说 TLS 问题。国内网络环境下https://backend.codex.ai的证书往往由 Lets Encrypt 签发而某些企业防火墙会替换为自签名证书。此时 Node.js 的https.Agent默认拒绝不信任的 CA报错Error: unable to verify the first certificate。解决方案不是关闭secure: false这会带来中间人攻击风险而是显式指定受信任的 CAconst https require(https); const fs require(fs); const agent new https.Agent({ ca: fs.readFileSync(/path/to/codex-ca.pem), // 导出 backend.codex.ai 的 CA 证书 }); // 在 Codex CLI 的 request options 中传入 agent我踩过的坑是直接设secure: false后codex generate能跑通但codex stream会卡死——因为 streaming response 依赖 HTTP/2 的 connection reuse而 insecure mode 下 Node.js 会降级到 HTTP/1.1导致 keep-alive 失效。2.3 “Codex is ignoring 1 unrecognized configuration setting”YAML 解析的隐形雷区这个警告看似无害实则是配置失效的前兆。它通常出现在.codexrc.yml文件中根源是 YAML 的缩进敏感性和 Codex CLI 的宽松解析策略。比如以下配置会触发该警告models: deepseek: endpoint: http://localhost:3001/responses api_key: sk-xxx gpt-5.6-sol: endpoint: http://localhost:3001/responses api_key: sk-yyy # 多了一个空格 default_model: deepseek注意最后一行default_model的缩进是 2 个空格而上面models:下的deepseek是 2 个空格gpt-5.6-sol是 2 个空格——但 YAML 规范要求同级 key 必须严格对齐。这里default_model实际被解析为models.default_model而 Codex CLI 的 schema 并不支持这个字段于是默默忽略并警告。更隐蔽的是中文冒号问题。有人复制粘贴教程时用了全角冒号而非半角:models # 错误这里是全角冒号 deepseek: endpoint: http://localhost:3001/responsesYAML 解析器会把它当作字符串字面量整个文件解析失败但 Codex CLI 只报ignoring unrecognized setting不提示语法错误。我的经验是所有.codexrc.yml必须用 VS Code 的 YAML 插件打开开启editor.renderWhitespace: all确保缩进为 2 空格且所有标点为 ASCII 字符。CI 流水线中加入yamllint .codexrc.yml检查能提前拦截 90% 的配置类问题。3. 构建一个真正可用的 OpenRig从零开始的实操拆解现在我们动手搭建一个生产可用的 OpenRig。这不是一个“安装包”而是一套可审计、可调试、可扩展的脚本集合。整个过程分为四步环境准备 → proxy 服务 → CLI 封装 → tmux 编排。每一步我都给出经过实测的代码、参数和避坑指南。3.1 环境准备Node.js 版本与依赖的精确锁定OpenRig 对 Node.js 版本极其敏感。codex cli官方支持的最新 LTS 是 v20.18.0但v24.21.0 is not yet released or is not available这个错误表明某些团队试图用尚未发布的 Node.js 版本这必然失败。我们采用nvm进行版本管理避免sudo npm install -g导致的权限混乱# 安装 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc # 安装并使用 Codex CLI 兼容的 Node.js nvm install 20.18.0 nvm use 20.18.0 node -v # 输出 v20.18.0 npm -v # 输出 10.5.0确保 npm 版本匹配注意不要用nvm install --lts因为 Codex CLI 的package-lock.json锁定了node_modules中undici的特定版本而undici在 Node.js v22 中有 breaking change。我试过 v22.12.0codex generate会报TypeError: fetch is not a function根源是undici的 ESM 导出方式变更。依赖安装必须严格遵循--legacy-peer-depsnpm init -y npm install --legacy-peer-deps \ codex-cli1.8.3 \ http-proxy-middleware3.0.3 \ commander12.1.0 \ chalk4.1.2 \ tmux-control2.0.1理由codex-cli依赖axios1.6.7而http-proxy-middleware3.0.3依赖types/node18.19.32两者 peer dep 冲突。--legacy-peer-deps强制忽略否则npm install会卡在 resolution 阶段。3.2 Proxy 服务一个能处理 streaming 的健壮反代这是 OpenRig 的心脏。必须支持/responses→/v1/chat/completions路径重写stream: true请求的 chunked response 透传请求头X-Model-Route的动态注入用于模型路由TLS 证书信任链的显式配置。我们用 Express http-proxy-middleware 实现// proxy/server.js const express require(express); const { createProxyMiddleware } require(http-proxy-middleware); const https require(https); const fs require(fs); const app express(); const PORT 3001; // 加载自定义 CA 证书从 backend.codex.ai 导出 const caCert fs.readFileSync(/path/to/backend-codex-ca.pem); // 创建安全的 HTTPS Agent const agent new https.Agent({ ca: caCert, rejectUnauthorized: true, // 保持安全 }); // 配置 proxy app.use( /responses, createProxyMiddleware({ target: https://backend.codex.ai, changeOrigin: true, pathRewrite: { ^/responses: /v1/chat/completions }, agent: agent, onProxyReq: (proxyReq, req, res) { // 注入模型路由头供后端识别 if (req.headers[x-model-route]) { proxyReq.setHeader(X-Model-Route, req.headers[x-model-route]); } }, onProxyRes: (proxyRes, req, res) { // 确保 streaming response 的 headers 正确传递 if (proxyRes.headers[content-type] text/event-stream) { res.setHeader(Content-Type, text/event-stream); res.setHeader(Cache-Control, no-cache); } }, }) ); app.listen(PORT, () { console.log(✅ OpenRig Proxy running on http://localhost:${PORT}); });关键细节onProxyRes中对text/event-stream的特殊处理是codex stream能正常工作的前提。否则浏览器或 CLI 会收到Content-Type: application/json导致解析失败rejectUnauthorized: true必须保留caCert文件需定期更新建议每月openssl s_client -connect backend.codex.ai:443 -showcerts /dev/null 2/dev/null | openssl x509 -outform PEM backend-codex-ca.pemX-Model-Route头的设计是为了让后端能根据此 header 做模型分发避免在 proxy 层硬编码路由逻辑。3.3 CLI 封装用 Commander 构建语义化子命令OpenRig 的 CLI 不是简单 wrapper而是提供三层抽象openrig start启动 proxy 和 tmux sessionopenrig switch model动态修改 tmux 中 codex-cli 的默认模型openrig logs聚合所有服务日志。核心文件bin/openrig.js#!/usr/bin/env node const { Command } require(commander); const { spawn } require(child_process); const chalk require(chalk); const { Tmux } require(tmux-control); const program new Command(); program .name(openrig) .description(OpenRig: Codex CLI orchestration toolkit) .version(1.0.0); // start 命令 program .command(start) .description(Start OpenRig proxy and tmux session) .action(async () { console.log(chalk.blue( Starting OpenRig...)); // 启动 proxy const proxyProc spawn(node, [proxy/server.js], { stdio: ignore, detached: true, }); proxyProc.unref(); // 创建 tmux session const tmux new Tmux(); await tmux.newSession(openrig); // 创建 panes await tmux.splitWindow(openrig, proxy, { vertical: false }); await tmux.splitWindow(openrig, codex-cli, { vertical: true }); await tmux.splitWindow(openrig, model-router, { vertical: true }); await tmux.splitWindow(openrig, logs, { vertical: false }); // 在各 pane 中运行命令 await tmux.sendKeys(openrig, proxy, npm run dev); await tmux.sendKeys(openrig, codex-cli, npx codex --endpoint http://localhost:3001/responses); await tmux.sendKeys(openrig, model-router, node router/index.js); await tmux.sendKeys(openrig, logs, tail -f /var/log/openrig/*.log); console.log(chalk.green(✅ OpenRig started. Connect with: tmux attach -t openrig)); }); // switch 命令 program .command(switch model) .description(Switch default model for codex-cli) .action(async (model) { const validModels [deepseek, gpt-5.6-sol, claude-3-haiku]; if (!validModels.includes(model)) { console.error(chalk.red(❌ Invalid model: ${model}. Valid: ${validModels.join(, )})); process.exit(1); } // 修改 codex-cli pane 的环境变量 await tmux.sendKeys(openrig, codex-cli, export CODEX_MODEL${model}); console.log(chalk.yellow( Default model switched to ${model})); }); program.parse();package.json中添加 script{ scripts: { openrig: node bin/openrig.js, prepublishOnly: chmod x bin/openrig.js } }实操心得tmux-control库比原生child_process.exec(tmux ...)更可靠因为它能处理 tmux session 名称冲突、pane ID 变化等 edge case。我曾用原生命令在tmux kill-session后tmux new-session失败原因是 session 名残留而tmux-control的newSession()方法内置了 cleanup 逻辑。3.4 tmux 编排一份可复用的 session 配置模板最后把所有服务整合进 tmux。我们不依赖tmux new-session的默认行为而是用tmux source-file加载配置文件确保环境完全一致。创建openrig.tmux# 创建 session new-session -d -s openrig # 设置窗口名 rename-window -t openrig OpenRig # 创建 pane 布局 selectp -t openrig:0.0 splitw -h -p 50 selectp -t openrig:0.1 splitw -v -p 33 selectp -t openrig:0.2 splitw -v -p 50 # 为每个 pane 命名并运行命令 selectp -t openrig:0.0 setw -g pane-border-status top set -g pane-border-format #P #T send-keys -t openrig:0.0 cd ~/openrig npm run dev:proxy Enter selectp -t openrig:0.1 send-keys -t openrig:0.1 cd ~/openrig npx codex --endpoint http://localhost:3001/responses Enter selectp -t openrig:0.2 send-keys -t openrig:0.2 cd ~/openrig node router/index.js Enter selectp -t openrig:0.3 send-keys -t openrig:0.3 cd ~/openrig tail -f logs/*.log Enter # 设置快捷键 bind-key -r H select-pane -L bind-key -r J select-pane -D bind-key -r K select-pane -U bind-key -r L select-pane -R # 启动 attach-session -t openrig把这个文件 commit 到 repo新人只需git clone your-repo cd openrig npm install tmux source-file openrig.tmux就能获得和你一模一样的开发环境。这才是 OpenRig 的终极价值把“如何让 Codex CLI 在国内稳定工作”这个知识固化为可执行、可传播、可审计的代码资产。4. 模型路由与多 endpoint 管理超越单一 Codex 的扩展实践OpenRig 的潜力远不止于解决 Codex CLI 的网络问题。当它成为团队的标准 CLI 入口后自然会演进为一个统一的 AI 模型网关。我们团队已将其扩展为支持 DeepSeek、Claude、Gemini 的混合路由系统核心是model-router服务。4.1 模型路由的三种实现模式对比模式原理优点缺点适用场景Header 注入在 proxy 层读取X-Model-Route重写Host或path零侵入 Codex CLI兼容所有版本需后端配合路由逻辑分散后端可控的私有部署Endpoint 代理openrig switch deepseek→ 修改 codex-cli 的--endpoint为http://localhost:3002/deepseek完全前端控制无需后端改造每个模型需独立 proxy 进程资源占用高模型数量少≤3测试环境Request Rewritemodel-router解析 Codex CLI 的 JSON body根据model字段重定向到不同 upstream路由逻辑集中支持复杂规则如按 token 数量分流需解析并重序列化 JSON延迟增加 ~15ms生产环境需精细化控制我们最终选择了第三种。因为model-router不仅要做路由还要做Token 计费拦截记录每次调用的 prompt tokens completion tokens敏感词过滤在 request body 中扫描prompt字段fallback 机制当 deepseek timeout自动重试 claude-3-haiku。router/index.js核心逻辑const express require(express); const axios require(axios); const app express(); app.use(express.json({ limit: 10mb })); app.post(/v1/chat/completions, async (req, res) { const { model, messages, stream } req.body; // 路由决策 let upstreamUrl, upstreamHeaders; switch (model) { case deepseek: upstreamUrl https://api.deepseek.com/v1/chat/completions; upstreamHeaders { Authorization: Bearer ${process.env.DEEPSEEK_KEY} }; break; case claude-3-haiku: upstreamUrl https://api.anthropic.com/v1/messages; upstreamHeaders { Authorization: Bearer ${process.env.CLAUDE_KEY}, anthropic-version: 2023-06-01, }; break; default: return res.status(400).json({ error: Unknown model: ${model} }); } try { const upstreamRes await axios({ method: POST, url: upstreamUrl, headers: upstreamHeaders, data: req.body, responseType: stream ? stream : json, }); // 透传响应 res.status(upstreamRes.status); Object.keys(upstreamRes.headers).forEach(key { if (key ! connection) res.setHeader(key, upstreamRes.headers[key]); }); upstreamRes.data.pipe(res); } catch (error) { console.error(Upstream error:, error.message); res.status(502).json({ error: Upstream service unavailable }); } }); app.listen(3002, () { console.log( Model Router listening on http://localhost:3002); });4.2 如何接入 DeepSeek绕过 Codex CLI 的原生限制Codex CLI 官方不支持 DeepSeek但通过 OpenRig 的model-router我们可以无缝接入。关键是DeepSeek 的 API 与 OpenAI 兼容只需微调请求体。DeepSeek 的/chat/completions接口要求model字段必须是deepseek-chatmessages格式相同但stream为true时返回的 event 是data: {id:...,object:chat.completion.chunk,choices:[{delta:{content:...},index:0}]}与 OpenAI 完全一致。所以model-router中对 DeepSeek 的处理只需case deepseek: upstreamUrl https://api.deepseek.com/v1/chat/completions; upstreamHeaders { Authorization: Bearer ${process.env.DEEPSEEK_KEY} }; // 重写 model 字段适配 DeepSeek req.body.model deepseek-chat; break;然后在openrig switch deepseek后codex generate --model deepseek就能正常工作。用户完全感知不到底层切换这就是 OpenRig 的抽象价值。4.3 处理 “cli 反代 gemini 显示 403”Google 的 OAuth2 陷阱Gemini 的 API 不同于 OpenAI/DeepSeek它强制要求 OAuth2 认证且Authorization: Bearer token中的 token 必须是 Google 的 access_token而非 API Key。当你看到cli 反代 gemini 显示 403根本原因是Gemini 的/v1beta/models/gemini-pro:generateContentendpoint 拒绝了无效的AuthorizationheaderGoogle 的 access_token 有效期仅 1 小时需定时刷新。解决方案是在model-router中集成 OAuth2 flowconst { google } require(googleapis); // 初始化 Google auth client const authClient new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, process.env.GOOGLE_REDIRECT_URI ); // 刷新 token async function getAccessToken() { const token await authClient.refreshAccessToken(); return token.credentials.access_token; } // Gemini 路由 case gemini-pro: const accessToken await getAccessToken(); upstreamUrl https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent; upstreamHeaders { Authorization: Bearer ${accessToken} }; // Gemini 的 request body 格式不同需转换 req.body { contents: [{ parts: [{ text: messages[0].content }] }], }; break;注意googleapis库体积较大~15MB生产环境建议用google-auth-library替代它只包含 auth 模块。我在npm install google-auth-library8.9.0后model-router的启动时间从 3.2s 降至 0.8s。5. 故障排查全景图从报错信息反向定位根因OpenRig 的日常维护80% 时间花在排查各类报错。我把高频问题整理成一张“报错-根因-验证-修复”四维排查表覆盖从网络层到应用层的所有环节。报错信息最可能根因快速验证命令修复方案internetopenurl() failed. 0x80072f7dWindows 系统 DNS 解析失败常因 IPv6 优先导致nslookup api.codex.ai看是否返回 IPv6 地址在proxy/server.js中https.Agent添加family: 4强制 IPv4codex login: Error: connect ETIMEDOUT本地防火墙拦截了https://auth.codex.aicurl -v https://auth.codex.ai观察 TCP handshake 是否超时配置防火墙放行 auth.cod
返回列表