
1. OpenCode 本地代理到底在解决什么问题如果你最近在折腾 OpenCode 这类终端里的 Agent 工具大概率会遇到一个很具体的场景工具本身支持自定义模型通道但你想让它走一个统一的 Key 和 API 入口而不是把各家厂商的 Key 散落在配置文件里。OpenCode 的本地代理local proxy就是干这个的——它在你的机器上起一个轻量 HTTP 服务把 OpenCode 发出来的请求接住再按你定义的options转发到真正的上游。这篇聚焦的是options配置项。很多人第一次看代理代码会被hostname、port、path、method、headers这几个字段绕晕尤其是path为什么不能带域名、Content-Length为什么要手动算。我试过把这几个字段拆开逐个改观察请求到底发去了哪里才真正理解它们的分工。下面我会先讲清楚options每个字段的作用再给出一份可复制的config.toml骨架和settings.json片段最后用一次本地代理请求验证配置是否生效。适合谁看已经在用 OpenCode、想接入统一 Key/API 通道的开发者或者你只是想搞明白 Node.jshttps.request的options对象到底怎么填。读完你能自己改代理的目标地址、路径和鉴权头并且知道改错了会报什么错。2. 接入前的准备TaoToken 的 Key 与通道在动options之前先把上游通道确定下来。TaoToken 提供统一的 API 入口你只需要一个 Key就能在 OpenCode 里通过本地代理转发请求不用在每个工具里分别配不同厂商的地址。你需要做两件事拿到 API Key以及确认接入文档里的 Base URL 和路径规则。Key 在控制台的 API Keys 页面创建创建后复制保存后面会写进settings.json或环境变量。接入文档里会说明兼容模式的路径前缀这个前缀直接决定你options.path怎么写。注意Key 不要硬编码进代理源码里。代理的设计意图是透传客户端带来的Authorization头所以 Key 应该放在 OpenCode 侧的配置或环境变量中代理只负责转发。相关入口我放在这里按需取用创建和管理 Keyhttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档路径与兼容模式说明https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite想先在网页里验证模型是否通https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite如果你打算长期在 OpenCode 里跑编码任务或 Agent 流程可以看下 Coding Plan额度模型更适合高频调用https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite3. options 配置项逐个拆解options是传给 Node.jshttps.request的配置对象。它回答四个问题发给谁、发到哪个路径、用什么方法、带什么头。下面逐个说。3.1 hostname 与 port目标服务器定位hostname是目标服务器的主机名只写域名不能带协议。比如写dashscope.aliyuncs.com是对的写https://dashscope.aliyuncs.com会直接报错因为https.request本身已经隐含了 HTTPS协议部分由模块处理。port是目标端口HTTPS 默认 443。省略port时https.request也会用 443但显式写出来更清晰尤其在你要切换到非标准端口做本地联调时写出来不容易漏。const options { hostname: dashscope.aliyuncs.com, port: 443, path: /compatible-mode/v1/chat/completions, method: POST, headers: { Authorization: authHeader, Content-Type: application/json, Content-Length: Buffer.byteLength(body) } };3.2 path请求路径不含域名和协议path是最容易写错的一个。它只包含 URL 里域名之后的部分比如/compatible-mode/v1/chat/completions。OpenCode 插件发出来的是POST /v1/chat/completions代理要做的事情之一就是把路径改写成上游兼容模式的路径。这就是代理存在的意义客户端不用知道上游的真实路径代理帮你映射。如果你把path写成完整 URLhttps.request不会报语法错但请求会发到错误的位置通常表现为 404 或连接异常。排查时先打印options.path确认。3.3 method 与 headers方法与鉴权透传method用POST因为聊天请求要把消息内容放在 body 里GET 无法携带 body。行业惯例也是 POST。headers里三个字段各有分工。Authorization从客户端请求头透传过来代理不做硬编码这样 Key 的归属清晰换 Key 只改客户端配置。Content-Type固定application/json告诉上游 body 是 JSON。Content-Length用Buffer.byteLength(body)计算因为 body 是字符串字符数和字节数在含中文时不一致必须按字节算否则上游可能截断或报长度不匹配。const authHeader req.headers[authorization] || ;这行是防御性写法。如果客户端没带Authorizationreq.headers[authorization]是undefined加|| 兜底成空字符串避免后面拼头时出现undefined字面量。Bearer sk-xxxxxx里的Bearer表示持有者令牌是标准的鉴权方案前缀。4. 可复制的 config.toml 与 settings.json下面给一份能直接改的骨架。config.toml放在 OpenCode 的配置目录settings.json片段用于声明模型通道。# config.toml [proxy] enabled true host 127.0.0.1 port 8787 upstream_hostname dashscope.aliyuncs.com upstream_port 443 upstream_path /compatible-mode/v1/chat/completions timeout_ms 60000 [model] provider openai-compatible base_url http://127.0.0.1:8787/v1{ models: { taotoken-agent: { provider: openai-compatible, baseUrl: http://127.0.0.1:8787/v1, apiKeyEnv: TAOTOKEN_API_KEY, options: { hostname: dashscope.aliyuncs.com, port: 443, path: /compatible-mode/v1/chat/completions, method: POST } } } }把 Key 放进环境变量避免写进文件export TAOTOKEN_API_KEYsk-你的Key代理启动后监听127.0.0.1:8787OpenCode 把请求发到本地代理按options转发到上游。base_url指向本地代理options里的hostname和path指向真实上游两者分工明确。5. 验证请求一次本地代理调用配置改完别急着跑完整 Agent先用一条 curl 验证代理链路通不通。curl -sS http://127.0.0.1:8787/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: qwen-plus, messages: [{role: user, content: 只回复 ok}] }预期返回里能看到choices数组message.content是ok。如果返回 401说明Authorization没透传成功检查代理里authHeader的读取和headers拼装。如果返回 404多半是path写错对照接入文档确认兼容模式前缀。如果返回 400 且提示长度问题检查Content-Length是否用了Buffer.byteLength。代理侧加一行日志把实际发出的options打出来联调时非常有用console.log(proxy -, options.hostname options.path, options.method);看到日志里路径和上游一致且 curl 返回正常就说明options配置生效了。这时候再回到 OpenCode 里跑一次真实对话确认端到端可用。6. 本篇常见错排查报错getaddrinfo ENOTFOUNDhostname写错或带了协议。只写域名不带https://。报错Cannot set headers after they are sent代理里对同一个响应重复调用了写头或写 body检查end事件里是否只处理一次。返回 401 UnauthorizedAuthorization头没透传或 Key 失效。先确认客户端请求头里有Bearer再确认 Key 在控制台有效。返回 404 Not Foundpath与上游不匹配。对照接入文档的兼容模式路径注意前缀。中文内容被截断Content-Length用了body.length而不是Buffer.byteLength(body)中文字符字节数大于字符数。代理启动但 OpenCode 连不上base_url端口与代理监听端口不一致或代理只监听了127.0.0.1而 OpenCode 在容器里跑。确认网络可达。排障时优先看代理日志里打印的options再对照 curl 的返回码基本能定位到是路径、鉴权还是长度问题。接入相关的 Key 和文档入口https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite7. 把通道固定下来后续只改配置options这几个字段一旦调通后面换模型、换上游基本只改hostname和path代理逻辑不用动。我的习惯是把options抽成一个函数入参是上游配置返回拼好的对象这样联调时改一处就够。如果你还在选长期方案OpenCode 这类 Agent 工具调用频率高Coding Plan 的额度模型更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite想先在网页里确认模型通不通再回来配代理用模型对话页最快https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite控制台里可以统一管理 Key 和用量https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content