opencode2pi0.5.0
OpenCode Zen's anonymous free models (no API key) as a native omp/pi provider extension.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
opencode2pi
在 omp / pi 里直接用 OpenCode Zen 的匿名免费模型。
无需 API key · 无需注册 · 无需登录 · 无需子进程 · 无需本地端口
⚠️ 这是匿名免费通道,上游随时可能改闸门或直接关停。 它不依赖任何凭据,所以上游一旦调整请求形状校验,本扩展会全线 403。 出问题先跑
/opencode2pi doctor,它会直接告诉你闸门还在不在。
安装
# 推荐:npm(已发布,自动带 provenance)
omp install npm:opencode2pi
pi install npm:opencode2pi
# 备用:git @stable(最近一次发版)
omp install git:https://github.com/FanchangWang/opencode2pi@stable
pi install git:https://github.com/FanchangWang/opencode2pi@stable
# 追踪最新代码:装的是"此刻 main 指向的 commit",含未发布的改动,之后不会自动更新
omp install git:https://github.com/FanchangWang/opencode2pi@main
pi install git:https://github.com/FanchangWang/opencode2pi@main
为什么 npm 优先:git 装的是源码快照,宿主要现读现编译;npm 装的是已发布的
tarball,带 provenance 签名(可在 npmjs.com 包页面核验),且 tag 与版本号一一对应。
注意 omp/pi 不支持直接装 .tgz 文件(实测报 package.json not found),
所以只能用 npm spec 或 git URL。
@stable 是最近一次发版的版本,随发版前移;要固定到某一版,去
releases 挑一个 tag,
把 @stable 换成那个 tag 名或 commit SHA。上游随时可能改闸门,只有钉死才能复现"当时能用"的状态。
@latest 用不了:git 没有这个 ref,omp 解析会报错。
从源码运行(开发用)
git clone https://github.com/FanchangWang/opencode2pi.git
cd opencode2pi
# 链接当前目录:改代码立即生效,不用重装
omp install .
# 单次试用,不落盘
omp -e ./src/index.ts -p --model opencode-zen-free/big-pickle "你好"
pi 侧对应命令(同一个扩展,两个宿主都支持):
pi install .
pi -p --model opencode-zen-free/big-pickle "你好"
pi -e ./src/index.ts -p --model opencode-zen-free/big-pickle "你好" # 单次试用
omp install . / pi install . 是链接而非拷贝:装的是路径,所以改 src/ 下的代码下次启动就生效。
代价是删掉或移动这个目录,扩展就失效了 —— 所以本地开发用它,给别人用请走上面的 npm 或 git。
不要只复制
src/index.ts。扩展是多文件模块,index.ts依赖同目录的其它模块。 要手动常驻就整目录复制:cp -r src ~/.omp/agent/extensions/opencode2pi。
使用
选模型
会话里用 /model 选择,provider 名是 opencode-zen-free。
命令行要用 --model <provider>/<model>,不要用 --provider:
# 对
omp -p --model opencode-zen-free/big-pickle "你好"
# 错,会报 Unknown provider
omp --provider opencode-zen-free
扩展注册的 provider 在参数校验阶段还不可见,所以
--provider一定失败。 同理omp models不列出这个 provider,查清单请用会话内的/model或/opencode2pi status。
模型列表是自动发现的
免费池轮换频繁,所以列表不是写死的,而是实时发现 + 元数据判定:
| 级 | 来源 | 作用 |
|---|---|---|
| S1 | GET {zen}/v1/models |
谁现在真的在售 |
| S2 | models.dev | 免费判定 + 真实能力参数 |
| S3 | 内置静态种子 | 完全离线时的兜底 |
实测当前发现 7 个 免费模型(2026-10-11 实测后从 13 个减下来,恒定被拒的
id 已进 denylist,见 docs/FINDINGS.md §2.2)。付费模型不会进列表;断网也能用
(退回内置种子,能力参数不会变成错误的默认值)。
图片输入
能否附加图片取决于模型本身。把图片作为消息的一部分传入即可:
omp -p "@photo.png" "这张图是什么颜色?"
只支持文本的模型会收到明确的拒绝提示,而不是模糊的 400。
能力参数
上下文长度、最大输出、计价、reasoning effort 都取自 models.dev 的真实数据, 不是拍脑袋的默认值。某个字段上游没给时,会落到保守默认值并写进日志, 而不是悄悄用一个错的数字。
/opencode2pi
omp 没有扩展设置页,斜杠命令是本扩展唯一的界面。
| 命令 | 作用 |
|---|---|
/opencode2pi doctor |
检查形状闸门是否仍然成立。上游一改闸门,这里立刻告警 |
/opencode2pi status |
显示上次探测的结果;从未探测过的模型(含新发现的)会当场补测 |
/opencode2pi probe |
重新探测全部模型 |
/opencode2pi filter |
选择要在 /model 里隐藏哪些模型(默认一个都不隐藏) |
/opencode2pi pool |
出口池:换一个出口 IP(本机配额用尽、或地区封锁时用) |
不带参数执行会弹出八项菜单。
配额用尽时换出口 IP(出口池)
匿名通道按出口 IP 记每日额度。额度用完时上游对所有请求回 429,这时换一个出口 (代理)就能继续用 —— 但换不换由你决定,扩展不会自己转:出口可能更慢、可能看到你的 请求,匿名通道上乱重试还会把自己的额度挤掉。
出口池的开关跨会话保留。因此当当前选中的模型属于本 provider 且出口池开着时,进入 TUI 会在会话里提示当前出口(含出口 IP);如果开着却没有有效出口,或运行时没有可用的代理机制, 提示会明确说请求将从本机 IP 发出。这只在启动时提示一次,不改变任何请求路径;关闭出口池 后不再出现。
/opencode2pi pool 的菜单:
| 项 | 做什么 |
|---|---|
| 开启 / 关闭出口池 | 关着的时候请求完全按原样发出(本机 IP 或你原来的 PI_PROXY),选择保留着。开启时如果还没有选中的出口,会先问一条路:选择已扫描的内置出口、自动扫描,还是从 exits.txt 里选 / 手动输入 |
| 选择自定义出口 | 你自己那几行 exits.txt(菜单里按位置叫「自定义池 1/2/3…」);有 30 分钟内的完成验证就直接展示并询问,否则先跑闸门探测和一次真实回答。也可以就地手动添加一行 |
| 选择内置出口 | 扫描已经测通的出口,随时可以回来选,不必在扫描当时决定。只列当前证据仍可用的结果;刚验证过的直接询问是否切换,过期的会重新验证 |
| 自动扫描内置出口池 | 先按证据顺序并发探测池里已有的出口;一个都不行才去拉公开免费代理列表。每屏测通过一个就立刻做闸门探测,闸门通过后再要求它真的回完一个短回答;找到可用出口后列出结果让你选,不自动切换 |
| 删除自定义出口 | 从 exits.txt 移除一行 |
| 设置免费源 | 逐项开 / 关内置的 18 个公开免费代理列表来源;停用的源下载和缓存都会跳过 |
| 清空内置结果 | 删掉内置出口的探测结果与隔离记录,保留 exits.txt、当前选择和源设置 |
| 查看当前状态 | 打印完整出口列表与每条结论,不做任何改动 |
「自动扫描内置出口池」是一个命令而不是两个,因为「补充」和「选哪个」本来就是同一个决策的两半:
屏检只说明代理能转发 TLS,补充完你手里依然没有一个能直接用的出口。先试已有的再拉源,
是因为健康池子几秒就有结果,而无条件拉源要付出一次完整下载 + 几百次 trace 的等待。
扫描当时没选也不要紧:测通的出口都留在池里,扫描结束和「选择内置出口」用的是同一张列表,
30 分钟内刚验证过的不会重复消耗配额。两个入口也挂在「开启出口池」里,所以 exits.txt
是空的时候不必先去主菜单找它们。
池里存的是闸门探测 + 完成探测都通过的出口,不是「屏测通过」的地址。所以:
- 手动跑一次通常会攒到 3 个完成可用的出口,外加并发探测里同时通过的另外几个(一并入池)。
- 下次再跑,先探上次那批有可用证据的,全挂了才去拉新源 —— 备用靠多次运行累积, 不是靠一次跑囤满 12 个。
- 闸门 200 只说明上游接受了这次请求的形状;一个代理可以在返回 200 之后于第一个 token 前
死掉,所以每个候选还要回完一次
max_tokens: 24的真实回答才算 ✅(见下表)。 - 池里的地址如果闸门或完成探测失败(连不通 / 地区被拒 / 形状被拒 / 流中断),会被移出池并 隔离 24 小时。429(限流)不隔离、不隐藏 —— 配额窗口会滚。
- 没找到就继续问你:一轮跑完还没有可用出口,它会报出「还剩 N 个地址没测」并问你要不要 继续。你说继续就再来一轮(18 个源合并打乱后整体走一遍),一直找到、你说停、或者源被测干为止。 结束时也会告诉你还剩多少 —— 所以「没找到」和「它放弃了」不会长得一样。
- 源列表本地缓存 1 小时:18 个源合并下载一次就存到数据目录(
free-sources.json), 一小时内再跑直接复用、不联网重拉。缓存过的源会在结果里标出来。
出口分两层:
- 手动出口:数据目录里的
exits.txt,一行一个,你自己的代理。格式host:port或http://user:pass@host:port(只支持 http/https;socks 在 Bun 的 fetch 上 实测不可用)。凭据只留在这个文件里,pool.json里只有哈希。 - 内置出口:从公开免费列表拉取。闸门与完成探测都通过才入池并标记 ✅ —— 它们是 第三方代理,只在必要时用。
两种测量,因为问题不同:
| 测量 | 走什么请求 | 花费 | 回答什么 |
|---|---|---|---|
| 预筛 | 向代理发 CONNECT cloudflare.com:443 |
0 配额,一次 TCP | 这个地址的端口真的开了隧道吗(不开隧道的死地址不进屏检) |
| 屏检 | cloudflare /cdn-cgi/trace |
0 配额 | 这个代理通不通、出口 IP 与地区、延迟分级(S/A/B/C,超过 3s 不算可用) |
| 闸门探测 | 真实形状的最小请求 | 少量配额 | 上游匿名通道接受这个出口吗(429 / 地区封锁 / 链路失败) |
| 完成探测 | 真实形状 + max_tokens: 24 的流式回答 |
一次短回答的配额 | 回答能完整走完这个出口吗(流中断 / 200 里的形状错误 / 回答不是 OK) |
一次发现最多跑 目标 × 4 次完成探测(默认 3 × 4 = 12)。预算按尝试次数计, 429 也花掉一次但不会被隔离 —— 配额窗口滚动后同一个出口还值得再试。
429 和地区封锁不等于坏出口:配额按模型记账,同一个出口通常还能跑别的模型。所以真实
请求失败时,扩展只在该出口上记一笔(429/地区拒绝,按模型),下次打开 pool 就能看到
实际发生了什么 —— 它不会替你换出口。
健康标记
| 标记 | 含义 |
|---|---|
| ✅ | 可用 |
| ⚠️ | 现在不行(5xx、400、无法归类的错误),不代表模型没了 |
| 🚧 | 配额受限(429 / transport 超时)。这是通道的匿名额度用完了,不是模型的问题,等一会儿会自动恢复 |
| ❌ | 确定死了(地区封锁,或连续两次 ModelError) |
| ❓ | 闸门或凭据被拒 —— 全 roster 的问题,跑 doctor |
每行末尾直接带上原因(HTTP 状态与上游原文)和探测时间,所以不存在
「这个模型到底怎么回事」需要另外去查的情况。超过 6 小时的结果标 (已过期),
并提示你去跑 probe —— status 不会替你重探。
默认只标注、不隐藏。 这条通道波动很大,单次失败不足以判死刑 ——
实测有模型一分钟内先 401 ModelError、再 200,所以一次 ModelError 只标 ⚠️,
连续两次才升级 ❌,任何一次成功都会清零。
想让失败的模型不出现在 /model 里
/opencode2pi filter(或每次 probe 结束后自动询问)可以隐藏地区封锁的、以及所有
探测失败的模型。这个选择记在数据目录的 filters.json 里,重启后仍然有效;
随时可以改回「全部保留」。
配额耗尽和链路故障不会隐藏任何模型:那是通道的状态,不是模型的。被隐藏的只是
不进 /model 与 --model,status 里照样能看到全部。
排障
先跑 doctor
大部分「全都不能用」的情况,先跑 /opencode2pi doctor。它会直接告诉你闸门还在不在。
错误对照表
扩展会把上游错误翻译成人能看懂的原因:
| 你看到的 | 归类 | 原因 / 处理 |
|---|---|---|
上游形状闸门拒绝(FreeTierError) |
SHAPE_REJECTED |
闸门条件被破坏,通常是代码回归 —— 提 issue |
该模型在当前网络地区不可用(RegionError) |
REGION_BLOCKED |
地区封锁,不是凭据问题。换模型或换网络节点 |
模型已下线(ModelError) |
MODEL_GONE |
上游已不再支持该模型 |
匿名配额按出口 IP 限流(429) |
RATE_LIMIT |
等窗口滚动,或 /opencode2pi pool 换一个出口 |
上游拒绝了请求(400) |
REQUEST_REJECTED |
模型无法处理这次输入,最常见是给只支持文本的模型发图片 |
上游暂时不可用(5xx) |
UPSTREAM |
上游临时故障,换模型重试 |
其它常见问题
| 现象 | 处理 |
|---|---|
Unknown provider "opencode-zen-free" |
用了 --provider。改用 --model opencode-zen-free/<id> |
omp models 查不到这个 provider |
已知行为(实测),用会话内 /model 或 /opencode2pi status |
| 断网了还能用吗 | 能。退回内置种子,能力参数是正确的实测值 |
它做了什么、没做什么
宿主内建的 opencode-zen provider 需要付费 OPENCODE_API_KEY;本扩展走的是 Zen 的
匿名通道(Authorization: Bearer public),拿到的是那一批免费模型。
流式引擎、tool call 分片聚合、reasoning、图片 modality、模型选择器 —— 全部由
omp/pi 内建,本扩展不重复实现。它只补上匿名通道要求的那部分请求形状,
其余全部委托给宿主。细节见 docs/DEVELOPMENT.md。
不做:付费 Zen 通道、OpenAI/Anthropic 协议互转、子进程、自动换出口 (出口池是显式开关 + 手动选择;换与不换由你决定,见上文)。
文档
| 文档 | 内容 |
|---|---|
docs/DEVELOPMENT.md |
架构、闸门条件、宿主 API 实测行为、模块说明、验证与发布流程 |
docs/FINDINGS.md |
实测结论存档:闸门精确条件、宿主扩展 API 的真实行为、同类扩展为什么都不能用 |
docs/PLAN.md |
任务清单与验收标准 |
AGENTS.md |
给接手的 AI:项目地图、验证命令、别踩的坑 |
致谢
灵感与闸门条件的逆向,来自 FishBottle7/opencode2dsh
(DSH 版的同类工具,其 legacy/ 里的 Go sidecar 源自 opencode2api)。
许可证
MIT