opencode 的 Zen / Go 订阅走自家网关,请求必须带一个请求头 x-opencode-session(值随便填),不带就直接拒绝:
{"error":{"type":"server_error","message":"MissingSessionID"}}
HTTP 400。问题在于,很多客户端面板只让你填三样东西:接口地址、API Key、模型名,没地方加自定义请求头。于是直连必然 400,而报错信息里根本看不出「少了一个头」,只说 MissingSessionID。
这篇记录一个很小的解法:在本机回环上起一个转发服务,自动补上这个头,其余原样透传。

思路
客户端 ──普通 HTTP──> 127.0.0.1:8788 ──补 x-opencode-session──> https://opencode.ai/zen/go/v1/...
转发器只做三件事:收下请求、补一个头、原样转发回去(含流式 SSE)。不碰请求体、不缓存、不解析内容,所以它不需要也不应该知道你的 Key。
为什么用 PowerShell
Windows 自带 HttpListener(底层是 http.sys)和 Invoke-WebRequest,一个 .ps1 就够,不需要 Node、Python、.NET SDK,也不需要装服务、改注册表。Win7 到 Win11 都能跑(Win7 需要 .NET 4.6.2+ 与 KB3140245 补丁才有 TLS 1.2)。
配置全在一个 config.txt 里:
BIND_HOST=127.0.0.1 # 只允许本机使用
BIND_PORT=8788 # 端口被占用时改这里
UPSTREAM=https://opencode.ai/zen/go # Go 订阅;Zen 订阅改成 https://opencode.ai/zen
SESSION_HEADER=x-opencode-session
SESSION_VALUE=ses_local_forwarder # 值随便
TIMEOUT_SECONDS=600
三步跑起来
- 双击启动脚本,出现黑窗口并打印
started和listen : http://127.0.0.1:8788/即成功。窗口最小化放着,关掉就等于停止。 - 双击自检脚本,看到
RESULT : OK HTTP 200说明服务活着。 - 客户端里填:地址
http://127.0.0.1:8788/v1、Key 填你自己的oc_sk_...、模型 ID 填网关认的名字(如deepseek-v4.1-flash)。

地址那一格最容易错
不同客户端对 Base URL 的处理不一样,三种写法都得知道:
| 客户端行为 | 该填什么 |
|---|---|
自动在后面拼 /chat/completions(多数) |
http://127.0.0.1:8788/v1 |
| 认为地址就是完整接口 | http://127.0.0.1:8788/v1/chat/completions |
自动拼 /v1/chat/completions |
http://127.0.0.1:8788 |
判断依据只有两条:回 404 说明路径拼重了或拼漏了,换一种;回 400 MissingSessionID 说明请求根本没走转发器,地址里还留着 opencode.ai,这是最典型的「以为在用转发器其实在直连」。
报错对照
| 报错 | 真正原因 | 处理 |
|---|---|---|
| 400 MissingSessionID | 地址填的是官方域名 | 改成本机地址 |
| 连接被拒绝 | 转发器没启动,或端口与配置不一致 | 启动它、跑自检 |
| 401 Invalid credential | Key 错/过期,或粘贴时带了空格引号 | 重新复制 |
| 402 Insufficient account funds | 网关走错:Go 的 Key 打到了 Zen(少了 /go),或额度耗尽 |
改回 zen/go,再看额度 |
| 429 FreeUsageLimitError | 免费模型当日额度用完 | 换付费模型或隔天 |
| 一直转圈到超时 | 客户端自身超时偏短,或上游慢 | 把超时提到 900 秒 |
日志按天切分,每行一次请求:
[16:14:21] 19 ms GET /healthz -> 200
[16:20:02] 8841 ms POST /v1/chat/completions -> 200
判断口诀很干净:日志里没有你的请求 = 请求没到转发器;有请求但 4xx = Key/模型/网关问题;有请求且 200 = 转发器这头全程无事。
安全边界
- Key 只存在客户端里。转发器把
Authorization原样往后传,自己不存不打印,日志里只有方法、路径、状态码、耗时——所以整个文件夹和日志都可以直接发给别人排错。 - 默认监听
127.0.0.1,是回环,不是监听网卡,局域网里谁也连不进来,也不需要开防火墙端口。 - 改成
0.0.0.0才允许同网段设备借用这条通道;不要做公网端口映射,真要远程用就走 SSH 隧道或带鉴权的反向代理。
验证到什么程度
| 测试项 | 结果 |
|---|---|
/healthz 健康检查 |
200 |
| 补头后上游不再报 MissingSessionID | 通过 |
| 300 KB 随机二进制请求体,SHA-256 对比 | 字节完全一致 |
| 2 MB 大请求体 | 正常吞吐 |
| 流式 SSE 逐块回吐(上游每 1s 一块) | 首字节 1.0s / 总 3.0s |
| 中文、emoji、大 body 编码 | 无乱码 |
唯一没法替你验证的是「用你的真 Key 拿到 200」——Key 只在你自己手里。
整套东西不到 100 KB,纯文本可审,删掉文件夹即彻底卸载,不写注册表、不装服务、不改环境变量。遇到只能填三格的面板,这类「本机补一个头」的做法比重写客户端划算得多。