Magpie Plus · 多模型网关与看板
从一块余额看板出发,自研三套平台网关,最后二开 magpie 并把看板并回网关
- 角色
- 设计与实现(单人)
- 量级
- 四代演进 · 三套平台网关 · 一条到期优先路由
- 时间
- 2026年10月 — 至今
做了什么
- 起点是一块看板:power-meter 用批次级 CreditLot 与到期时间线,把 9 个平台的额度压成一张按「最快作废」排序的表
- 中间自研三套平台网关(WorkBuddy / Qoder / Trae),把客户端内积分转成 OpenAI / Anthropic / Responses 三协议端点,带账号池、分级冷却与 doctor 自检
- 二开 magpie(Go)为 magpie plus:独立 bundle id 与登录项、可与官版共存;power-meter 以 sidecar + 反向代理并入 GUI,成为「余额看板」页
- 新增 routing=expiry:用看板的批次到期时间排序账号,并为 Qoder / Trae / CodeArts 写薄包装插件补上 runsOutAt
- 跟上上游 225 个提交只解 7 处冲突——二开代码集中在新包与薄包装,不碰上游核心
起点:一块看板
手上同时用着几个平台的模型额度,每个平台又有多个账号:有的按月订阅、有的每日签到、有的分批到账、分批作废。 余额散在各家的网页控制台里,哪个账号先用、什么时候作废,只能一个个打开看。
最初的版本只想解决这件事——把余额放在一页里看:
- 数据契约先定:
ProviderSnapshot/QuotaWindow(session / daily / weekly / billing)/CreditLot(带到期时间的积分批次)/DailyUsage,以及 8 种状态(正常、限流、凭证失效、上游异常…)。 - 批次是核心:余额不是一个数字,而是「一堆会过期的东西」。
CreditLot记剩余量、发放量、到期时间,看板按最快到期排序高亮。 - 9 个平台适配器:Qoder(双站点)、Trae、CodeBuddy、CodeArts(AK/SK 与 Cookie 双模式)、多 key 合并卡片、签到积分平台等,各自把异构接口压进同一份契约。
- 凭证自己抓:
.env+accounts.json热加载;Chrome DevTools Protocol 抓 Cookie,扫码/链接登录后自动落库。 - 前端是独立静态页:总览 / 按平台 / 额度卡片 / 到期时间线 / 诊断,命令面板(⌘K),无构建步骤;后端零运行时依赖。
这个阶段最重要的结论是:余额的价值不在数字,在「什么时候失效」。先过期不先用,额度就是白扔。
第二步:三套平台网关
看板能看,但额度只能在厂商自己的客户端里花。要让 Claude Code、Codex、Cline、Cursor 都能用上,得给每个平台写一层协议网关。
WorkBuddy(腾讯 CodeBuddy / WorkBuddy,国内版 + 国际版):复用桌面端登录态,不必单独登录;把会话、设备指纹、每日活跃都留在网关侧;向上暴露 OpenAI / Anthropic 兼容端点,账号池轮转、熔断冷却、会话粘性,另有定时签到与保活。
Qoder:它有两套平面——业务接口走 Bearer,推理接口走自研的 COSY 签名。网关把短期登录态和请求级签名都藏在内部,对上层签发一把静态 sk-,客户端只知道 base_url + key。
Trae:一个进程同时提供 Anthropic Messages、OpenAI Chat Completions 与 Responses 三套端点;多账号按剩余积分挑号,限流 / 鉴权失效 / 上游异常分级冷却;doctor 逐项自检,挑号失败时列出每个账号被跳过的原因;模型名差异用别名一次映射。
三套网关解决的是同一类问题:
| 通用 API 的假设 | 平台实际情况 | 网关的处理 |
|---|---|---|
长期静态 sk- 密钥 |
短期登录态,JWT 会换 | 网关签发密钥,内部映射账号池 |
Authorization: Bearer 单头 |
请求级签名 / 自研协议 | 各 provider 内部实现签名,对上层透明 |
| 不过期 | 积分有到期时间、倍率、连登档位 | 独立的签到调度与到期视图 |
| 无风控 | 设备指纹、节奏、并发被校验 | 稳定设备号 + 每日抖动 + 串行签到 |
三套网关之后又合并出 credit2api:WorkBuddy / Trae / Qoder / CodeArts 四个平台收敛成一个入口,签发统一的密钥,签到、账号池与用量审计放在一处——否则三套配置、三套账号、三套签到,维护成本会反噬收益。
第三步:二开 magpie → magpie plus
到这一步,网关、协议、账号池都有了,缺的是「常驻的载体」:一个菜单栏应用、一套 GUI、能把各家 Agent 的配置改对、能在多个 provider 之间切换。这些从零写不值得——magpie 已经做了,而且 MIT 许可允许独立分发。
于是决定二开。难点在于 magpie 是 Go(70+ 个 internal 包),power-meter 是 TypeScript/Node,不能代码级合并,只能做进程级集成:
magpie plus (Go, 127.0.0.1:3425)
GUI 新页面 plugins/powermeter/ ──fetch──▶ /api/powermeter/*
│ │
│ internal/powermeterproxy(反向代理)
│ │
│ internal/powermeterproc(拉起 / 守护)
▼ ▼
余额卡片 / 到期时间线 / 账号 / 诊断 power-meter (Node sidecar, 随机回环端口)
/api/summary · accounts.json · .env
- 不改 power-meter:9 个适配器、聚合层、到期时间线原样复用,它是独立仓库,继续独立演进。
- sidecar 化只要三个开关:端口、账号文件路径、
.env路径都可配,于是不用改一行源码。 - 安全默认:sidecar 只绑回环,每次运行换一个 token,配置放在 magpie 自己的配置目录下;余额看板默认关闭。
- 身份分离:
com.a1ether.magpie-plus、magpie plus的 bundle 名与登录项,官版 magpie 与 plus 能同时装、同时开;配置目录故意共用(账号与用量历史只登录一次),网关端口先到先得。 - 跟上游:fork 的代码集中在新包与页面插件,不碰核心;合入上游 225 个提交只解了 7 处冲突。
第四步:到期优先路由
magpie 原生有 smart 路由,按剩余额度挑账号。但它的视野里没有「批次到期」这回事:Qoder 的积分藏在网页 Cookie 接口里、CodeArts 是一本逐笔台账、Trae 是签到攒下来的——社区插件只报汇总,smart 对这些账号就退化成按列表顺序。
于是补了两层:
路由层:新增 routing=expiry(routes.ExpiryFirst)。它读 sidecar 的 /api/summary 到期时间线(30 秒缓存,从不阻塞请求),把平台与账号名映射到 magpie 的账号上,按「最早作废」排序;读不到就退回 smart 的顺序,额度接近耗尽的账号排最后。GUI 的 provider 编辑与路由组菜单、Routing 页说明、CLI(routing=expiry)、TUI 与四种语言都补齐。
数据层:给三个平台写薄包装插件(magpie-qoder-cookie / magpie-trae-batches / magpie-codearts-batches),只重写插件的 auth.usage,把每个额度桶里最早到期的批次写成 runsOutAt / runsOut。magpie 原生 smart 立刻就能用上——不装补丁也能跑,只是没有到期时间。
几个关键判断
- 复用优于重写:9 个适配器约 8K 行 TS,含 CDP 抓凭证、AK/SK、签到积分、双站点回退,重写成 Go 的成本远高于维护一个 sidecar。
- 数据只有一份:看板与路由读同一个
/api/summary,不各算一套「哪个先到期」。 - 可解释:为什么这个请求走了那个账号、为什么这个账号被跳过,
doctor与 trace 都要能说出来。 - 不碰上游核心:新增包 + 薄包装,是能持续跟上游的前提。
- 默认安全:只绑回环、每次换 token、默认关闭——本地工具也不该默默监听到公网。
结果
现在所有客户端指向同一个本地入口,所有账号的余额与到期时间在一页里,额度按「最快作废」自动消耗。这条线从一块看板开始,最后长成了一个「网关 + 看板 + 路由」的闭环——每一层都不是新造的数据,而是同一份余额的三种用法。
还在继续
跟随上游更新、补更多平台的批次到期、把看板的诊断信息接进网关的请求 trace。