Magpie Plus · Multi-Model Gateway & Balance Board
A balance board first, three platform gateways next, then a magpie fork that folds the board back into the gateway
- Role
- Design and implementation (solo)
- Scale
- Four generations · three platform gateways · one expiry-first route
- Period
- Oct 2026 — Present
What I did
- It started as a board — power-meter flattens nine platforms into one table sorted by soonest expiry, using batch-level CreditLot and an expiry timeline
- Then three self-built platform gateways (WorkBuddy / Qoder / Trae) expose client-only quota as OpenAI / Anthropic / Responses endpoints, with account pools, tiered cooldowns and a doctor command
- A magpie (Go) fork became magpie plus — its own bundle id and login item, coexisting with the official app; power-meter runs as a sidecar behind a reverse proxy and becomes the Balance Board page
- A new routing=expiry orders accounts by the board's batch expiry, with thin wrapper plugins supplying runsOutAt for Qoder / Trae / CodeArts
- Merging 225 upstream commits cost seven conflict resolutions — the fork lives in new packages and thin wrappers, not in upstream core
Starting point: a board
Model quota was spread across several platforms, each with several accounts: some subscription, some daily check-in, some arriving in batches that expire in batches. Balances lived in vendor consoles, so “which account first” and “when does it expire” meant opening one page after another.
The first version only wanted to put balances on one page:
- The data contract came first:
ProviderSnapshot/QuotaWindow(session / daily / weekly / billing) /CreditLot(a credit batch with an expiry) /DailyUsage, plus eight statuses (healthy, rate-limited, credential expired, upstream error…). - Batches are the point: a balance is not a number but a pile of things that expire.
CreditLottracks remaining, granted and expiry, and the board sorts and highlights by soonest expiry. - Nine adapters: Qoder (two sites), Trae, CodeBuddy, CodeArts (AK/SK and cookie modes), multi-key merged cards, check-in credit platforms — each squeezing a different API into the same contract.
- Credentials captured directly:
.envplusaccounts.jsonwith hot reload; cookies via Chrome DevTools Protocol, QR or link login written straight into the account pool. - A static front end: overview / by platform / quota cards / expiry timeline / diagnostics, a ⌘K command palette, no build step; the backend has zero runtime dependencies.
The conclusion from this stage: a balance is worth nothing without its expiry. Spend the slow-expiring first and the quota is simply wasted.
Step two: three platform gateways
The board could see quotas, but quota could only be spent inside the vendor’s own client. For Claude Code, Codex, Cline and Cursor to use it, each platform needed a protocol gateway.
WorkBuddy (Tencent CodeBuddy / WorkBuddy, domestic and international): reuse the desktop client’s login state so no separate sign-in is needed; keep session, device fingerprint and daily activity on the gateway side; expose OpenAI / Anthropic compatible endpoints with account rotation, circuit breaking, cooldowns, session stickiness, plus scheduled check-in and keep-alive.
Qoder: it has two planes — a business API behind Bearer auth and an inference API behind its own COSY signature.
The gateway hides the short-lived session and per-request signing, issuing a static sk- upward so clients only know
base_url + key.
Trae: one process serving Anthropic Messages, OpenAI Chat Completions and Responses at once; accounts picked by
remaining credits, with tiered cooldowns for rate limits, auth failures and upstream errors; doctor checks each piece
and lists why every account was skipped; model-name differences mapped once through aliases.
All three solve the same class of problem:
| What a generic API assumes | What these platforms do | What the gateway does |
|---|---|---|
A long-lived static sk- key |
Short sessions, rotating JWTs | Issues a key, maps it to an account pool internally |
One Authorization: Bearer header |
Per-request signing / a custom protocol | Signs per provider, transparent upward |
| Never expires | Credits expire, with multipliers and streak tiers | Separate check-in scheduler and expiry view |
| No risk control | Device fingerprint, pacing, concurrency checked | Stable device ids, daily jitter, serial check-ins |
After the three gateways came credit2api: WorkBuddy / Trae / Qoder / CodeArts collapsed into one entry point with one issued key, check-in and account pools and usage audit in one place — three configs, three pools and three check-in flows would otherwise cost more than they return.
Step three: forking magpie into magpie plus
By now the gateway, protocols and account pools existed; what was missing was a resident carrier — a menu-bar app, a GUI, something that rewrites each agent’s config correctly and switches providers. Building that from scratch is not worth it, and magpie is MIT-licensed, so a fork is allowed.
The difficulty: magpie is Go (70+ internal packages), power-meter is TypeScript/Node. They cannot merge at the code level, only at the process level:
magpie plus (Go, 127.0.0.1:3425)
GUI page plugins/powermeter/ ──fetch──▶ /api/powermeter/*
│ │
│ internal/powermeterproxy (reverse proxy)
│ │
│ internal/powermeterproc (spawn / supervise)
▼ ▼
cards / expiry timeline / accounts / diag power-meter (Node sidecar, random loopback port)
/api/summary · accounts.json · .env
- power-meter untouched: nine adapters, the aggregation layer and the expiry timeline are reused as-is; it stays an independent repository that keeps evolving on its own.
- Sidecar-ising needed three switches: port, accounts path and
.envpath are configurable, so not a line of source changed. - Safe by default: the sidecar binds loopback only, gets a fresh token each run, keeps its config under magpie’s own directory, and the board is off by default.
- Separate identities:
com.a1ether.magpie-plus, its own bundle name and login items, so official magpie and plus install and run side by side. The config directory is deliberately shared (log in once, both see accounts and usage history); the gateway port is first-come. - Following upstream: fork code lives in new packages and page plugins, not core; merging 225 upstream commits cost seven conflicts.
Step four: expiry-first routing
magpie’s native smart routing picks accounts by remaining quota. But it cannot see batch expiry: Qoder’s credits sit
behind a web cookie endpoint, CodeArts keeps a per-transaction ledger, Trae accumulates through check-ins. Community
plugins report only totals, so for those accounts smart degrades to list order.
Two layers fixed it:
Routing: a new routing=expiry (routes.ExpiryFirst) reads the sidecar’s /api/summary expiry timeline (30-second
cache, never blocking), maps provider and account names onto magpie’s own, and orders by soonest expiry. Unknown data
falls back to smart’s order, and an almost-exhausted account stays last. The provider editor and group routing
menu, the Routing page, CLI (routing=expiry), TUI and four languages all carry it.
Data: three thin wrapper plugins (magpie-qoder-cookie / magpie-trae-batches / magpie-codearts-batches)
rewrite only the plugin’s auth.usage, writing the earliest-expiring batch of each quota bucket as runsOutAt /
runsOut. Native smart then does the right thing — and without the patches it still runs, just without expiry times.
Decisions that mattered
- Reuse over rewrite: the nine adapters are ~8K lines of TS including CDP credential capture, AK/SK, check-in credits and dual-site fallback; rewriting them in Go costs far more than maintaining a sidecar.
- One copy of the data: the board and the router read the same
/api/summaryinstead of each computing “which expires first”. - Explainable: why a request used that account, why an account was skipped —
doctorand the trace have to say it. - Never touch upstream core: new packages plus thin wrappers are what makes following upstream sustainable.
- Secure defaults: loopback only, a fresh token each run, off by default — a local tool should not quietly listen on the public internet.
Outcome
Every client now points at one local entry, every account’s balance and expiry sits on one page, and quota is consumed in soonest-expiry order. What began as a board ended up a loop of gateway, board and routing — each layer a different use of the same balances.
Still going
Following upstream, adding batch expiry for more platforms, and feeding the board’s diagnostics into the gateway’s request trace.