Releases: Practice019/multi2api
Release list
multi2api v1.7.3
本版修三个缺陷
两个是用户实际报障(其中 #1 由外部用户报),一个是跨全部 10 个上游的续期问题。
三者有一个共同形态,值得单独说:
上游的业务失败是
HTTP 200+ body 里的业务码,而旧代码只看 HTTP 状态码。
于是失败被记成成功 —— 界面显示绿色、历史表写ok,而实际什么也没发生。失败至少能被发现;假成功会一直骗到用户自己去核对积分。
① TRAE 签到「签到不了」(closes #1)
报障原文:trae的签到没生效。实测复现,两个缺陷叠加:
X-Device-Id 发错了值 —— 签到接口要的"设备"是账号 uid,
而代码一直发登录用的 32 位 hex 指纹。端点、头名、body 全对,唯独这个值错了:
X-Device-Id = uid(3929003642848586) → {"code":0} 到手 ✅
X-Device-Id = 32hex(ca2079…) → {"code":9074} 拒绝
不带该头 → {"code":9004} 拒绝
设备维度的回执也印证同一点(did_checked_in 是设备级、checked_in 是账号级):
uid → did_checked_in=true ← 上游承认这台"设备"
32hex → did_checked_in=false ← 不承认
业务失败被记成成功 —— 业务拒绝是 HTTP 200 + code != 0,
而 doJSON 只在 StatusCode >= 400 时报错:
上游回 {"code":9074,"message":"当前参与用户太多"} → err == nil
→ 记 StatusOK → 界面"签到成功" → 而积分一分没到账
实测 9074 持续数分钟(8/8 次、间隔 30 秒),所以"重试一下就好"不成立。
已修:解析业务 code 并分类(9074 限流 / 9095 设备今日已签 / 9004 缺设备 /
1005 权益不足);claim 后复查 status 判定真实到账(存在 code=0 但没到账的情形);
补 enable 守卫;credits + extra_credits 一并统计(额外奖励此前完全没统计)。
同一 bug 在后台定时任务里也有第四处,一并修。
② raccoon「添加账号」打开的是微信落地页
点「添加账号」后浏览器打开空白/无用页。根因是返回的 URL 根本不是网页:
https://xiaohuanxiong.com/login/mp?code=<32hex>
那是二维码内容,只能微信扫。实证:
官网主 bundle 里 `login/mp` 出现 0 次(只被当作二维码内容拼出来)
微信 UA 与桌面 UA 请求它 → 返回同一份 SPA 外壳(路由表里没有 mp)
即"在电脑浏览器里打开它什么都没有"是上游设计如此,不是拼错了。
改法:改用商汤官方 VS Code 扩展自己的浏览器登录机制
(Raccoon-VSCode/src/raccoonClient/raccoonClinet.ts 的 getAuthUrl):
/login?appname=…&redirect=http://127.0.0.1:<端口>/raccoon/callback?nonce=…
官方页面上有微信扫码与手机号短信两个 Tab,过完阿里云滑块点授权后
把 authorization_code 回调到本机端口 —— 我们当场换凭证。
滑块由官方页面与用户完成,我们不碰。
实测(真实账号,端到端跑通):
回调收到 authorization_code=ac_TBFJ_…
换凭证 HTTP 200 {"code":0,"data":{"access_token":"eyJ…"}}
user_info HTTP 200 {"code":0,"name":"RaccoonSophia","id":"7497524"}
redirect 没有白名单限制。
⚠ 两个必须遵守的细节:
绝不能带login_source=desktop—— 官网用它判断是否走
office-raccoon://深链;带了就完全忽略redirect。
防串号标识塞进 redirect 的 query(nonce),不能用state——
官网只转发authorization_code。
③ 每个请求都续期(跨全部 10 个上游)
出口层判"该不该续期"读的是池投影的过期时刻,而各上游的池投影都刻意不抄
ExpiresAt(它是启动快照,活 secret 续期后不会跟新)。实测 /admin/accounts 里
expires_at 键一个上游都没有 ⇒ 恒为 0。而 NeedsRefresh 对
ExpiresAt <= 0 恒返回 true ⇒ 每个 chat 请求先做一次续期往返。
危害按 refresh_token 形态分两档:
可重复使用(cline 等)→ 白费一次往返(实测:发一次对话后 token 被换了)
一次性(raccoon / codearts)→ 每次请求烧掉一个 token
已修:判据优先用活凭证的过期时刻。顺带修 ParseJWTTimes 缺 iat 时
回落 nbf —— raccoon 的 token 只有 nbf 没有 iat(抽样全部上游只有它如此),
导致它的比例窗口整个空转。
升级说明
无配置项变更、无破坏性变更。换二进制即可。
⚠ 若你之前遇到过 TRAE 账号显示"签到成功"但积分没涨、或
raccoon 点「添加账号」打开空白页 —— 本版就是修这两个。
⚠ 本版移除了 raccoon 的二维码登录(QRLoginExt)。原因不是"不支持扫码",
而是新登录 URL 编成二维码是坏码:手机扫会跳到手机自己的 localhost。
扫码能力没丢 —— 官方登录页自己就有微信扫码 Tab。
产物
全部由 CI 构建(本机不手工上传)。五个平台,命名统一:
wb2api-server-windows-amd64.zip 含 exe + config.example.json + start.bat
wb2api-server-linux-amd64.tar.gz
wb2api-server-linux-arm64.tar.gz
wb2api-server-darwin-amd64.tar.gz
wb2api-server-darwin-arm64.tar.gz
SHA256SUMS.txt
校验:
sha256sum -c SHA256SUMS.txt回滚
本版纯修复。回滚换回 v1.7.2 的二进制即可 —— 凭证文件格式未变,
两版可互换读取(raccoon 登录只改了怎么发起,不改凭证结构)。
配置级:server.disables_chat_tools: true 可关掉对话工具注入
(与本次改动无关,仅作应急开关)。
触发条件:若出现 raccoon「添加账号」收不到回调,说明上游收紧了
/login?redirect= 的校验(本版实测可行)—— 此时会如实报错
(回调缺少 authorization_code),不会静默超时。
multi2api v1.7.2
本版修三个你实际报障的缺陷
三个报障都指向确定性缺陷(不是偶发),都已实测复现、修复、加回归测试。
值得注意的是:三个报障的初始推断都是错的,真因各不相同 —— 所以下面每条
都写了「真因」而不只是「改了什么」。
① agent 框架(DSH / Claude Code / Cursor)里生不了图
你的原话:「DSH 里面它是没有办法去生图的,因为它会判定你的模型是无法生图的模型」。
真因不是 DSH 的判断,是网关自己的判据主动不注入工具:
if hasOwnTools(body) { return nil, false } // 客户端带 tools → 一个都不注入那条判据写在"普通聊天客户端"的语境里(出发点是对的:客户端带 tools 说明它
懂协议、要自己驱动工具循环)。但 agent 框架每轮都带 bash / read / write ——
于是永远命中它 → 模型没有 generate_image 可调 → 只能吐一段 SVG 源码或说"我画不了"。
改法:边界从"整个不接管"改成按工具名分工:
| 模型调谁 | 谁执行 | agent 框架看到什么 |
|---|---|---|
客户端自己的(bash/read/…) |
客户端 | 正常的 tool_calls |
网关注入的(generate_image/web_search) |
网关 | 只见模型"直接回了张带图的 markdown" |
于是 agent 框架侧零配置 —— 不必在它的配置里声明生图工具,自包含成立。
⚠ 顺带修掉一个连带 bug:
injectTools原来是obj["tools"] = wireTools
(赋值 = 替换)。旧判据挡住了所有带工具的客户端所以没暴露;判据一删就会
抹掉 agent 框架的全部工具 —— 模型再也执行不了任何命令,而现象是
"DSH 突然变笨了",跟生图这功能表面毫无关联。已改合并 + 重名去重。
② 文字不是流式的了
上一版工具循环只有非流式一条路(内部跑完整轮再整块发出),而它对每个
loomy 请求都成立 —— 所有对话都被缓冲成一整块。实测复现:12.6 秒的内容挤在 2 帧里。
那个设计基于一个被实测推翻的假设。真实上游 25 帧的流显示:
第一个含 content 的帧 : 不存在(-1)
第一个含 tool_calls 的帧 : 第 8 帧
即:模型要调工具时正文一个字都不先发;纯文字回复从头到尾没有 tool_calls 帧。
所以"边流边判断"完全可行,改成两态状态机(forward / buffer),
普通文字 100% 保持逐字流式,只在真要调工具的那一轮拦截。
⚠ 两个必然副作用都处理了:① 工具执行期间 13-35 秒不发字节 → 中间代理会掐连接
→ 加 SSE 注释帧保活;② 流式下模型收尾的话是逐帧流出去的,图片 markdown
没有地方发 → 第一版漏了这步,实测"图生成了、模型说话了、用户看不到图"
而积分已扣。
③ 图片"无法预览" / "拿不到文件实体"
你的结论写的是「上游生图网关出图→传云端的管道断了」。这个推断被实测推翻 ——
连续生成 3 张立刻验证:
HEAD → 200 GET → 200/330444
HEAD → 200 GET → 200/217678
HEAD → 200 GET → 200/1363756
对象实体一直都在。 真因是签名 URL 对 HEAD 必然 403(腾讯 COS 把 HTTP method
也算进签名,而 URL 是按 GET 签的),而预览器 / 取图器 / 文件发送队列通常先发 HEAD
探测类型与大小 —— 一探测就 403,于是显示"无法预览"、"file unavailable"。
现象像管道断了,真因只是 method 不匹配。
修法:探测裸形式(去掉签名)是否可用,可用就用它(HEAD/GET 都通、不过期、
markdown 里也没有 & 与 ;),不可用则退回签名 URL。
不硬编码"bucket 公开读"—— 那是上游的部署事实,上游改私有后会自动退回。
⚠ 另有第二个根因:提示词里的
]会截断 markdown 链接(alt是用户输入,
而 CommonMark 在第一个未转义的]处结束)。你看到的那段
"图片无法预览 · 电商产品主图宣传海报…"就是截断后的 alt。已转义
\ [ ] ( ) < > * _~ | #`。⚠ 还有第三个:上一版只修了对话工具那条路径,
/v1/images/generations漏了
(那条链直接透传上游响应体)。已把改写挪到两条路径的共同入口GenerateImage。
④ raccoon 的 Token 一直显示"已过期"
管理台同时显示「正常」与「已过期」。实测你那份凭证:
access_token 寿命约 3 小时,已过期 532 分钟
refresh_token 寿命 30 天,还剩 29.5 天 ← 明明能救
真因:核心续期只有两条触发路径,raccoon 一条都不占。 它有 RefreshCredential、
有 RefreshSkew,但没有 Jobs() —— 于是只有"有人拿它发对话请求"时才顺手续期。
横向核对:workbuddy / codearts / cline / lobsterai / qoder / trae 都有,
只有 raccoon 没有。
已补 internal/raccoon/jobs.go(10 分钟一轮)。实测:无人调用对话接口的情况下,
42 秒内自动续期成功。
教训(值得记住):「具备续期能力」与「会去续期」是两件事 ——
前者是方法签名,后者是注册了后台任务。缺后者时没有任何报错,只是悄悄过期。
这与 cline 此前那次报障("Token 已过期,怎么不会自动刷新")逐字同一种病。
稳定性加固
- 流式响应可能被写两遍(响应损坏):
consumeRound三条错误路径里只有两条
检查了"是否已写过响应",status >= 400那条漏了 → 已写出内容后仍换号重试
→ 往同一个 ResponseWriter 再写一套 SSE → 客户端收到两段交错的流。
改用统一出口finalize(sw)(十余条 return,逐条手写就是十余处可能漏掉的地方)。 - 工具执行不跟请求取消(白扣积分):三处都用
context.Background(),而 handler
手上明明有r.Context()→ 客户端断开后生图照跑满 5 分钟、积分照扣。已接请求 ctx。 - panic 有了可诊断的收场:net/http 的 recover 只关连接 + 打 stack,客户端拿到
"连接被重置"然后重试(再触发同一个 panic)。新增panicRecovery中间件,
挂在withAuth(/v1全部端点的唯一共同入口)。 - 服务器补了
IdleTimeout/MaxHeaderBytes,但刻意不设WriteTimeout——
它会掐断正常的长 SSE(实测搜索 35.4s、工具循环最长 15 分钟),
且故障形态是"短请求都好、长请求偶发中断",极难归因。 - 日志 TTFB / tok / tok/s 三列不再为空:工具循环是另一条读取路径,绕过了
chatStatsReader—— 功能完全正常,只有日志悄悄空了。
实测(真实上游)
逐字流式 31-88 个内容帧
流式生图 86 帧,含保活注释帧 + 图片 markdown + [DONE]
流式搜索 569 帧
两条生图路径 字段 created/data/points_consumed 完整,points=110,HEAD 200
raccoon 续期 无人调用对话接口,42 秒内自动续期成功
日志三列 TTFB=859ms tok=357 69.3tok/s
MaxHeaderBytes 2MB header → 431
产物
全部由 CI 构建(本机不再手工上传)。五个平台,命名统一:
wb2api-server-windows-amd64.zip 含 exe + config.example.json + start.bat
wb2api-server-linux-amd64.tar.gz 含可执行文件 + config.example.json
wb2api-server-linux-arm64.tar.gz
wb2api-server-darwin-amd64.tar.gz
wb2api-server-darwin-arm64.tar.gz
SHA256SUMS.txt 上面五者的校验和
下载后校验:
sha256sum -c SHA256SUMS.txt回滚
本版纯修复、无破坏性变更(无新增上游、无接口变更、无配置项移除)。
- 配置级回滚:
server.disables_chat_tools: true可关掉对话工具注入
(回到"对话里说一句不会生图"的行为)。生图端点/v1/images/generations
不受它影响。 - 版本回滚:换回 v1.7.1 的二进制即可。凭证文件格式未变,
两个版本可互换读取(raccoon 续期只新增了后台任务,
不改凭证结构)。 - 触发条件:若出现生图/对话请求失败率显著上升,先关
disables_chat_tools定位是否与工具注入有关;仍异常则回滚二进制。
已知限制(与上一版相同,未变)
- 只接了 Loomy 生图。其它上游要生图需各自实现
ImageGenExt。 - 图生图未实测(上游支持
images参数,本次只实测文生图)。 - 上游忽略
response_format=b64_json与n>1,网关如实透传、不替它纠正。 - 工具循环轮次上限 3;到顶会去掉工具定义强制收尾。
- 没有 WebUI 生图入口(只有 API)。
- 裸图片 URL 依赖"对象存储桶是公开读"这个上游部署事实;
上游若改成私有读,会自动退回签名 URL(HEAD 会 403,预览器可能仍不工作)。
文档
CHANGELOG.md—— v1.7.1 / v1.7.2 条目(含每条的真因)docs/chat-tools.md—— 工具注入的实现与取舍docs/image-generation.md—— 生图实现、验证、限制docs/loomy-local-toolchain.md—— Loomy 本地工具链的实测记录
multi2api v1.7.1
本版重点:Loomy 生图 + 上游工具自动注入
用 Loomy 的任意一个对话模型说一句「帮我画一张图:…」就能拿到图,
客户端零改动。联网搜索同理。
curl -X POST http://127.0.0.1:7863/v1/chat/completions \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{"model":"loomy/qwen3.8-flash",
"messages":[{"role":"user","content":"帮我画一只戴墨镜的柴犬"}]}'回复正文里直接带 ,任何支持 markdown 的界面会渲染出图;
同时响应顶层多一个 loomy_artifacts 字段给程序读取(URL / 提示词 / 扣分)。
为什么做成了「工具注入」而不是「自动路由」
对话模型本身不会画图 —— 实测请它画猫,它返回一段 SVG 源码。
原因是模型声明的 modalities.output:会说话的都是 ["text"],
会画图的(doubao-seedream-5-lite / qwen-image-3.0-pro / Hy-Image-3.5-preview)
都是 ["image"] 且不支持 tool_call。
所以只能由外部替它调生图模型。借助上游本身支持的工具调用能力:
模型产出 tool_calls ──→ 网关替它执行 ──→ 结果回喂 ──→ 模型收尾
工具是「上游私有」的
「只有用 Loomy 时才启用 Loomy 的工具」是结构上成立的,核心零改动:
| 请求路由到 | 拿到什么 |
|---|---|
| loomy | 生图 + 联网搜索两个工具 |
| workbuddy | 一个都没有(没实现该扩展点,行为与改造前逐字节一致) |
工具背后是上游自己的端点(/images/generations、/search/tencent),
换个上游这些路径根本不存在 —— 做成「核心内置工具」等于让核心假装
知道所有上游的端点。
两种客户端都兼容
| 客户端 | 生图 | 它自己的工具 |
|---|---|---|
| 普通聊天客户端 | 说一句就出图 | — |
| agent 框架(DSH / Claude Code) | 说一句就出图,它看不到工具调用 | bash/read 等原样透传,网关不拦 |
agent 框架侧零配置 —— 不必在它的配置里声明 generate_image。
网关只接管自己注入的那几个工具名,其余全部透传。
新增
生图端点(OpenAI Images API 形状)
POST /v1/images/generations
{"model":"loomy/doubao-seedream-5-lite","prompt":"…","n":1,"size":"1024x1024"}
三个可用模型(均实测通过):doubao-seedream-5-lite、qwen-image-3.0-pro、
Hy-Image-3.5-preview。它们在 /v1/models 里带
capabilities:["image_generation"] 标记,且不进对话模型目录
(走 /chat/completions 会 404)。
/images/generations 与「对话里说一句」的取舍:前者一次往返直出图(更省);
后者模型会替你润色提示词、失败会解释、能多轮追问。
对话工具注入
gateway.ChatToolExt新扩展点:上游自报「可注入对话的工具 + 怎么执行」- Loomy 实现两个工具:
generate_image、web_search - 客户端自带
tools时按工具名分工(见上表),不整个不接管
配置
{ "server": { "disables_chat_tools": false } }缺省 false(功能开启)。只影响注入;客户端自己带 tools 时本就不接管。
修复
图片「无法预览」(两个确定性根因,各自 100% 复现)
① 签名 URL 的 HEAD 请求必然 403 —— 上游给的是腾讯 COS 签名 URL,
而 COS 把 HTTP method 也算进签名(URL 是按 GET 签的)。预览器通常
先发 HEAD 探测类型与大小,一探测就失败:
带签名 URL GET → 200 ✓ HEAD → 403 ✗(10/10)
裸 URL GET → 200 ✓ HEAD → 200 ✓
走 GET 的客户端一切正常 —— 所以现象看起来像「链接偶尔损坏」,取决于
客户端先发哪种请求。修法:探测裸形式是否可用,可用就用它,不可用
退回签名 URL(依赖 bucket 公开读这个部署事实,不硬编码)。
② 提示词里的 ] 会截断 markdown 链接 —— alt 是用户输入,
CommonMark 在第一个未转义的 ] 处结束。用户看到的那段
「图片无法预览 · 电商产品主图宣传海报…」就是截断后的 alt。
修法:转义 \ [ ] ( ) < > * _ \ ~ | #`。
文字不再流式
上一版工具循环只有非流式一条路(内部跑完整轮再整块发出),而它对
每个 loomy 请求都成立 —— 所有对话都被缓冲成一整块。
实测复现:12.6 秒的内容挤在 2 帧里。
那个设计基于一个错误假设(「必须先知道要不要调工具才知道给客户端什么」)。
实测推翻它:
第一个含 content 的帧 : 不存在(-1)
第一个含 tool_calls 的帧 : 第 8 帧
模型要调工具时正文一个字都不先发;纯文字回复从头到尾没有
tool_calls 帧。所以改成边流边判断:普通文字 100% 保持逐字流式,
只在真要调工具的那一轮拦截。
修后实测:纯文字 31-88 个内容帧;流式生图 86 帧含保活注释帧;
流式搜索 569 帧。
agent 框架里生不了图
上一版有条「客户端自带 tools 就不接管」的判据。它在普通聊天客户端
语境下是对的,但 agent 框架每轮都带自己的工具,于是永远命中 →
网关不注入 → 模型没工具可调 → 只能吐 SVG 源码。
用户观察到的是「DSH 判定该模型不能生图」,
真相是网关主动不给了。已改为按工具名分工(见上)。
顺带修掉一个连带 bug:injectTools 原来是 obj["tools"] = wireTools
(赋值 = 替换)。在旧判据下它不出问题,判据一删就会抹掉 agent
框架的全部工具 —— 模型再也执行不了任何命令,而现象是
「DSH 突然变笨了」,与本功能毫无表面关联。
日志 TTFB / tok / tok/s 三列为空
普通 chat 路径把上游流包在 chatStatsReader 里读;工具循环是另一条
读取路径,绕过了它 —— 三个字段没人填,日志渲染成 -。
功能完全正常,只有日志悄悄空了。
修法:给 chatStatsReader 加 SetReader,让工具循环的每一轮流
穿过它读(不为统计另写一套 usage 解析 —— 那会让同一份知识
有两个实现)。
CI
Release 改为五平台全部由 CI 编译,并消除产物重复。
v1.7.0 的 Release 上有 7 个资产但实际只有 5 个平台 —— 两套命名并存
(CI 编的 2 个 + 本机编的 5 个),windows-amd64 与 linux-amd64
各重复一次,命名风格还不一致。
现在:CI 覆盖 windows-amd64 / linux-amd64 / linux-arm64 /
darwin-amd64 / darwin-arm64,命名统一为
wb2api-server-<goos>-<goarch>.<zip|tar.gz>(不带版本号)。
归档格式按平台分两种(Windows 用 zip,其余 tar.gz)。
新增质量门禁 job(build/vet/test/tidy/gofmt)与残留资产清理步骤。
本机不再手工上传任何产物。
已知限制
- 只接了 Loomy。其它上游要生图需各自实现
ImageGenExt。 - 图生图未实测 —— 上游支持
images参数(参考图改图),本次只实测文生图。 - 上游忽略
response_format=b64_json(始终返回 url)与n>1(只给一张、
只扣一次费)—— 网关如实透传,不替它纠正。 - 签名 URL 的确切有效期未实测,仅从
q-sign-time推断约 12 小时。 - 工具循环轮次上限 3;到顶会去掉工具定义强制收尾。
- 没有 WebUI 生图入口(只有 API)。
文档
docs/chat-tools.md—— 工具注入的实现与取舍docs/image-generation.md—— 生图实现、验证、限制docs/loomy-local-toolchain.md—— Loomy 本地工具链的实测记录
multi2api v1.7.0
v1.7.0 — 上游扩到 10 个 + 登录/签到/额度/续期四条链路系统性补齐
📦 打包形式(同 v1.6.1 / v1.6.2)
每个平台 zip 内含:可执行文件 + config.example.json(10 个上游全部配好)+ 使用说明.txt(Windows 另外带 start.bat),解压即用。
⚠ 本版只增不删:没有删除任何 v1.6.2 里已发布过的上游。
mimo/buddy两个上游确实被删了,但它们从未进过任何 Release
(是 v1.6.2 之后加的),所以升级的用户本来就没见过它们。
配置里若残留mimo段会被静默忽略,建议删掉。
旧 config.json 可直接用 —— 没有任何配置字段被改名或删除。
🆕 上游:4 个 → 10 个
v1.6.2 只有 4 个(workbuddy / codearts / loomy / trae)。本版新增 6 个:
| 上游 | 登录方式 | 能力 |
|---|---|---|
| WorkBuddy 海外版 | 页内 OAuth(www.workbuddy.ai) | 对话 / 额度(海外版无玩法) |
| Cline | WorkOS 设备码轮询(不起本地端口,服务器部署天然可用) | 对话 / 余额 / 自动续期 |
| Raccoon(商汤小浣熊) | 微信扫码(页面直接出二维码) | 对话 / 积分余额 / 登录奖励 |
| LobsterAI(有道龙虾) | 页内登录 | 评论 / 三步式签到领积分 |
| Qoder(阿里系) | PKCE 设备码 | 对话(WASM 加密推理)/ 积分 / 每日签到 |
| Qoder 中国版 | 同上 | 同上(与 qoder 共用凭证目录,靠 product_id 区分) |
✨ 新增能力
两层「全部签到」
账户池顶部一个「全部签到」→ 触发所有上游的全量签到(后台任务 + 进度回执)。
各上游卡片头上另有一个「全部签到」→ 只签本上游。
额度三层作用域 + 原始值
- 账号行内「额度」:只刷该账号
- 卡片头「刷新本上游额度」:只刷该上游
- 顶部「刷新全部额度」:刷所有账号
额度列显示上游自己的记账单位(不做单位换算假设)。
批量导入统一到所有上游
卡片头「批量导入」:把你已有的凭证 JSON 直接粘进来(单个对象或 [ ] 数组),导入后自动重载账号池。
10 个上游都支持 —— 此前只有 2 个。
Token 列 / 自动续期
- 各上游按自己的凭证寿命比例提前续期(不再是固定 10 分钟),判据取自 token 自己的
iat/exp。 - 「Token 到期」列现在如实显示剩余时间。
登录
- 「添加账号」默认用无痕窗口打开授权页(网关自己启动浏览器 + 独立 profile,避免 OAuth 拿浏览器里已登录的账号完成授权)。
- 扫码类上游(raccoon)现在直接渲染二维码 —— 此前给的是一个扫不了的链接。
🔧 修掉的问题(都是用户实测报的)
「添加账号有问题」
- qoder:此前完全没有 PKCE —— 授权页能打开、点了授权却永远拿不到 token(一直转圈到 5 分钟超时)。按参照实现补齐 PKCE 五件套,轮询改用 GET。
- raccoon:给出扫不了的链接 —— 它的链接是给手机扫的内容,而界面只给「打开链接」。
- 弹窗同时说两句矛盾的话 —— 顶部引导语写死"去浏览器登录",扫码流程还会再显示"用手机微信扫码"。现按登录形态分叉。
「签到失败」
- lobsterai:上游返回的
actions是字符串数组,我们按对象数组解析 → 每次签到必失败。 - qoder:缺少机器身份头(服务端就不下发可领活动)。根因是那个头的来源文件在本机根本不存在(纯登录流程不产生它),而旧实现只读文件、读不到就静默降级 → 实际是永久不发头。现在实时调用官方
runtime-info.exe生成。 - 瞬时 503 被当成永久失败:实测上游约 25% 的请求回
DEPENDENCY_UNAVAILABLE(与请求头无关,/usage同时刻恒 200)。旧实现不重试,而签到每 30 分钟扫一次、撞上就记一次失败。现加退避重试。
「额度是 0」
真相是**"我们还没问过上游"** —— 额度探测只在手动点按钮时才被调用,没有定时任务扫它。现在由续期任务每轮顺带写回。
「Token 不会自动刷新」
- cline:它的续期能力实现了,但没有注册后台任务 —— 核心的两条续期路径(出站请求 / 后台定时)一条都不占,没人用它时 token 就静静过期。
- qoder 续期响应字段名全错:上游返回
device_token+expires_at(ISO 字符串),我们读的是access_token+expires_in(两个都不存在)→ 续期永远失败、过期时刻从未落盘。 - qoder 两个实例注册了同名后台任务 → 日志
任务名 qoder-refresh 重复…已跳过后者→ 中国版永不续期。
其它
- 「刷新本上游额度」点下去报
refreshAccounts is not defined(调用了一个从未定义的函数)。 - 额度回执显示
[object Object]。 workbuddy批量导入会丢deviceToken—— 它是每账号一个的风控头,丢了表现为"导入后这个号开始被风控",且看不出与导入有关。
🎨 界面
- 卡片头按钮顺序统一为:
全部签到 → 添加账号 → 批量导入 → 刷新本上游额度 → 重载 auths
「添加账号」去掉了+前缀。
🧪 工程
- 新增真浏览器 e2e(点按钮 → 抓真实请求),验的是行为而不是"有没有某个元素"。
变异验证:把事件委托的"只绑一次"守卫去掉 → 刷新 5 次后点一次发 7 个请求,而所有只看 DOM 的静态断言全绿。 - 新增二维码编码器(零依赖自实现),正确性由交叉验证守住:真的调用 Python
qrcode库逐位比矩阵 —— 结构断言全绿不等于码能扫。
🚀 快速开始
- 解压对应平台的 zip
copy config.example.json config.json,编辑api_key- 放凭证到
auths/<上游>/,或直接用管理台的「添加账号」/「批量导入」 - Windows 双击
start.bat;Linux/macOS 执行./wb2api-server -config config.json - 打开 http://127.0.0.1:7863/ui
📥 下载
推荐(带版本号、含 使用说明.txt + 完整 config.example.json):
wb2api-server-v1.7.0-windows-amd64.zip— Windows x64wb2api-server-v1.7.0-linux-amd64.zip/-linux-arm64— Linuxwb2api-server-v1.7.0-darwin-amd64.zip/-darwin-arm64— macOS
CI 自动构建(由 GitHub Actions 从 tag 构建,不含 使用说明.txt):
wb2api-server-windows-amd64.zipwb2api-server-linux-amd64.tar.gz
SHA256SUMS.txt — 上面全部 7 个压缩包的校验和。
校验方法:
sha256sum -c SHA256SUMS.txt(Windows 可用certutil -hashfile <文件> SHA256逐个核对)。⚠ 本仓库此前几个版本的
SHA256SUMS.txt只覆盖了 CI 那两个,占多数的
带版本号 zip 没有校验和 —— 本版已修正为覆盖全部资产。
完整变更见仓库
CHANGELOG.md与 git log(v1.6.2..v1.7.0)。
multi2api v1.6.2
v1.6.2 — 30 分钟主动全量续期 + 手机号昵称 + 稳定性
📦 打包形式(同 v1.6.1)
每个平台 zip 内含:可执行文件 + config.example.json(默认启用四上游)+ start.bat + 使用说明.txt,解压即用。
✨ 相比 v1.6.1 的更新
token 续期:统一「30 分钟主动全量续」
- 之前"被动只刷临近过期":codearts 60s 扫描只刷剩 <3min 的,用户看到 token 倒计时却不续(观感差)
- 现在:所有上游(workbuddy/trae/codearts)每 30 分钟无条件全量刷新全部账号 token——token 永远是"30 分钟内续过"的,不会走到"请求时发现过期"
- codearts 续期间隔默认 60s → 30min(可配置)
- 死 token 号保留退避(不刷屏)+ 账号池可见「需重新登录」
- ⚠ 注意:codearts refreshToken 消费型,同一凭证目录只应被一个实例使用
账号昵称统一手机号
- loomy:昵称优先级改为手机号优先(批量导入也显示手机号,之前显示 NKxxx)
- workbuddy:本就手机号
- codearts/trae:凭证体系无手机号,显示官方账号标识(也是登录凭据)
修复
- 导入/重载账号后数据自动更新(重载路径补额度刷新 + 无感轮询检测新账号行)
- 重复导入/重载不再清掉已有凭证(投影同步保留 token)
- 移除「每 5s 刷新」开关(数值实时性由 30s 无感轮询覆盖)
- 管理密钥一键轮换(写 config 立即生效无需重启)
- 趋势图 hover tooltip + 布局修复;网关卡 2→7 张
🚀 快速开始
- 解压 zip
- copy config.example.json config.json,编辑 api_key 与凭证目录
- 放凭证到 auths/(或用管理台「+添加账号」页内登录)
- 双击 start.bat(或 ./wb2api-server -config config.json)
- 打开 http://127.0.0.1:7863/ui
📥 下载
wb2api-server-v1.6.2-windows-amd64.zip— Windows x64wb2api-server-v1.6.2-linux-amd64.zip/linux-arm64— Linuxwb2api-server-v1.6.2-darwin-amd64.zip/darwin-arm64— macOSSHA256SUMS.txt— 校验和
完整变更见仓库 git log(v1.6.1..v1.6.2)。
multi2api v1.6.1
v1.6.1 — API 管理 + 仪表盘增强 + 稳定性修复
📦 本次打包形式:压缩包(解压即用)
每个平台的 zip 内含:可执行文件 + config.example.json(配置模板,默认启用全部四个上游)+ start.bat(Windows 启动脚本)+ 使用说明.txt。不再需要分别下载配置文件。
✨ 相比 v1.6.0 的新功能
API 管理(对标 new-api 令牌管理)
- 多把 API Key(存 data/apikeys.json,持久化,重启不丢)
- 每把 Key:额度上限(token,0=不限)、每分钟请求上限(0=不限)、启用/禁用、独立用量统计(请求数/消耗 token/失败数)
- 管理密钥一键轮换:生成新钥匙 → 写入 config.json → 立即生效无需重启(旧钥匙作废)
- 鉴权语义:config 的 api_key 是管理钥匙(不限额);普通 Key 额度用尽 → 402、超速 → 429、禁用/未知 → 401
- 兼容性:未配置多 Key 时行为与旧版完全一致
仪表盘增强
- 24h 趋势图:请求量 + 成功率双折线(内联 SVG,零图表库),hover 显示各小时请求/成功/失败/token
- 网关状态卡 2 → 7 张:账号总数/健康/冷却中/已禁用/在途请求/累计请求/成功率
- 移除「每 5s 刷新」开关:数值实时性统一由 30s 无感轮询覆盖(只更新数值不整页重绘)
- API 管理面板移到仪表盘下方,管理钥匙与普通 Key 同屏显示
稳定性
- 后台续期失败统一接入账号池可见禁用(连续 3 次 → 「已禁用:凭证失效,需重新登录」)
- codearts 死 token 不再每 60s 重试打上游(指数退避 + 抖动,借鉴 LiteLLM)
- 全部上游签到/保活/福利领取统一 30 分钟被动扫描(幂等)
🚀 快速开始
- 解压对应平台的 zip
copy config.example.json config.json,编辑 api_key 与凭证目录- 放入凭证到 auths/(或用管理台「+添加账号」页内登录)
- Windows 双击 start.bat;Linux/macOS 执行
./wb2api-server -config config.json - 打开 http://127.0.0.1:7863/ui
📥 下载
wb2api-server-v1.6.1-windows-amd64.zip— Windows x64wb2api-server-v1.6.1-linux-amd64.zip/linux-arm64— Linuxwb2api-server-v1.6.1-darwin-amd64.zip/darwin-arm64— macOSSHA256SUMS.txt— 全部压缩包校验和
完整变更见仓库 git log(v1.6.0..v1.6.1)。
multi2api v1.6.0
v1.6.0 — 解压即用的预编译 Release
📦 下载后怎么用(3 步)
- 按系统下载对应压缩包,解压得到:
wb2api-server(主程序)+config.example.json(配置模板,四个上游默认全开)+start.bat(Windows 双击启动) - 把
config.example.json改名config.json,填入你的 API Key(api_key 字段)与凭证:- 方式 A:把已有的凭证目录
auths/(workbuddy/codearts/loomy/trae 的 json)放进解压目录 - 方式 B:启动后在管理台「+ 添加账号」页内登录(workbuddy/codearts/trae/loomy 都支持)
- 方式 A:把已有的凭证目录
- 启动:
- Windows:双击
start.bat(自动用同目录 config.json) - Linux/macOS:
./wb2api-server -config config.json - 管理台:http://127.0.0.1:7863/ui
- Windows:双击
没有 config.json 时程序会提示复制 config.example.json,并以默认值启动(不会硬退)。
端口改 config.json 的listen字段(默认 7863;避开被占用的端口,如 Clash 的 7899)。
新增上游
- TRAE SOLO:第四个上游 —— 多账号对话、页内添加账号(浏览器 OAuth 零手动)、自动签到、自动续期、权益额度
- loomy 邀请码体系:绑定/导入/额度全链路,错误翻译成人话
稳定性(移植 AIClient2API 机制)
- 主动健康检查:定时探测冷却/熔断中的账号并提前恢复
- 熔断失败窗口衰减、刷新失败连续 3 次自动禁用
- 全部上游统一 30 分钟被动扫描(签到/保活/福利领取同一粒度、幂等)
控制台 UI 大改
- 设置页重设计:6 组按主题切分 + 生效时机徽章;删减不必要的设置、必要项只读
- 账号池额度/状态/统计卡无感轮询:只更新数值不整页重绘
- 成长计划精简:12 列/8 卡 → 6 列/6 卡;「一键完成」支持全部账号
- 修复:boot 崩溃、邀请码绑定路由、codearts 福利"已领取"显示、缺配置硬退
校验
下载后可用 SHA256SUMS.txt 校验(sha256sum -c SHA256SUMS.txt)
multi2api v1.5.1
服务标识从 workbuddy2api 统一为 multi2api(顶栏标题 / /healthz service / X-Service 头 / 启动日志)。
multi2api v1.5.0
multi2api —— 多上游聚合反代网关(WorkBuddy / CodeArts / Loomy → OpenAI 兼容 API)。本版为自 v1.2.0 以来的累积发布。
✨ 新增
- Loomy 手机号验证码登录:对接讯飞账号网关(HMAC-SHA1 签名),「+ 添加账号」可输手机号+验证码直接登录。
- Loomy 新手任务:新标签页「新手任务 · Loomy」—— 8 个 onboarding 任务(10000 分),一键完成单账号 / 全部账号。
- Loomy 邀请码:新标签页「邀请码 · Loomy」—— 激活状态 / 我生成的码(active/exhausted)/ 绑定别人的码;首登初始化自动补。
- 批量粘贴导入:账号池「批量导入」按钮,粘贴 JSON(单条/数组)逐条落盘 + 自动重载账号池。
- 额度改走积分网关:按 session 逐账号查可用总额度(availableBalance = 总余额 + 当日剩余),不再依赖本机缓存。
🎨 控制台重构
- 仪表盘精简:网关状态只留「账号总数/健康」;调用统计 8 张核心卡,累计消耗改 token 口径(跨上游统一单位)。
- 删除「任务历史」标签页;账号池删「熔断/在途」列、昵称自动打码、去掉顶部「全部签到」。
- codearts 按钮文案「领取福利」→「签到」;workbuddy「签到」与「额度」互换位置(额度排最前)。
- 仓库更名 multi2api(多上游聚合反代),README 全面重写并附界面截图。
🛠 修复
/admin/login/poll不再下发ExpiresAt零值(之前让添加账号弹窗显示「已过期」)。- 页面轮询不再拆建面板 / 重复拉取已加载面板;输入框重绘后保留已输内容。
- 邀请码绑定前置校验"不能用自己的码",失败提示翻译成人话。
📦 产物
wb2api-server-windows-amd64.zip(exe + config.example.json + start.bat)wb2api-server-linux-amd64.tar.gzSHA256SUMS.txt
配置参考
config.example.json;Loomy 短信登录签名 key 随官方安装包分发(混淆而非加密),可用loomy.sms_*覆盖。
multi2api v1.2.0
WorkBuddy2API v1.2.0
本版主题:移植并接通「客户端行为遥测伪造层」 —— 让网关能把一批上游成长任务直接做完;
同时补上内容误报治理(提示词体系 + 降级重试)与出站身份的一致性。
亮点
1. 任务中心「一键完成」
控制台的任务明细现在长出了三个入口:
| 入口 | 位置 | 端点 | 形态 |
|---|---|---|---|
| 一键完成 | 每行「操作」列 | POST /admin/growth/auto |
同步,实测 ~10s |
| 一键完成待办 | 任务明细工具条(作用于选中的账号) | POST /admin/growth/auto-all |
202 + 后台,实测 ~186s |
| 开学季一键完成 | 同上 | POST /admin/school/run |
202 + 后台 |
按钮按后端能力表(GET /admin/growth/auto/actions,17 条)动态出现,不在前端写死;
不在表里的任务(如需微信真实认证的 Expert_Philanthropy)仍显示「去客户端做」。
在此之前,这 7 条遥测端点一条都没被前端引用 —— 能力只存在于 curl 里。
而且任务行对accepted/in_progress一律显示「去客户端做」,注释还写着
"这两类任务本网页无法代做":能力接进来之后那句话变成了假话。
2. 两类任务的代价不同(重要)
| 类别 | 任务 | 做的事 |
|---|---|---|
| 纯伪造 | chat_5、Buddy_App、automation_1、Library_read、template_5、playbook_prompt、create_canvas、Hp_Appearance 等 |
只发构造好的事件链,没有任何真实动作,不消耗账号配额 |
| 需真实对话 | expert_5、Expert_team_use_3、Expert_lighthouse、skill_1、Model_chat_GLM5.2、RichMeow_Chat |
必须真发一次 chat 才能拿到上游签发的 requestId —— 实测上游会校验专家 id 与 requestId,自造的不计数 |
black_cat(夜猫子)只在 23:00–08:00 本地时间计分;窗口外不会假装成功,
而是如实回报「不在窗口、行为不计分」,并交由每日 23 点的排程补足。
3. 内容误报治理:提示词体系
上游内容审核不是语义审核,而是对客户端注入的固定模板句做逐字精确匹配,
命中就整单拦截(HTTP 400 + 审核文案)—— 一批完全合法的流量因此被误杀。
三层防护,互不替代:
- 提示词体系:出站前用网关自有提示词整体替换客户端 system/developer 消息
(prompt.mode=custom,内置 2086 字节,可用prompt.file覆盖); - 指纹脱敏兜底:user/assistant/tool 消息里的已知指纹串逐串擦;
- 内容拦截成为一等错误类(
ErrKindContentBlocked):与账号故障分开,
不再被误报成「所有账号不可用」。
被拦截时不罚账号、不换号(内容策略与账号无关),同请求内换极简中性提示词
重试一次并记忆到次日 00:00 CST。
4. 其余新增
- 软限流按模型冷却:解析上游
code 6004的重置时刻 + 指数退避,上限cooldown.soft_rate_max。 - 会话死亡计数:连续 3 次才禁用,替代"一票否决"。
- 出站身份一致性:三段式 UA + 设备令牌(5 分钟缓存)+ 客户端 IP 透传 + attribution/billing UA。
- 请求体硬上限
server.max_body_mb(默认 8 MiB)。 - DeepSeek thinking 注入 +
reasoning_content回填。 - 对话活跃上报(
schedule.activity_hours,空 = 关闭)。 - 管理台 7 条新端点(仅本机 loopback)。
修复(均为"不报错但用户会以为坏了"那类)
- 请求体 > 8 MiB 被静默截断后仍转发成功(HTTP 200) → 改为 413。
thinking未列入supportedFields→ 注入被整体剥离,配置形同无效。- 设备令牌缓存重置时误用 mutex →
fatal error: sync: unlock of unlocked mutex。 6004正则会把"code":60040误判成软限流。upstreamToGateway漏映射内容拦截 → 单上游部署返回 503 掩盖真实原因。- 单账号部署的内容拦截降级重试无法重新选号。
- 任务中心 4 个缺陷:已领取的任务也长按钮;
/admin/task完成摘要恒报「成功 0」
(实测开学季真跑完 3 次抽奖 +78c,摘要却是ok=0);一键完成后缓存快照不刷新
(跑完 186 秒、14 项领奖成功,界面仍显示原样待办);toast 把奖励播报两遍。 - 顺带修复领养前置:派猫前先补
ensureAdoptPrereq,此前恒失败。 - 冷却到"绝对时刻"时会改写调用方给的时刻(纳秒级偏差):上游给出的配额
重置时刻没有原样生效。根因是两次读钟之差被加进了目标时刻,
而且它只在 Linux 上暴露(Windows 时钟粒度约 15ms,两次读钟通常同值 → 偏差恒为 0,
本机一直全绿)。现已让"绝对时刻"不经过时长往返。
实测证据(本版关键路径都做过真机验证)
- 单任务伪造遥测:
Library_read上游计分并被接受 ——progress_before 0/1 → 1/1、claimed=true、+100 分 +5 能。 - 全量一轮:账号待办 15 → 1(仅剩窗口外的
black_cat);12 项执行成功并领奖。 - 提示词层行为级 A/B:同一句指令放
role=user时模型服从(BANANA-7),
放role=system时不服从 —— 证明 system 消息确实在出站前被替换(而非"配了没生效")。 - 缓存刷新:写入后
GET /admin/growth(不带refresh=1)从 5 条待办变为 4 条。 - 全部 6 个提交都过了 CI 的完整门禁(先在本机用 CI 同版本 go1.22.5 在
git archive导出的 LF 树上预演,再以 CI 的 Linux 结果为准):
gofmt -l无输出、go mod tidy无差异、build/vet/test全绿。 - 顺带说明:本版先修好了 master 上长期变红的
Check formatting,
才让 CI 第一次真正执行到go test—— 上面那条纳秒偏差正是这样被暴露出来的。
升级说明
- 本版零新增第三方依赖(
go.mod仍只有go-redis一个直接依赖,单文件静态二进制)。 config.json新增可选段(均可省略,省略即用默认值):
server.max_body_mb、cooldown.soft_rate_max、schedule.activity_hours/
activity_enabled、prompt.mode/prompt.file、upstream.user_agent/
client_version/cli_version/client_name/device_token/
device_token_file/passthrough_ip。详见config.example.json与 README。- ⚠
schedule.activity_hours是唯一「留空 = 关闭」的排程项
(签到 / 保活留空是回落到默认时点)—— 刻意如此,避免存量部署升级后凭空产生上游请求。 - 附带修复了 master 上 CI «Check formatting» 长期变红的 9 个存量文件(纯注释格式,零行为变化)。
产物
wb2api-server-windows-amd64.zip—— 含wb2api-server.exe+config.example.json+start.batwb2api-server-linux-amd64.tar.gz—— 含可执行文件 +config.example.jsonSHA256SUMS.txtwb2api-server.exe—— 另附一份未压缩的 Windows 可执行文件,方便不想解压的用法
由 CI 在干净环境用 go1.22.5 交叉编译(-trimpath -ldflags="-s -w",CGO_ENABLED=0),
以去掉本机路径并保证产物同构。
关于那份裸 exe
- 它逐字节取自
wb2api-server-windows-amd64.zip(同一份 CI 产物,不是另编一份),
工具链与构建参数因此完全一致。 - SHA256:
82941918d8948996a2061575ee86b270e1ec6004fa17ac74d5959cd5d67843cb - ⚠ 该哈希不在
SHA256SUMS.txt里 —— 那份文件由 CI 生成,只覆盖两个归档。
要校验裸 exe 请以上面这个哈希为准(或直接校验 zip 后解压)。 - 已用这份发布产物做过一次实机冒烟:独立端口 + 关闭全部排程启动 →
/healthz返回{"healthy":3,"service":"workbuddy2api","total":3},
且/ui下发的 HTML 含本版的三个「一键完成」按钮与相关判据 ——
即"发布的这个 exe 确实带着这些改动",而不是只看源码推断。
本项目仅供学习和研究使用。使用者需遵守上游服务条款,自行承担使用风险。