Repository navigation
Releases: Userluckytian/opencode2api_enhance
Release list
opencode2api v1.7.6-beta
修复「工具 schema 里名为
title的属性被宿主当注解删掉」导致严格校验的上游 400:
表现为同网关下qw2api/qwork-advanced恒 502、而qwork-auto正常。
🐛 修复
- 工具 schema 的属性名被误当注解删除(
qw2api/qwork-advanced恒 502 的真因):
core/protocol/claude_convert.go的CleanJSONSchema(Anthropic 入口转换工具声明时
剔掉$schema/title/examples等注解键)原按「键名等于这三个之一就丢弃」递归过滤,
无法区分「schema 关键字title」与「调用方恰好把属性命名为title」——于是
properties.title被整段删掉,而兄弟节点required里的"title"原样保留,
一个自洽的 schema 被改造成「required引用了不存在属性」的非法 schema。
严格校验的上游(实测 QwenWorkqwork-advanced后端)对此直接 400 拒掉整条请求,
宽松后端(同厂商qwork-auto)容忍 —— 故表面现象是「换个模型就好」,极易误判为上游限流。
现改为区分「schema 关键字位置」与「名字容器位置」(properties/patternProperties/
$defs/definitions/dependentSchemas/dependencies):名字容器内的键一律保留、
只清洗其值(值才是 schema),仅在关键字位置剔除注解键。注解title仍照旧剔除、
additionalProperties/format等约束键仍保留、仍返回副本不改入参。
该函数同时服务vendors/custom/{proto_anthropic,proto_gemini,proto_responses}.go的出站声明,
三处一并受益。
Full Changelog: v1.7.6...v1.7.6-beta
opencode2api v1.7.6
修复 opencode 免费档对话全部失败(403 FreeTierError):按上游 2026-10-14 的新契约迁移到
/inference/v1,并复刻 v2 客户端的身份头与工具指纹。
🐛 修复
- opencode 免费档恢复可用(403 FreeTierError):上游 2026-10-14 把 keyless 免费推理从
/zen/v1/*迁到/inference/v1/*——旧路径对免费档任何形态的请求一律回 403——同时收紧
身份与请求体契约。本版按新契约对齐四处:① 端点迁到/inference/v1/chat/completions与
/inference/v1/responses;② UA 改为官方 v2 形态opencode/<channel>/<version>/<client>
(发布线 channel 取prod,低于 2.0.0 的探测值一律回落 2.0.29 兜底);③ 身份头补齐四个同值
会话头(x-opencode-session/x-opencode-session-id/x-session-affinity/x-session-id),
并停止发送已被上游拒绝的x-opencode-request;④ 免费档 body 门禁工具改为shell/glob/grep/read
并补上真实参数 schema(空 schema 的占位定义会被判为非官方客户端)与prompt_cache_key。
带 key 的 zen / go 通道请求形态保持不变。
Full Changelog: v1.7.5-beta.9...v1.7.6
opencode2api v1.7.5-beta.9
修复打包版插件卡片球形图标(面板入口)点击恒定失败(宿主代开浏览器依赖 PATH 解析
cmd)。
🐛 修复
- 插件面板入口恒定失败 + 提示语误导(打包版):
open_url.go用exec.Command("cmd", "/c", "start", …)调系统浏览器,有两个问题:① 按名字 exec 只查 PATH,宿主进程的 PATH
不保证含%SystemRoot%\system32,本机实测直接exec: "cmd": executable file not found in %PATH%—— 插件卡片 🌐 点开必然失败;② URL 里的&被 cmd 当命令分隔符,
gateway/plugin/key/models参数会被整段截断。现改为shell32!ShellExecuteW(API 调用,
不做命令行拼接,也不依赖 PATH);非 Windows 平台改为先LookPath再启动。
前端openPluginPanel同时不再吞掉 core 返回的error——此前只弹一个 URL,用户无从
排查。回归测试core/manager/open_url_test.go覆盖:完整 URL 原样透传(含&)、
底层错误可透出、非 http/非回环/换行/query 裸引号被拒、特殊字符编码、方法校验。
Full Changelog: v1.7.5-beta.8...v1.7.5-beta.9
opencode2api v1.7.5-beta.8
两项改动:① opencode 免费通道身份串前缀改为与官方客户端一致的时间戳编码,解决今天403问题;
② 完整移除 windsurf 厂商(含其账号池契约)。另修一处版本号漂移。
🐛 修复
- opencode 身份串前缀补上时间语义:上游自 opencode 1.19.0 起会把
x-opencode-session/x-opencode-request的 12 位十六进制前缀解码为标识符时间戳
(会话~(ms*0x1000+counter)降序、请求ms*0x1000+counter升序,均取低 48 位)。
本项目原实现用crypto/rand随机 hex 充当前缀 —— 只满足正则、不含时间语义,属与官方
格式的确凿偏差。现改为真时间戳编码,并复刻官方「同毫秒自增计数器」(会话/请求各持一个)。
已用官方 OpenCode CLI 1.18.14 的真实请求头交叉验证:官方同款前缀同样以ffe/001
结尾,编码逐位同构。 - 移除 windsurf 厂商:整目录(含
connect/子包)、自注册 blank import、内建厂商物化
列表、contract.PoolVendor契约、自定义源 id 冲突守卫一并删除,测试/配置/示例/文档同步
清理,源码无功能性残留。老配置里若仍写windsurf:告警日志 + 静默跳过 + 请求回落
opencode,不 panic、不启动失败。 - 修
src-tauri/tauri.conf.json版本号漂移:此前停留在1.7.5-beta.6,与本版对齐。
Full Changelog: v1.7.5-beta.7...v1.7.5-beta.8
opencode2api v1.7.5-beta.7
适配上游免费通道版本下限上调至 1.18.0,并将客户端版本改为可配置。
🐛 修复
- 免费模型全量 426 Upgrade Required:上游把免费通道的最低客户端版本从 1.17.0 抬到
1.18.0,报文「OpenCode 1.18.0 or newer is required to use the free tier」。
根因不在本地校验而在兜底值——versionDef原先直接等于minFreeTierVersion,而版本探测
走代理池出口访问 npm,失败即回落该值(日志里 session 初始化版本在 1.18.32 探测成功与
1.17.0 回落兜底之间反复跳)。兜底值贴着下限,上游每抬一次下限就会在探测失败的窗口里集体
426。上游只做「版本 ≥ 下限」的单向比较,实测 1.18.0 / 1.18.35 / 1.99.0 均 200、1.17.0 才
426,故versionDef与下限解耦、独立取 1.99.0:探测成功时报真实版本,失败时报 1.99.0,
两条路都在门禁之上,使探测失败不再是可用性单点 - 客户端版本支持配置:新增
providers[opencode].params.client_version,配套
ConfigGet/ConfigSet接口与设置页「免费通道客户端版本」输入框。上游再抬下限改配置即可
跟进,无需重编译;留空回到自动探测。格式校验限定 x.y.z 三段数字
Full Changelog: v1.7.5-beta.6...v1.7.5-beta.7
opencode2api v1.7.5-beta.6
两项修复:流式 tool_calls 增量丢工具名(grok CLI 工具调用全失效)+ 插件启停状态跨进程竞态。
🐛 修复
- 流式 tool_calls 增量丢工具名(逐帧取值客户端工具调用全失效):OpenAI 流式约定同一
工具调用只有首帧带id/type/function.name,后续帧只有arguments增量,客户端
应自行累加。但部分客户端(实测 grok CLI 1.0.34)逐帧取值、被空字符串覆盖,最终工具名
为空报Tool not found。现网关流式转发按请求持有状态、按index记住首帧头字段并在
后续帧补齐(normalizeToolCallDeltas);对正确累加的客户端无副作用。responses / claude
两条流路径本就跨帧聚合,不受影响 - 插件启停状态被陈旧快照写回旧值(跨进程竞态):经管理 API 启用某插件后,状态文件可能在
十几秒内被其它实例写回false、插件被停(2026-09-21 现场:vibex;同一轮连续 toggle 6 次复现 1 次)。
根因是跟随路径(applyStateChanges)用函数开头的快照值落盘,而Toggle落盘前会先killCurrent
(taskkill可能耗时数秒),窗口内用户经另一进程改的新值被旧快照覆盖。现改为:跟随路径
预检 + 写时 CAS(updateStateFileCAS),磁盘值 ≠ 期望值则放弃落盘;用户 toggle 路径不变
(无条件生效)。写盘/跳过补id/enabled/pid日志。详见docs/PLUGIN-PROVIDERS.md§4.4
Full Changelog: v1.7.5-beta.5...v1.7.5-beta.6
opencode2api v1.7.5-beta.5
插件生态两件套:管理面板入口 + 模型元数据;外加一次误路由事故的根修与
插件功能建议清单(docs/PLUGIN-PROVIDERS.md §11)。
修复打包版(Tauri 壳)内插件面板入口点击无反应的问题;随版附插件功能建议清单(§11)
与通用契约冒烟测试脚本。
✨ 新增
- 插件卡片管理面板入口(球形图标):插件可用
panel:"sidecar"(面板挂插件子进程
动态端口)或panel_port:N(固定端口,0=禁用)声明自带控制面板;卡片渲染 🌐 图标,
点击新窗口打开并把网关地址 / 实例密钥(打码)/ 暴露模型清单经查询参数带给面板;
sidecar 模式点击时实时解析最新端点,规避子进程重启换端口导致的死链 - 插件模型上下文信息:插件
/v1/models可携带context_window/max_output_tokens,
经聚合目录透传——管理页插件卡片显示最大上下文、暴露弹层逐模型显示、对外/v1/models
输出 OpenRouter 风格context_length扩展字段(OpenAI 兼容客户端自动忽略未知字段) - 插件功能建议清单(§11)与通用契约冒烟测试:docs/PLUGIN-PROVIDERS.md 新增 §11
(提炼自 mmxproxy / skywork 两个参考实现的 P1/P2 功能点与源码组织建议);
scripts/plugin-smoke-test.sh模拟宿主拉起验证插件契约(就绪行 / 鉴权 / 端点 / 面板可达)
🐛 修复
-
带厂商前缀的模型不再误兜底默认厂商(误路由事故根修):插件停用/缺席期间,
插件供应商类请求此前会兜底落到 opencode 上游——产生误导性 401(Model not supported)
并把 opencode 账号池误打入坏池。现改为:前缀命中已注册厂商 → 直连该厂商(目录本轮
拉取失败 ≠ 厂商不可用);前缀厂商未注册 → 返回明确 503;无前缀模型保持原兜底行为 -
插件模型清单拉取失败不再永久为空:管理器就绪后拉取插件模型目录改为退避重试
(0/5s/15s/30s + WARN 日志)——此前单次超时会导致面板「暴露模型」按钮永久禁用 -
打包版插件面板入口点击无反应(Tauri WebView 拦截 window.open):管理 UI 跑在
Tauri WebView 内时,window.open打开的跨源新窗口被静默拦截(dev 下前端经浏览器
访问故仅打包版暴露)。现由 core 新增POST /api/admin/open-url(登录鉴权域内)代为
调用系统默认浏览器;仅允许 http/https 的 127.0.0.1/localhost/[::1] 回环地址,拒绝
引号/尖括号/换行(防 cmd 参数逃逸)。前端打开插件面板统一走该端点,失败时兜底
window.open(浏览器直访模式)
📝 文档
- README 构建章节补充 core 重嵌入警告:重编 core 后需 touch Rust 源文件或 cargo clean,
否则壳内仍是旧 core - docs/PLUGIN-PROVIDERS.md §11 插件功能建议清单:提炼自 mmxproxy / skywork 两个
参考实现——P1 强烈建议(内嵌面板三卡 / 面板入口声明 / provider.json 自管理 / 账号池 /
网络管理 / stderr 纪律 / 契约冒烟测试)、P2 按需(对话调试 / 凭证自动恢复 / 号池守护 /
用量统计 / 网络设置分离持久化 / 专属分组建议 / 上下文信息透出)、源码组织两条已验证
路线(fork+同步器 vs 同仓库独立模块;单源码双模式已回退) - scripts/plugin-smoke-test.sh 通用契约冒烟测试:模拟宿主拉起插件,验证就绪行
(port/auth 回显/id 一致)、鉴权 401、端点可达、面板可达;已对 skywork 插件实测
通过 7/0/0
Full Changelog: v1.7.5-beta.3...v1.7.5-beta.5
opencode2api v1.7.5-beta.3
上游 opencode 免费通道再次加码:新增请求体级门禁(强制流式 + 工具名指纹),本版适配后免费模型恢复可用。
🐛 修复
- opencode 免费模型再次 403 FreeTierError(请求体级门禁):上游自 2026-09-18 起在身份头之外新增两项
请求体校验——① 上游请求体必须stream:true(非流式即使其余全对也一律 403);②tools必须包含
bash/glob/grep/read四个名字(大小写敏感,schema 与描述不限)。本版对匿名免费通道
自动补齐这四个占位工具(客户端已有同名工具保留不覆盖)、强制上游流式,并在网关内把上游 SSE 聚合回
非流式 JSON(chat.completion,保留 content / reasoning / 工具调用 / usage);客户端未提供工具的
纯聊天额外下发tool_choice:"none",避免模型调用占位工具。chat 与 responses 两条上游路径同步生效,
付费通道(zen/go 带 key)请求形态不变。真机复验:非流式下游与流式下游均 HTTP 200
Full Changelog: v1.7.5-beta.2...v1.7.5-beta.3
opencode2api v1.7.5-beta.2
上游 opencode 免费通道新增服务端身份门禁,导致全部免费模型 403;本版适配后恢复可用。
🐛 修复
- opencode 免费模型恢复可用(403 FreeTierError):上游自 2026-09-16/17 起对匿名免费通道
(Authorization: Bearer public)增加两项服务端校验——①x-opencode-session必须为官方身份格式
ses_+ 12 位十六进制 + 14 位 base62(共 30 字符);②User-Agent必须为opencode/<版本>且
版本 ≥ 1.17.0(低于该版本返回 426 UpgradeRequired,无版本号或第三方 UA 返回 403 FreeTierError)。
本版按官方规范生成会话与请求 ID(x-opencode-request同为msg_+ 26 位身份串),并把版本探测
兜底值提升到门禁下限之上(探测到偏低版本时按 1.17.0 上报);chat 与 responses 两条上游路径同步生效。
真机复验:mimo-v2.5-free恢复 HTTP 200(修复前同场景 curl 实测 403)
Full Changelog: v1.7.5-beta.1...v1.7.5-beta.2
opencode2api v1.7.5-beta.1
插件式供应商体验增强:模型清单手动刷新。三平台安装包见下方 Assets;安装/升级方式与历史版本一致。
🚀 新功能
插件式供应商「刷新模型」按钮
「暴露模型」弹层头部新增 刷新模型 入口:点击后经插件子进程的标准 /v1/models 接口实时重拉上游(官网)最新模型清单,一键同步三处:
- 弹层列表:模型清单即时更新并清空当前搜索词,可直接查看/勾选新模型
- 卡片模型数:插件卡片的模型计数同步刷新
- 聚合目录:触发主进程强制重建聚合目录,统一网关
/v1/models即时生效(无需重启或重新扫描实例)
交互与安全
- 白名单自动取交集:已勾选暴露的模型会与最新清单取交集——上游已下线的模型被自动移除,不会残留失效项
- 失败不破坏现场:插件未运行 / 上游不可达 / 返回空清单时,保留旧清单并只提示错误,不影响现有可用模型
- 刷新中防重复点击:按钮带旋转动效与禁用态
后端
- 新增
POST /api/admin/plugins/{id}/refresh-models(鉴权同管理面),返回刷新后的插件视图 - 插件侧 零改动、零特化:仅依赖插件子进程既有的 OpenAI 兼容
/v1/models接口,对所有插件通用
✅ 验证
go test通过;Windows / Linux / macOS 三平台 CI 构建产物见下方 Assets- 刷新链路(前端按钮 → 管理 API → 插件子进程
/v1/models→ 模型计数更新 → 聚合目录重建)已按设计文档实现
⚠️ 说明
- 本版本为 beta 预发布,验证通过后再合入 main 打正式版
- 插件需处于
running状态才可刷新;未运行时请求返回明确错误提示(不会静默失败)