zen-free 是这个插件的前身。先说清楚:上游一直在维护,修 Bug 也很快。问题在于它对免费模型支持这件事,始终缺少一个稳定、明确的态度
免费模型失效时,用户反馈issue,得到的答复却是将不再支持;过一段时间,相关问题又被悄悄修复,免费模型重新恢复可用。支持、放弃、再支持——用户只能跟着上游的态度反复折腾,却始终无法确定这份支持究竟能持续多久
既然有人依赖它,就值得一个明确的答案,而不是反复摇摆,这就是本项目诞生的原因
| UTC | 提交 | 上游说了什么 |
|---|---|---|
| 2026-10-13 23:53 | 643ef50 |
README 加上说明:免费层「只在 OpenCode 本体里能用」,this plugin doesn't work around that,请改用 Zen 付费 key 或 OpenCode Go;提交说明里写着 No version bump … The code on main is left as it is |
| 2026-10-14 10:57 | a810ee7 |
0.1.6 把那段说明删掉,改为发 OpenCode 1.19.0 原样的请求头——距加上去 11 小时 |
| 2026-10-14 17:59 | 5fc0a75 |
0.1.7:free models answer again |
上游的后续更新会按需合并,同时保留自己的取舍(比如工具措辞按代从真实 OpenCode 采集,而不是钉死某一个版本)
magpie plugin add opencode-zen-freex
magpie plugin login opencode-zen-free密钥填 public。这是 Zen 免费层的共享凭据,不需要个人 API key——填别的也会被丢掉。
个人 key 请加到 magpie 内置的 OpenCode Zen provider,那里支持多把 key 的轮换与故障转移。凭据保存在 OpenCode 的 auth.json 或 magpie 的 plugin-auth.json。
模型名写作 opencode-zen-free/<id>,例如 opencode-zen-free/big-pickle。
- 自动发现:宿主列举模型时读取 Zen 的列表,60 秒内的重复列举复用上一次结果。读到的列表会写进缓存(
~/.cache/opencode-zen-freex/discovery.json,Windows 在%LOCALAPPDATA%下);列表读不到时由缓存或包内旧表顶替,有缓存时把数据的年龄一并告知宿主。 - 上限按实测:目录、厂商自述与路由实际执行的墙常有出入。上下文按实测能过的最大值对齐,输出优先取同一网关付费孪生的声明。例如
mimo-v2.6-flash-free目录写 20 万,实测 37.5 万;glm-5.3-flash-free的付费孪生声明 100 万,实测 32.3 万;路由自己否掉的上限也照认,deepseek-v4.1-flash-free的 100 万因此收紧到 32.6 万。依据与复测方法见MAINTAINING.md的 “Limits Zen under-reports”。 - 上限还会从流量里学:插件不额外发请求,只用自己转发的请求向上试探、向下收敛;学到的值重启后仍在,被拒的模型会自动收回到已经证明能过的尺寸。
- 实测快照不用等发版:仓库每小时做一次增量实测,写入
limits-observed.json并发布到main;插件启动后每小时最多后台拉取一次,读不到就用缓存或包内副本。 - 列表说了算:Zen 列表里的
-free模型立即提供(本插件不支持的协议除外),不等目录补齐;目录读不到只影响它描述的那部分,不代表列表是回退结果。 - 不再免费即 404:错误会列出当时可用的免费模型;模型重新免费后,刷新列表即可。可用性与限流归 Zen 管,插件看不到剩余额度。
请求发往 https://opencode.ai/inference/v1,按每个模型的原生协议走(Chat Completions / Responses / Anthropic Messages),流式与普通 JSON 都支持。
- 只借 OpenCode 的外壳:请求头、User-Agent 与工具集合按同一代 OpenCode 配套,宿主缺的
required工具会补发描述(背后没有实现)。请求头是刻意挑出的最小集合——不要自行添加,两个各自无害的头组合起来也会被拒。 - 工具名对不上就原样交给宿主:先精确匹配,再忽略大小写,最后忽略
_/-/./空白,只取唯一结果;对不上或有歧义时不猜、不丢,由宿主回自己的「没有这个工具」,模型据此改名重试。只有没有名字的调用会被略去,并在正文说明。 - 会话保持稳定:宿主给了 OpenCode 形状的会话 id 就原样传,否则从宿主的会话标识派生并记住,同一会话各轮共用一个 id。
- 截断与错误如实报告:流提前结束时,Chat 流若有内容且无未完成调用,按截断回答收尾并注明;其余情况一律失败。错误正文只读一小段找 Zen 自陈的上限,其余替换为说明。
请求头与会话 id 的完整规则
请求头只有这些:content-type、accept、user-agent、x-opencode-project、x-opencode-session 与 x-opencode-session-id(同一个值)、x-opencode-client: cli、authorization: Bearer public;走 Messages 时改用 x-api-key: public 加 anthropic-version。anthropic-beta 插件自己从不设置,宿主发了才原样透传。
会话 id 按顺序取:会话头 x-opencode-session / x-opencode-session-id / x-session-affinity / x-session-id / session_id / conversation-id / conversation_id,body 的 conversation_id,metadata.session_id,或 Claude Code 的 metadata.user_id 里的 _session_<uuid>。都没有时用 system/instructions 与首条用户消息做种子(system 变了就是另一个会话);再没有才看 previous_response_id,仍然没有则每次新生成。空字符串一律不算名字。
插件给出的 403 会写明措辞采集自哪个 OpenCode 版本,先从它查起。下面三条都只在仓库副本里可跑(check:gate 与 capture 不在发布包里),需要 Bun;capture 还需要 PATH 里有 opencode:
bun run check:gate # 问 Zen 还认哪代指纹;只要有一代 floor 被应答,退出码为 0
bun run capture -- --dry # floor 全被拒时:取新采集并报告差异,不写入
bun run capture # 确认差异后才写入fingerprint.json 缺失或损坏时,capture 会重写一份而不是拒绝。闸门并不稳定:结论只代表此刻,信之前再跑一遍;同一地址反复被拒后还可能进冷却期。逐代结论与失败判读见 MAINTAINING.md 的 “The loop” 与 “Reading a failure”。
| 现象 | 处理 |
|---|---|
每个请求都是 403 FreeTierError,正文没有 IP 封禁文案 |
指纹措辞过期。在仓库副本里跑 bun run check:gate;全被拒时跑 bun run capture |
403 正文是封禁文案(abuse of traffic,或 free models aren't available … from your current network) |
免费层封的是当前出口 IP。换网络、换 VPN 出口,或等封禁解除;重试与改请求都无用 |
426 Upgrade Required |
这个包自称的 OpenCode 版本比 Zen 要的旧。升级插件,或在更新的 OpenCode 里运行 |
404 … not available as an OpenCode Zen free model now |
该模型已不再免费。从报错列出的模型里换一个 |
Model is not an available free model for this protocol |
请求发出时列表变了。让宿主刷新模型列表再试 |
uses the public credential |
存的密钥不是 public。用 public 重新登录 |
不带 FreeTierError 的 403 |
是 Zen 自己的 403,与本插件无关;按原文处理 |
400 context_length_exceeded,或 max_tokens 超出范围 |
路由按模型自己的上限校验。压缩对话或调小 max_tokens;上下文拒绝不报窗口数字,max_tokens 拒绝会给出允许范围 |
| 窗口偏小想重学,或新设的环境变量没生效 | 删掉缓存目录下的 opencode-zen-freex/limits.json(目录规则见环境变量);环境变量只在宿主启动时读,改完要重启 |
| 模型被问错了协议 | 协议文档的端点表没读出来。设 OPENCODE_ZEN_FREE_MODELS_DEBUG=1 看是哪份文档、为什么没读出来 |
| 想确认快照是否新鲜、模型是否下线 | 在仓库副本里跑 bun limits-watch.mjs --status;退出码 0 正常、2 已读但有问题、1 读不到。unavailable 只是历史,不是停用开关 |
窗口停在旧值,或连不上 raw.githubusercontent.com |
远程快照只是优化,失败不影响请求。用环境变量换源或关闭(见环境变量) |
最常用的几个;完整清单与默认值见 .env.example。缓存目录在 Windows 上是 %LOCALAPPDATA%,其余平台是 $XDG_CACHE_HOME 或 ~/.cache。
| 变量 | 作用 |
|---|---|
OPENCODE_ZEN_FREE_DISCOVERY_CACHE |
模型发现缓存路径;空字符串表示只在内存中 |
OPENCODE_ZEN_FREE_LIMITS_SNAPSHOT |
实测快照路径;空字符串表示不读快照 |
OPENCODE_ZEN_FREE_LIMITS_SNAPSHOT_URL |
远程快照地址;空字符串关闭远程 |
OPENCODE_ZEN_FREE_LIMITS_STATE |
学习到的上限状态路径;空字符串表示只在内存中 |
OPENCODE_ZEN_FREE_LIMITS_DEBUG |
1 时把抬高、被拒与路由自报的窗口打到 stderr |
OPENCODE_ZEN_FREE_MODELS_DEBUG |
1 时打印模型目录的解析细节 |
OPENCODE_ZEN_FREE_MAX_WINDOW |
向上试探的窗口上限,默认 1048576 |
| 类别 | 项目 |
|---|---|
| 本插件前身 | zen-free |
| 插件载体 | magpie |
| 模型提供者 | anomalyco/opencode |
| 本仓库常用 Agent | deepseek-harness · hermes-agent |
| 灵感来源 | 9router · opencode2api · zen-gate |
MIT。分叉自 zen-free,LICENSE 正文原样未改:版权行同时保留上游作者 xiaozhou26 与本分支的 OpenCode-Zen-FreeX contributors。来源与维护缘由见 NOTICE。