Skip to content

Releases: Practice019/multi2api

multi2api v1.7.3

Choose a tag to compare

@Practice019 Practice019 released this 05 Oct 07:49

本版修三个缺陷

两个是用户实际报障(其中 #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

Choose a tag to compare

@Practice019 Practice019 released this 04 Oct 19:56

本版修三个你实际报障的缺陷

三个报障都指向确定性缺陷(不是偶发),都已实测复现、修复、加回归测试。
值得注意的是:三个报障的初始推断都是错的,真因各不相同 —— 所以下面每条
都写了「真因」而不只是「改了什么」。


① 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

Choose a tag to compare

@Practice019 Practice019 released this 04 Oct 05:21

本版重点: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":"帮我画一只戴墨镜的柴犬"}]}'

回复正文里直接带 ![…](https://….png),任何支持 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

Choose a tag to compare

@Practice019 Practice019 released this 30 Sep 10:47

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 库逐位比矩阵 —— 结构断言全绿不等于码能扫。

🚀 快速开始

  1. 解压对应平台的 zip
  2. copy config.example.json config.json,编辑 api_key
  3. 放凭证到 auths/<上游>/,或直接用管理台的「添加账号」/「批量导入」
  4. Windows 双击 start.bat;Linux/macOS 执行 ./wb2api-server -config config.json
  5. 打开 http://127.0.0.1:7863/ui

📥 下载

推荐(带版本号、含 使用说明.txt + 完整 config.example.json):

  • wb2api-server-v1.7.0-windows-amd64.zip — Windows x64
  • wb2api-server-v1.7.0-linux-amd64.zip / -linux-arm64 — Linux
  • wb2api-server-v1.7.0-darwin-amd64.zip / -darwin-arm64 — macOS

CI 自动构建(由 GitHub Actions 从 tag 构建,不含 使用说明.txt):

  • wb2api-server-windows-amd64.zip
  • wb2api-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

Choose a tag to compare

@Practice019 Practice019 released this 16 Sep 06:05

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 张

🚀 快速开始

  1. 解压 zip
  2. copy config.example.json config.json,编辑 api_key 与凭证目录
  3. 放凭证到 auths/(或用管理台「+添加账号」页内登录)
  4. 双击 start.bat(或 ./wb2api-server -config config.json)
  5. 打开 http://127.0.0.1:7863/ui

📥 下载

  • wb2api-server-v1.6.2-windows-amd64.zip — Windows x64
  • wb2api-server-v1.6.2-linux-amd64.zip / linux-arm64 — Linux
  • wb2api-server-v1.6.2-darwin-amd64.zip / darwin-arm64 — macOS
  • SHA256SUMS.txt — 校验和

完整变更见仓库 git log(v1.6.1..v1.6.2)。

multi2api v1.6.1

Choose a tag to compare

@Practice019 Practice019 released this 15 Sep 11:41

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 分钟被动扫描(幂等)

🚀 快速开始

  1. 解压对应平台的 zip
  2. copy config.example.json config.json,编辑 api_key 与凭证目录
  3. 放入凭证到 auths/(或用管理台「+添加账号」页内登录)
  4. Windows 双击 start.bat;Linux/macOS 执行 ./wb2api-server -config config.json
  5. 打开 http://127.0.0.1:7863/ui

📥 下载

  • wb2api-server-v1.6.1-windows-amd64.zip — Windows x64
  • wb2api-server-v1.6.1-linux-amd64.zip / linux-arm64 — Linux
  • wb2api-server-v1.6.1-darwin-amd64.zip / darwin-arm64 — macOS
  • SHA256SUMS.txt — 全部压缩包校验和

完整变更见仓库 git log(v1.6.0..v1.6.1)。

multi2api v1.6.0

Choose a tag to compare

@Practice019 Practice019 released this 15 Sep 07:54

v1.6.0 — 解压即用的预编译 Release

📦 下载后怎么用(3 步)

  1. 按系统下载对应压缩包,解压得到:wb2api-server(主程序)+ config.example.json(配置模板,四个上游默认全开)+ start.bat(Windows 双击启动)
  2. 把 config.example.json 改名 config.json,填入你的 API Key(api_key 字段)与凭证:
    • 方式 A:把已有的凭证目录 auths/(workbuddy/codearts/loomy/trae 的 json)放进解压目录
    • 方式 B:启动后在管理台「+ 添加账号」页内登录(workbuddy/codearts/trae/loomy 都支持)
  3. 启动:
    • Windows:双击 start.bat(自动用同目录 config.json)
    • Linux/macOS:./wb2api-server -config config.json
    • 管理台:http://127.0.0.1:7863/ui

没有 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

Choose a tag to compare

@Practice019 Practice019 released this 14 Sep 20:37

服务标识从 workbuddy2api 统一为 multi2api(顶栏标题 / /healthz service / X-Service 头 / 启动日志)。

multi2api v1.5.0

Choose a tag to compare

@Practice019 Practice019 released this 14 Sep 19:39

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.gz
  • SHA256SUMS.txt

配置参考 config.example.json;Loomy 短信登录签名 key 随官方安装包分发(混淆而非加密),可用 loomy.sms_* 覆盖。

multi2api v1.2.0

Choose a tag to compare

@Practice019 Practice019 released this 14 Sep 13:03

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.bat
  • wb2api-server-linux-amd64.tar.gz —— 含可执行文件 + config.example.json
  • SHA256SUMS.txt
  • wb2api-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 确实带着这些改动",而不是只看源码推断。

本项目仅供学习和研究使用。使用者需遵守上游服务条款,自行承担使用风险。