plugins: a built-in moved onto its plugin looks and works as it did (你确保插件版的订阅 provider 和其对应的内置的…都保持一模一样了吗?) — moved onto its plugin, a built-in lost pieces: its tile's name, icon, plans and risk note went to the plugin's own, "Add account" still opened the built-in's sign-in (in the app, on the server and in magpie accounts add), an agent's own sign-in the built-in still found showed a second usage card of the same account, the account's plan badge went, WorkBuddy's daily check-in stopped, the provider's icon and site were the plugin's, WorkBuddy AI's card was named WorkBuddy, and Devin, which had no card of its own, lost the plugin's. Now a moved built-in keeps its tile and risk sheet and signs in through its plugin; its cards take the built-in's place and name, and it has none of its own; the plan the plugin tells stays with the account; WorkBuddy checks in through the plugin, which signs the call; a card is left out only where its built-in's slot showed it. TestMovedBuiltinQuiet, TestWorkBuddyCheckinThroughPlugin, TestMoveToPlugin (Devin-like card) and TestPluginUsage (plan kept) fail without the change. Command Code and Devin moved and back in a sandbox HOME: one card each, plan, name. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; plugin-move, plugin-signin, commandcode-risk, old-webkit pass; click-scroll's WebKit fails the same on origin/main under this load.
2026-10-01 00:40:22 +08:00
app: Magpie is in the Start menu on Windows (#158: Windows 便携版首次运行后,无法在开始菜单 / 系统搜索中找到 Magpie — @Aimer779) — the Windows build is one portable exe with no installer, so nothing put it anywhere Windows search looks. Each start of the desktop app (not the CLI, TUI or serve) now makes sure %APPDATA%\Microsoft\Windows\Start Menu\Programs\Magpie.lnk (the Programs known folder) opens the exe it runs as, symlinks resolved, in its folder, with its icon and "Magpie" as its description; one that already does is left be, one for where the exe was before it was moved is pointed here again. It is the shell's own link, made through WScript.Shell with go-ole (already in the build through Wails, now a direct require), in the background beside the magpie:// registration, and a failure is only logged. An update replaces the exe in place, so the shortcut stays good; a magpie left running from magpie.exe.old(-N) (or .new) points it at magpie.exe, never at what was moved aside. HKCU App Paths\magpie.exe is set to the exe (and its folder) so Win+R magpie opens it too. No uninstall entry, nothing copied. MAGPIE_NO_SHORTCUT=1 skips it (tests, sandboxes); other systems do nothing. Tested with the Windows test built here and run on Windows 11: a shortcut made, left as it was on a second start, pointed at the moved exe. Run on Windows 11 as the desktop app: the shortcut made with the exe's folder and icon, pointed anew after the exe moved, left as it was on a start with nothing changed; App Paths set
2026-09-28 22:44:36 +08:00
2026-09-25 22:11:19 +08:00
rules: a routing group's rule can be for the agent compacting its conversation, so a cheaper, faster model writes the summary while the conversation stays on its own (X, MR.Guo: 希望 Magpie 能像 OpenCode 一样支持单独指定 context compression model:当 Agent 需要做上下文压缩时…指定一个更便宜、更快的小模型专门负责总结和压缩历史上下文) — a compaction went to whatever model the conversation was on, the priciest in the group as often as not, and a long session paid for each summary at that price. A rule's new condition, compact (provider.Rule.Compact, "compacting"; magpie group rule add <group> use=<model> compact, compact=yes|no; the GUI editor's 压缩上下文时 / compacting toggle; the TUI hint), matches a request that is an agent compacting, told from the agent's own prompt (internal/gateway/compacting.go, read from each one's release): the system prompt of Claude Code 2.1's /compact and auto-compact, OpenCode's compaction agent, Pi, Gemini CLI's state_snapshot and Qwen Code, or the last user message of Claude Code's, Codex's (and magpie's own for a Codex compaction_trigger) and Kimi Code's; a prompt only quoted earlier in a conversation doesn't count. Only in a group with such a rule is a request looked at this way, so nothing else changes. A compaction is decided on its own, never held by the turn's rule, its classifier never asked; it isn't remembered for the turn, and neither the conversation's affinity nor the turn's size is taken from its answer, so the requests after it go on on the model the turn was on and its cache (without that, the next request of a Claude Code turn followed the summary to the cheap model). A member whose context is less than the compaction is passed over for the next rule that matches, or the group's order (RuleHit.Small). The trace tells it ("The agent is compacting the conversation, so rule 1 sends the summary to …", the models passed over named), and the editor notes a compact rule's model taking less than another member. Docs: start#rules (en, zh). Tested: internal/gateway/compacting_test.go — each agent's prompt taken, a plain and a quoted one not; in a group with rules compact→a/small and tokens≥2000→b/big, a long Claude Code turn on b, its /compact to a (Compact, N=1), the turn's next request back on b held, Codex's compaction to a too; a 70k-token compaction skipping a (64k) for rule 2 with Small [a/small]; a group with no compact rule routing a compaction as before; ParseRule/Line/Conditions/Matches for compact. TestRuleCompact failed with the affinity kept from the summary. internal/gui/tests/routing-compact.test.cjs, Chromium and WebKit, en and zh: the compaction in the trace, the rule's words, the toggle on and off with nothing moved, the room note, a compacting-only rule saved as compact:true and not refused. routing-kind/served/signin/steady, click-scroll and old-webkit pass. go vet, go build -tags nogui (and GOOS=linux, GOOS=windows) and go test -tags nogui ./... pass. Not tried against real agents' compactions through the gateway (the prompts are matched as read from their releases) nor in the real Wails window.
2026-09-30 10:24:18 +08:00
library: an agent's own copy of a library skill gives way to it, and one that differs is settled from the warning (StringKe, JasonLeeForOnly on Discord: after Bring in all, "Claude Code already has a skill of its own called impeccable", 这里没有强制覆盖的能力) — a skill is brought in from the agent with the very folder and given to the agents that had that folder; another agent's copy (CC Switch copies a skill into each agent) was listed as "differs in" and left out, and giving the skill to it, or a copy put back into an agent after its folder became magpie's link (by CC Switch or whatever installed it), was a hard failure with no way out. A folder there holding the same files as the library's (bytes compared; .DS_Store, .git and magpie's copy mark aside) is now set aside into backups/<when>/<agent>/skills and the library's linked in its place; a skill brought in takes the agents with such a copy too, which are no longer said to differ; a move that copied it all but couldn't remove the folder brings it in all the same, what was left set aside. One that differs keeps the warning, now in the reader's language with "Use the library's" (the agent's set aside, the library's linked) and "Keep <agent>'s" (the agent, and any reading the same folder, taken off the skill) on its row — skills/use-library and skills/keep-own, magpie library skill use-library|keep-own <name> <agent>, which the CLI suggests under the problem. Backups holding a skill set aside are never pruned. TestOwnSkillInTheWay, TestSameTree, TestPruneKeepsSkillsSetAside and own-skill.test.cjs (Chromium, WebKit, en/zh) fail without the change; TestSharedAgentsSkills now expects the byte copy to be the library's. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; own-skill, import-all-skills, library-items, shared-skills, market-have, click-scroll, old-webkit pass.
2026-09-30 19:42:43 +08:00
gui: the page runs in macOS 12's WebKit — no regex lookbehind, the few newer built-ins filled in, fallbacks for container queries, subgrid, :has and color-mix (#220 uclort: on macOS 12.7.6 the app opened blank, the panel showing its tab bar, a "Profiles + Save current" heading and "Open magpie · Quit" and nothing else, no button working) — the page runs in the system's WebKit, on macOS 12 Safari 15.x's, and app.js's snippet highlighter had a regex lookbehind, (?<==)\S+ (app.js:2167), which WebKit only parses from Safari 16.4: a SyntaxError in a literal fails the whole file, so app.js never ran (and routing.js and library.js, which lean on it, threw), leaving index.html's static markup; library.js had two more, (?<![\w-]) in the SVG greying (library.js:162-163). The highlighter now finds a value after "=" by hand (the character before the match, a sticky \S+) and the greying checks the character before a colour name in its replace callback, each giving the same output as before (compared on shell, curl, Python and Node snippets and an SVG with attribute and style colours, data-fill and x-color left alone). Built-ins newer than Safari 15.0 in use, routing.js:307's findLast and library.js:1042's structuredClone (15.4), are filled in by a new compat.js loaded before the other scripts (with at, findLastIndex and Object.hasOwn), each only where missing; structuredClone there is a JSON copy, enough for the API's plain data. Styles: a selector list with :focus-visible (15.4) drops the whole rule before 15.4, so the agent handle's hover, an account's Remove and protocol buttons showing on hover, the protocol menu's hover and the editor's focus ring are split from their :focus-visible (or it is dropped where :focus covers it); .acc:has(.aq) and the panel's .profiles:has(> .chip-input) hint (15.4) are classes set by app.js (with-aq, naming); the Routing page's container queries and the request list's subgrid (16) get @supports not fallbacks by the window's width (the tray panel is narrower than all of them) and a row's own columns; and the panel's --panel-tint, a color-mix() (16.2) in a custom property that would have gone invalid and painted the panel clear (tintPanel then handing the system white under dark text), falls back to --bg, as the floating status and Library's sticky head do. Other things newer than 15.0 are left as cosmetic: inert (15.5) on the folded agent list, accent-color, the rest of color-mix() on hover and focus shades, .quota's subgrid rows. The build: Go 1.25 and later run on macOS 12 and up only (the linker stamps 12.0 as the oldest), so Info.plist's LSMinimumSystemVersion and the Makefile's -mmacosx-version-min go from 11.0 to 12.0 and the site says macOS 12+ rather than 11+. Tested with a new internal/gui/tests/old-webkit.test.cjs: every assets/*.js parsed with the Babel parser Playwright bundles, failing on a regex lookbehind, v flag or modifiers (in a literal or new RegExp("…")), a class static block, decorators, import attributes or using (checked against the old highlighter's regex and a static block, and not on named groups, lookahead, ??= or private methods); a grep for built-ins newer than 15.0 (at, findLast, structuredClone, hasOwn, toSorted and co., Set methods, groupBy, withResolvers, Iterator helpers, popover …) unless compat.js fills them in (fails on main's routing.js:307 and library.js:1042 without it); compat.js the first of the page's scripts and every script loaded; the CSS with no :has(), no :focus-visible in a mixed list, no nesting or dvh, and @container, subgrid columns and a color-mix() custom property only with an @supports not fallback in the same file; and, in WebKit with those built-ins deleted, the panel's tabs, Save current's name field and every window page drawn with no page error. Documented in internal/gui/tests/README.md. go build -tags nogui (and GOOS=linux, GOOS=windows), go build (darwin GUI), go test -tags nogui ./... and the whole GUI suite (130 tests) pass. Not tried on a real macOS 12 or Safari 15: the old engine's parse is judged by the parser, not run.
2026-09-29 23:52:38 +08:00
providers: a built-in subscription moves onto its community plugin and back, its accounts with it (the user: 慢慢下掉内置的那些 Subscription Provider…不要让用户一升级完之后,突然发现他的所有服务都不可用了) — eleven built-ins (Zed, Qoder, Factory, MiMo, Devin, Grok, Command Code Plan, Cursor, Kiro, WorkBuddy, ZCode) each get a mover to their @magpie-community package. A move installs the plugin (updated when older than the mover needs), hands it every account in the plugin's shape, checks the plugin lists models for them, and only then lets the plugin serve the same provider id, so agents' model ids don't change; any failure rolls back and the built-in keeps serving. The built-in's accounts are kept as a backup, and "Run built-in again" (magpie plugin move-back <id>) takes the plugin's current sign-ins back, never copying a rotating token: they are taken out of the plugin in one step, and an account the built-in renewed meanwhile keeps its newer token. An account only the plugin can hold (Cursor's browser sign-in) stays with it. The provider editor shows "Runs on" with the button, a failed move's reason, and the CLI has magpie plugin move / move-back. Automatic moves (Retiring) are wired in but the list stays empty until the packages are on npm. Checked with real accounts in a sandbox HOME: Command Code Plan and Devin moved, answered a chat through the gateway on the plugin, moved back and answered again on the built-in. TestMove*, TestPutBackKeepsNewer, the movers' round-trip tests and plugin-move.test.cjs (Chromium, WebKit, en/zh) are new. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; click-scroll, old-webkit pass.
2026-09-30 21:21:58 +08:00
providers: a Remote magpie provider uses another computer's shared magpie, each request translated at most once (Jorben on Discord: 远程 magpie / magpie 接力) — a custom provider pointed at another magpie has one API per address and couldn't know which API each model's own provider serves, so a request was translated on both computers (Chat → Messages here, then again there). magpie's /v1/models now tells each model's native_endpoints (the APIs its provider serves it on; none for a routing group) and its image input, and reads them, with max_output_tokens and supported_reasoning_levels, from a list; the new remote-magpie preset takes the other magpie's address however typed (host:port, /v1, /v1/messages) for all three APIs and lists all its models, its user's choice already. Add sheet tile with the address field and a hint to Settings → Share on local network; CLI: magpie provider add remote-magpie <key> url=http://<ip>:3425. Checked with two sandbox magpies over the LAN address with the key check on and a Messages-only fake upstream: Chat, Messages (streamed), count_tokens, Responses, a routing group and Gemini all reached it as /v1/messages, translated once. TestRemoteMagpieNativeAPI, TestFetchMagpieFields and TestRemoteMagpie fail without the change; remote-magpie.test.cjs (Chromium, WebKit, en/zh) added. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass but for TestCursorPluginContext, which fails on main (1ce05f0) as well; click-scroll, old-webkit pass.
2026-10-01 01:35:18 +08:00
davsync: sync can keep its file in an S3-compatible bucket as well as a WebDAV folder (#296) — AWS S3, Cloudflare R2, Backblaze B2, MinIO, Garage or a NAS. Settings › Sync and backup picks WebDAV or S3: endpoint (empty is AWS), bucket, prefix, region (empty is us-east-1, auto for R2), access key ID and secret, path-style; CLI magpie s3 on s3://bucket/prefix access-key-id=… endpoint=… region=… path-style. The bucket holds the same passphrase-sealed file at <prefix>/magpie/magpie.magpie-backup, signed with SigV4 (no SDK). It is read with its ETag and written with If-Match (If-None-Match: * for the first), so two computers syncing at once don't overwrite each other; a server that refuses conditional writes (501, or 400 naming the header) is remembered and gets a HEAD-then-PUT instead. An IP, localhost or a dotted bucket on https goes path-style on its own. Errors say what to do: wrong key/secret, clock skew, bucket in another region (names it), no such bucket, 403 on the first read on AWS without s3:ListBucket. The secret is kept as the WebDAV password is, sent only to the endpoint and key it was typed for, and may not be the passphrase. sigv4_test (AWS's published vectors), s3_test (fake server: conditions, conflict, fallback, regions, errors), s3_cli_test and s3-sync.test.cjs (Chromium, WebKit, en/zh) fail without the change; tested against MinIO. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestGroupAddSetRemove, a TempDir-cleanup flake under load, passes alone); click-scroll, old-webkit pass.
2026-09-30 17:07:47 +08:00
usage: a Requests tab lists every request of the period, one row each, and exports them as CSV, from the window and from magpie usage --csv (Discord 范不着: a per-request ledger as sub2api keeps, to set beside the upstream's own bill) — the Usage page summed calls by agent and model, and the log did not keep the model the agent asked for or the one the reply said answered, so a single request could not be checked against a vendor's bill. usage.Record now keeps Requested (req, the model id the client sent, a magpie alias or group as it came) and Served (served, the model the vendor's reply named, only when it named one); every usage.Append in the gateway fills them (the relayed and translated paths, Codex's backend, image draws, decide), and records written before load as they did, without them. The swap test the Routing page uses (a dated or pinned name is the same model) moved to usage.Swapped so the ledger marks the same rows. usage.Ledger gives the period's records newest first with each one's list-price cost, the agent it is counted under and whether the model was swapped, filtered by agent, failures alone and a search over the models, provider, account, session and effort; usage.WriteCSV writes them (time, agent, requested_model, provider, host, model, served_model, swapped, effort, input, output, cache write, cache read and reasoning tokens, cost_usd, duration_ms, ttft_ms, status, error, session, kind). The Usage page gets a third tab, Requests (请求), between Overview and Sessions, under the same period picker: an agent filter, All/Failed, a search field and Export CSV over a table of time, agent, requested model, provider · account, model sent, served model (amber with the Routing page's sentence when another model answered, plain on a dated name, a dash when the reply named none), effort, in, out, cache write, cache read, cost, duration and status, a failure's row marked by a red dot and bold code; the totals over it and 100 rows a page with Newer and Older. GET /api/usage/requests serves a page, POST /api/usage/requests/export saves all the filtered rows to Downloads (never over an earlier file) and opens the folder, GET /api/usage/requests.csv hands a browser the file in magpie web. The table scrolls sideways in its own box; at the window's narrowest the words after the head's cost give way so the three tabs fit without the page scrolling sideways. No border stripe; clicks move nothing (the pager held where it was). en and zh strings together. Tested with TestLedger (order, cost, swap, a dated name, an old record without the fields, each filter, the CSV exactly), TestServedModelRecorded and TestServedModelUnnamed (a request through a fake upstream answering with another model records Requested fake/sol, Model sol and the served name; one naming none leaves Served empty), TestUsageCSV (magpie usage --csv, a period, a stray argument refused), TestUsageLedgerRoutes (paging, totals, filters, export to Downloads twice) and a new internal/gui/tests/usage-ledger.test.cjs in Chromium and WebKit, en and zh, light and dark: the tabs and columns, the swap tag on the swapped row only, the 429 row, Older and Newer over 130 requests with the pager left where it was, the agent, Failed, search and period filters asking the server from the first page, Export CSV posting the filters with no page and saying where the file went, at 560 wide no tab scrolling the page sideways and the cost uncut, no left border. go build -tags nogui (and GOOS=linux, GOOS=windows), go test -tags nogui ./... and the whole GUI suite (122 tests) pass. Not tried in the real window through Wails or against a live gateway's log.
2026-09-29 21:26:01 +08:00
usage: a Requests tab lists every request of the period, one row each, and exports them as CSV, from the window and from magpie usage --csv (Discord 范不着: a per-request ledger as sub2api keeps, to set beside the upstream's own bill) — the Usage page summed calls by agent and model, and the log did not keep the model the agent asked for or the one the reply said answered, so a single request could not be checked against a vendor's bill. usage.Record now keeps Requested (req, the model id the client sent, a magpie alias or group as it came) and Served (served, the model the vendor's reply named, only when it named one); every usage.Append in the gateway fills them (the relayed and translated paths, Codex's backend, image draws, decide), and records written before load as they did, without them. The swap test the Routing page uses (a dated or pinned name is the same model) moved to usage.Swapped so the ledger marks the same rows. usage.Ledger gives the period's records newest first with each one's list-price cost, the agent it is counted under and whether the model was swapped, filtered by agent, failures alone and a search over the models, provider, account, session and effort; usage.WriteCSV writes them (time, agent, requested_model, provider, host, model, served_model, swapped, effort, input, output, cache write, cache read and reasoning tokens, cost_usd, duration_ms, ttft_ms, status, error, session, kind). The Usage page gets a third tab, Requests (请求), between Overview and Sessions, under the same period picker: an agent filter, All/Failed, a search field and Export CSV over a table of time, agent, requested model, provider · account, model sent, served model (amber with the Routing page's sentence when another model answered, plain on a dated name, a dash when the reply named none), effort, in, out, cache write, cache read, cost, duration and status, a failure's row marked by a red dot and bold code; the totals over it and 100 rows a page with Newer and Older. GET /api/usage/requests serves a page, POST /api/usage/requests/export saves all the filtered rows to Downloads (never over an earlier file) and opens the folder, GET /api/usage/requests.csv hands a browser the file in magpie web. The table scrolls sideways in its own box; at the window's narrowest the words after the head's cost give way so the three tabs fit without the page scrolling sideways. No border stripe; clicks move nothing (the pager held where it was). en and zh strings together. Tested with TestLedger (order, cost, swap, a dated name, an old record without the fields, each filter, the CSV exactly), TestServedModelRecorded and TestServedModelUnnamed (a request through a fake upstream answering with another model records Requested fake/sol, Model sol and the served name; one naming none leaves Served empty), TestUsageCSV (magpie usage --csv, a period, a stray argument refused), TestUsageLedgerRoutes (paging, totals, filters, export to Downloads twice) and a new internal/gui/tests/usage-ledger.test.cjs in Chromium and WebKit, en and zh, light and dark: the tabs and columns, the swap tag on the swapped row only, the 429 row, Older and Newer over 130 requests with the pager left where it was, the agent, Failed, search and period filters asking the server from the first page, Export CSV posting the filters with no page and saying where the file went, at 560 wide no tab scrolling the page sideways and the cost uncut, no left border. go build -tags nogui (and GOOS=linux, GOOS=windows), go test -tags nogui ./... and the whole GUI suite (122 tests) pass. Not tried in the real window through Wails or against a live gateway's log.
2026-09-29 21:26:01 +08:00
davsync: sync can keep its file in an S3-compatible bucket as well as a WebDAV folder (#296) — AWS S3, Cloudflare R2, Backblaze B2, MinIO, Garage or a NAS. Settings › Sync and backup picks WebDAV or S3: endpoint (empty is AWS), bucket, prefix, region (empty is us-east-1, auto for R2), access key ID and secret, path-style; CLI magpie s3 on s3://bucket/prefix access-key-id=… endpoint=… region=… path-style. The bucket holds the same passphrase-sealed file at <prefix>/magpie/magpie.magpie-backup, signed with SigV4 (no SDK). It is read with its ETag and written with If-Match (If-None-Match: * for the first), so two computers syncing at once don't overwrite each other; a server that refuses conditional writes (501, or 400 naming the header) is remembered and gets a HEAD-then-PUT instead. An IP, localhost or a dotted bucket on https goes path-style on its own. Errors say what to do: wrong key/secret, clock skew, bucket in another region (names it), no such bucket, 403 on the first read on AWS without s3:ListBucket. The secret is kept as the WebDAV password is, sent only to the endpoint and key it was typed for, and may not be the passphrase. sigv4_test (AWS's published vectors), s3_test (fake server: conditions, conflict, fallback, regions, errors), s3_cli_test and s3-sync.test.cjs (Chromium, WebKit, en/zh) fail without the change; tested against MinIO. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestGroupAddSetRemove, a TempDir-cleanup flake under load, passes alone); click-scroll, old-webkit pass.
2026-09-30 17:07:47 +08:00

magpie

One place to pick every agent's model: Codex on DeepSeek, Claude Code on Kimi, Gemini CLI on GLM, from the menu bar. usemagpie.ai

Discord

magpie is a single screen that lists each AI agent on your machine and the model it is set to. Click a value, pick a model. That is the whole app.

It lives in the menu bar: click the icon and a panel drops down; the same screen also opens as a normal window (magpie, or Open magpie in the tray menu), and there is a terminal version (magpie tui) and a plain CLI.

  ◉ magpie

  ▸ Claude Code   claude-fable-5-1[1m]                        ~/.claude/settings.json
    Codex         gpt-6-astra   effort medium
    Gemini CLI    gemini-3.1-pro
    OpenCode      anthropic/claude-sonnet-5   small anthropic/claude-haiku-4-5
    MiMo Code     anthropic/claude-sonnet-5
    Pi            openrouter/z-ai/glm-5.2:batch
    Goose         anthropic/claude-sonnet-5
    Cursor        auto
    Copilot CLI   claude-fable-5

  ↑↓ agent  ·  ←→ field  ·  ↵ change  ·  s save profile  ·  p profiles  ·  q quit
  • One small binary. Under 15 MB with the desktop app (it uses the system webview through Wails, nothing bundled), 7 MB for the terminal-only build. macOS, Linux and Windows.
  • Edits config files surgically. Only the one key you change is touched; comments, ordering and indentation in your settings.json, config.toml, opencode.jsonc or config.yaml survive intact. Writes are atomic.
  • One endpoint for every agent. magpie runs a local gateway that speaks OpenAI chat completions, OpenAI Responses and the Anthropic Messages API, and forwards to whichever vendor serves the model. Codex, Claude Code, OpenCode and the rest all point at http://127.0.0.1:3425/v1 and pick from one catalog; the translation between APIs happens in magpie, streaming and tool calls included.
  • Your subscriptions, shared. Sign in to Claude Code, Codex (ChatGPT) or Copilot and that login shows up as a provider: every other agent can use its models through the gateway, with nothing copied and no key to paste.
  • Providers with one field. Pick a preset (Anthropic, OpenAI, Gemini, DeepSeek, Kimi, GLM, MiniMax, StepFun, Qwen, Baidu Qianfan, Tencent Cloud Token Plan, Huawei Cloud MaaS, Volcengine Ark, Mistral, Groq, xAI, OpenRouter, Together, Fireworks, SiliconFlow, NVIDIA NIM, ModelScope, AiHubMix, 302.AI, Ollama, LM Studio…), paste a key, done. Custom vendors need a name and a base URL. magpie never reads keys from your shell environment.
  • Real model lists, nothing compiled in. With a key in hand magpie asks the vendor which models it serves and offers exactly those; the models.dev catalog fills in names, reasoning efforts and the list for vendors that have none, and refreshes itself in the background once it goes stale. Choose which models each provider exposes, or expose them all — a model released this morning is in the picker on the next refresh.
  • Each agent's own model list. Under an agent's name on the Agents page, "Showing 5 / 32 models" opens its list: click a model to take it out of that agent's picker (Codex's /model included, its ChatGPT models too) or put it back; other agents still use it, and a new model is shown.
  • Profiles. Snapshot every agent's settings under a name and switch all of them back in one move.
  • Real logos, no framework. Plain HTML over the system webview; brand icons from lobehub/icons.

Agents

Agent File Fields
Claude Code ~/.claude/settings.json provider, model, opus/sonnet/haiku/fable (through magpie)
Claude Desktop Claude/ + Claude-3p/configLibrary/ in ~/Library/Application Support (%LOCALAPPDATA% on Windows, ~/.config on Linux) provider (its third-party gateway mode: Code and Cowork on magpie, no Anthropic sign-in; restart Desktop)
Codex ~/.codex/config.toml provider, model, effort
Gemini CLI ~/.gemini/settings.json, ~/.gemini/.env auth, model
OpenCode ~/.config/opencode/opencode.json(c) ($OPENCODE_CONFIG_DIR) model, small
OpenChamber ~/.config/openchamber/preferences.json ($OPENCHAMBER_DATA_DIR; magpie's provider in OpenCode's config) model, small (its own defaults, over OpenCode's)
MiMo Code ~/.config/mimocode/mimocode.json(c) model, small
Pi ~/.pi/agent/settings.json model
OmO (omo-ai) ~/.omo/agent/settings.json (+ models.json; $OMO_CODING_AGENT_DIR, $SENPI_CODING_AGENT_DIR) model
Goose ~/.config/goose/config.yaml model
Cursor CLI ~/.cursor/cli-config.json model
Copilot CLI ~/.copilot/settings.json model
Crush ~/.config/crush/crush.json large, small
DeepSeek Harness (dsh) ~/.dsh/config.yaml ($DSH_HOME) model
Command Code ~/.commandcode/settings.json (+ providers.json) model
fx ~/.fx/settings.json model (a keyless magpie provider)
omp (oh-my-pi) ~/.omp/agent/config.yml (+ models.yml) model
Devin ~/.config/devin/config.json (%APPDATA%\devin\config.json on Windows) model
Hermes Agent ~/.hermes/config.yaml ($HERMES_HOME) model
Kimi Code ~/.kimi/config.toml ($KIMI_SHARE_DIR) model (a magpie provider; magpie's models in Kimi's /model)
Droid (Factory) ~/.factory/settings.json ($FACTORY_HOME_OVERRIDE) model (magpie's models as BYOK customModels, in Droid's /model)
Cline (CLI) ~/.cline/data/settings/providers.json ($CLINE_DIR) model, effort (magpie takes its openai-compatible provider)
Qoder (CLI) ~/.qoder/settings.json ($QODER_CONFIG_DIR) model, effort (a magpie custom provider; needs a Qoder plan with BYOK)
Qoder CN (CLI) ~/.qoder-cn/settings.json ($QODERCN_CONFIG_DIR) model, effort (as Qoder; its own accounts, a Qoder CN plan with BYOK)
Grok Build ~/.grok/config.toml ($GROK_HOME) model, effort
ZCode ~/.zcode/v2/config.json provider (magpie's models in ZCode's picker)
WorkBuddy ~/.workbuddy/models.json ($WORKBUDDY_CONFIG_DIR) provider (magpie's models in WorkBuddy's picker)
OpenHanako ~/.hanako/provider-catalog.json + agents/<id>/config.yaml ($HANA_HOME; its local API while it runs) model (the primary agent's; magpie's models as a provider)
Alma Alma's local API (localhost:23001, while Alma runs) model (Alma's default; magpie's models as a provider)

Provider-scoped agents (OpenCode, MiMo Code, Pi, OmO, Goose, Crush, omp, Hermes Agent) take provider/model. Only agents that are installed or configured are shown.

Providers and the gateway

Every model an agent can pick is spelled provider/model and served by magpie's gateway, so agents never hold vendor keys or vendor URLs. Add a provider, and its models appear in every agent's picker:

magpie presets                          # the vendors magpie knows, grouped: vendors, relays, local
magpie provider add deepseek sk-…       # a preset needs only the key
magpie provider add ollama              # local servers need none
magpie provider add "My Relay" url=https://relay.example.com/v1 key=sk-… models=gpt-5.5,claude-sonnet-5
magpie providers                        # host, key, exposed models, who uses what
magpie provider deepseek                # one provider in detail
magpie provider models deepseek         # re-fetch the vendor's list (add ids to choose which to expose)
magpie provider test deepseek           # one tiny request per API, with latency
magpie provider key deepseek sk-…       # replace the key
magpie provider rm deepseek
magpie models                           # the catalog agents see
magpie claude deepseek/deepseek-chat    # use it

Custom providers take url= (an OpenAI-compatible base), anthropic= (an Anthropic-compatible base), or both, plus responses= when the vendor has a separate Responses endpoint, catalog= to borrow a models.dev list, and models= to name the models to expose. Anything a preset does not know can be overridden the same way.

One magpie can serve several computers (an office one, a personal one): share it on the network (Settings → Share on local network), and on each other computer add it as a Remote magpie — in the app's Add sheet, or magpie provider add remote-magpie sk-magpie-… url=http://192.168.1.20:3425 id=office. Each computer's own magpie still wires its agents, while the providers, routing groups (office/group/…) and usage are the shared one's. A request goes on in the API the agent spoke — Anthropic Messages, Responses, Chat Completions, token counting — and a model the shared magpie's provider serves on another API only is turned into that API once, never on both computers.

Baidu Qianfan's Token Plans are available as baidu-qianfan: a personal (个人版) and an enterprise (企业版) plan and pay as you go, each with its own Chat Completions, Responses and Anthropic Messages endpoints, and a key that works only on its own plan. Add it with magpie provider add baidu-qianfan <api-key> — the id it carried its first day, qianfan-token-plan, is taken too. The plans serve no model list, so the preset carries their documented models; pay as you go serves its own at /v2/models.

Plugins

A subscription magpie doesn't sign in to itself can come from an OpenCode provider plugin: the npm packages OpenCode users install to sign in to a plan (their auth hook) work in magpie as they do there. magpie runs them on Bun, downloaded the first time a plugin needs it, and the plugin signs in, lists the models and makes each request; magpie serves them to agents like any provider's.

magpie plugin add opencode-gemini-auth   # an npm package, or a path to a plugin of your own
magpie plugin                           # the plugins, what each signs in to, and whether you are
magpie plugin login google-plugin       # its sign-in: the method, its questions, the browser or a key
magpie plugin logout google-plugin
magpie plugin off opencode-gemini-auth  # on brings it back; rm removes it; update updates them all

A provider id magpie already has (google, openai, anthropic) is <id>-plugin. In the app, Settings → Plugins adds and removes them, and the providers they sign in to are in Add provider → From plugins.

What a model costs

A call is counted at its effective price: what you set for that provider and model if you did, otherwise what the provider's own catalogue lists, and otherwise what models.dev lists for the model's maker. Out of the box that last one is the whole story, and it is the wrong number for any provider that does not charge list price — a relay reselling at a discount or a multiplier is counted at whatever the model's maker charges. Say what a provider actually charges, and the usage ledger and the session totals use that:

magpie model price relay-a/gpt-5.5                        # what it is counted at, and where that came from
magpie model price relay-a/gpt-5.5 0.12,0.60,0.01,0.15   # input,output,cache read,cache write
magpie model price relay-a/gpt-5.5 --reset                # take your price off this model
magpie model prices                                       # every model you priced

The four numbers are USD per million tokens. All four are asked for, because a price missing one would understate the rest of every call; 0 is a model served at no cost, which is a price, not the absence of one.

The order a price is looked for in is: the price for this model → the price for <provider id>/*, which covers every model of that provider → what the provider's own catalogue lists → what models.dev lists for the maker. --reset removes the first, and says so when a <provider id>/* price is still in force; reset that one by name to take it away too.

A price is one provider's tariff for one model, not the model's own: the same model through two providers is two prices, and each keeps its own. Nothing an agent can see changes. The model list, the agents' own settings and the pickers that choose a model for a background task — an image, a web search, a description — all still work from the catalogue; only the cost reports read the effective price.

Two things worth knowing. The ledger and the session totals re-price when they are read, so adding or changing a price restates earlier figures: they are estimates at the effective price, not settled charges. And a price is per provider and model — usage records do not retain which key or account served a call, so a provider charging different tariffs per account cannot be costed exactly from a single provider-wide price.

What a model takes

A provider that serves a model models.dev does not list, or lists at the wrong size, has a window and a reply limit magpie cannot know. Say what they are:

magpie model context relay-a/gpt-5.5 262144    # the window a request may hold, or 1m
magpie model output  relay-a/gpt-5.5 131072    # the most a reply may hold, or 128k
magpie model context "relay-a/*" 200000        # every model of that provider
magpie model output  relay-a/gpt-5.5 --reset   # take your limit off this model

The * is quoted because zsh treats a name it cannot expand as a command that failed, rather than passing the name on as bash does.

Both are looked for in this order: this model → this provider's * → the provider's own list → models.dev. --reset removes only the value this model has of its own.

A provider you keep unlisted, or switch off, takes them like any other: the numbers are kept, and are what its models take once it is serving again.

A window is a number agents are shown and a routing input: at 95% of the window a request held on a routing-group member moves to one that takes more, so overstating a window makes that move happen too late. A reply limit is advertised in /models and is what a group advertises the smallest of; the gateway does not itself cap a reply by it.

Saving a window or a reply limit writes that number into the model lists magpie keeps in the agents' own files — Pi's contextWindow and maxTokens, OpenCode's limit, and Crush's, droid's, Cline's, Qoder's and Zcode's — which an agent reads at start-up. A session already running therefore keeps the window it began with, while the gateway's own /models and every request from then on are right at once.

Routing groups

A routing group is several models, from one provider or many, that an agent picks as one: group/<id>. The gateway routes each request over every member's keys and accounts together. A model two of your providers serve under the same name becomes a group on its own; the Routing view in the app and magpie group make any other:

magpie groups                           # yours, then those magpie found
magpie group add "Opus anywhere" models=claude/claude-opus-5-5,copilot/claude-opus-5.5 routing=order stays=session
magpie group opus-anywhere              # one group, its models in order
magpie group set opus-anywhere models+=openrouter/anthropic/claude-opus-5.5 routing=usage
magpie group set opus-anywhere models-=copilot/claude-opus-5.5
magpie group rm opus-anywhere           # one magpie found is hidden; magpie group restore <id> brings it back
magpie claude group/opus-anywhere       # use it

routing= is smart (the default: of the subscriptions with quota to spare, the one whose allowance renews soonest first), order (the first model until it can't answer, then the next), rotate (each turn to the next member) or usage (least used first). stays= is how long a conversation stays with the key or account that answered it: auto (the default, while the vendor's cache of it is worth keeping), session, turn or off. models= replaces the whole list, in order; a bare model id works when only one provider serves it.

The app's Import from other apps dialog can copy providers from Claude Code's settings.json (CLAUDE_CONFIG_DIR when set) and Codex's config.toml (CODEX_HOME when set) into magpie. Codex imports custom [model_providers.*] entries with an inline experimental_bearer_token, including fixed headers for custom providers in [model_providers.*.http_headers] and models from [profiles.*] or model_catalog_json. Review the entries before importing; subsequent changes to agent settings are not automatically copied to magpie. Entries that point back to magpie or only name an env_key are skipped.

Signed-in agents as providers

An agent you have signed in to is a subscription with models behind it, so magpie offers it as a provider too. Claude Code (an OAuth login in the macOS Keychain or ~/.claude/.credentials.json), Codex (a ChatGPT login in ~/.codex/auth.json), Copilot (a GitHub login in ~/.config/github-copilot/apps.json), Devin (devin auth login, kept in ~/.local/share/devin/credentials.toml) and Qoder (signed in from magpie with its OAuth device flow, kept in magpie's own config; Qoder CN is its own subscription beside it, for accounts on qoder.cn made with an Alibaba Cloud account or a phone number, which can't sign in on qoder.com) appear in magpie providers and in the Providers tab as signed in as …, with their models spelled claude/claude-sonnet-5, codex/gpt-5.5, copilot/claude-sonnet-4.5 or devin/swe-2-max in every other agent's picker. magpie reads the agent's own credentials each time, refreshes tokens the way the agent does — writing a rotated token back where the agent will find it — and stores nothing but your model picks; sign out of the agent and the provider is gone. The model list is the vendor's own too: magpie asks Anthropic's, Copilot's or Codex's API with that same sign-in, so a model added upstream appears on the next refresh. The ChatGPT backend only streams and rejects a few parameters, so magpie translates non-streaming requests and drops what it would refuse. Claude subscriptions are different: Anthropic classifies another agent's system prompt as third-party traffic even when the OAuth request otherwise looks like Claude Code. magpie therefore drives the genuine local claude binary for every Claude subscription generation. The caller's tools are bridged into that live turn over MCP, and tool results resume the same Claude Code process; Pi, OpenCode and every other agent use this path automatically. The generated harness stays out of Anthropic's system-prompt classifier while its instructions remain part of the user context. This requires Claude Code to be installed and signed in. A Grok subscription (SuperGrok, signed in with Grok Build) talks straight to the Responses API the grok CLI uses, a Devin subscription to the API the devin CLI uses, and a Cursor subscription to the agent API cursor-agent uses, each with the CLI's sign-in and the caller's tools passed through (Cursor's model calls them as MCP tools; none of Cursor's own tools run). Google sign-ins — Gemini CLI's and Antigravity's — talk to Google's Code Assist API directly: magpie reads Gemini CLI's own login from ~/.gemini or signs one in itself, and refreshes the token in memory. Google no longer serves Gemini CLI's sign-in to individual accounts, only to Gemini Code Assist Standard and Enterprise, which need a Google Cloud project named (magpie accounts project gemini <email> <project-id>, or GOOGLE_CLOUD_PROJECT in ~/.gemini/.env). Google may suspend an Antigravity account it sees used outside Antigravity, so magpie asks before adding one; use an account you can afford to lose.

Connecting anything else

The gateway listens on 127.0.0.1:3425 (MAGPIE_ADDR changes it) and starts with the app; magpie serve runs it alone. It exposes:

Path API
/v1/chat/completions OpenAI chat completions
/v1/responses OpenAI Responses
/v1/messages Anthropic Messages
/v1/messages/count_tokens Anthropic token counting
/v1beta/models/{model}:generateContent Google Gemini (also :streamGenerateContent, :countTokens)
/v1/models, /v1beta/models the catalog

Each /v1/models entry includes reasoning and supported_reasoning_levels ([{"effort":"low"}, ...]). A routing group lists only the levels every member supports. native_endpoints (["/v1/messages"]) names the APIs a request for the model is passed straight through on; it is left out of a routing group, and of a model every request to is translated anyway.

Requests pass straight through when the vendor speaks the agent's API and are translated otherwise, streaming, tool calls and reasoning included. The key is magpie (any value works; the gateway only listens on loopback), and models are named provider/model. Anything with a base-URL setting can use it:

Tool speaks Base URL Environment
OpenAI http://127.0.0.1:3425/v1 OPENAI_BASE_URL, OPENAI_API_KEY=magpie
Anthropic http://127.0.0.1:3425 ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY=magpie
Gemini http://127.0.0.1:3425 GOOGLE_GEMINI_BASE_URL, GEMINI_API_KEY=magpie

An optional X-Magpie-Account: <account> header (the account's email or login, or its id on the Routing page) pins a request to one account of a subscription with several: only it is tried, and an unknown account, one whose plan lacks the model, or one resting is an error rather than another account's reply. The header is not sent on to the vendor.

The Gateway tab in the app has this as copy buttons and ready-made snippets (shell, curl, Python, Node) for each API, the list of model ids, and the recent calls; MAGPIE_DEBUG=1 logs every call to the terminal.

Claude Code gets ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN and the model variables in the env block of settings.json; picking a native model (opus, sonnet…) removes them and restores whatever was there.

Codex gets a [model_providers.magpie] table, model_catalog_json pointing at ~/.codex/magpie-models.json (written from the catalog, so the models show in Codex's own list) and a valid model/effort; picking a native model removes all of that. Your ChatGPT sign-in is never touched. Codex reads its model list at start-up, so restart it after a switch.

OpenCode, Pi, Crush get a magpie provider entry and magpie/provider/model.

Gemini CLI switches auth between API key, Google account and Vertex; the API key goes to ~/.gemini/.env. Picking a catalog model points GOOGLE_GEMINI_BASE_URL at the gateway (which speaks the Gemini API), sets auth to API key with the gateway token, and names the model in settings.json; a native model puts the previous auth back.

A vendor or relay can hand its users a ready-made provider as a link:

magpie://import?preset=deepseek&key=sk-…
magpie://import?name=Acme%20Relay&chat=https://api.acme.example/v1&anthropic=https://api.acme.example&key=sk-…&models=gpt-5.5,claude-sonnet-5

Opening one brings up magpie with what the link would add: the name, the hosts your prompts and key would go to, the models. Nothing is saved until you press Add. magpie import <link> does the same in a terminal.

Parameter Meaning
preset a preset id (magpie presets); its endpoints are used
region with a preset that has regions, which one
name the provider's name; required without a preset
id its id; derived from the name when absent
key the API key; the user pastes one when absent
chat OpenAI Chat Completions base URL (…/v1)
responses OpenAI Responses base URL (…/v1)
anthropic Anthropic Messages base URL (the root, without /v1)
models model ids to expose, comma separated
catalog models.dev provider id, for model names and reasoning levels
website, keys the vendor's site and its API-key page (https)
icon an https picture of the vendor's own (PNG, JPEG, GIF, WebP, ICO, SVG, at most 1 MB). magpie downloads it once, after you confirm the import, into its icons folder; without one it falls back to the catalog's logo or a plain mark

Base URLs must be https (plain http only to this machine or the local network). Web pages and GitHub don't link custom schemes reliably, so link to https://usemagpie.ai/import#<same parameters> instead: it opens magpie, and offers the download when it is not installed. The parameters stay in the fragment, which browsers never send to a server. The full guide, with a link builder: https://usemagpie.ai/docs/import.

Install

Download the app for macOS, Windows or Linux from usemagpie.ai, or install it from a terminal (on Linux, the desktop app when WebKitGTK 4.1 is installed, the command otherwise):

curl -fsSL https://usemagpie.ai/install.sh | sh

Mac releases are signed and notarised; the Windows and Linux builds are not signed yet (Windows SmartScreen may ask before the first run). Every build keeps itself current: the app downloads a new version in the background and installs it when you restart (Restart to Update in the menu) or quit; magpie update does the same from a terminal. Every release is on yetone/magpie-releases.

From source:

go install github.com/yetone/magpie@latest

or build locally:

make build            # ./magpie with the desktop app (needs cgo + the platform webview)
make app              # macOS: magpie.app, a menu bar app with no Dock icon
make cli              # terminal-only build, no cgo, cross-compiles anywhere
make release          # dist/: native app build + cli builds for every platform
make release-windows  # dist/: the Windows app, amd64 and arm64 (cross-compiles)
make release-linux    # dist/: the Linux app for this machine's arch

Linux needs libgtk-3-dev and libwebkit2gtk-4.1-dev for the app build (the Makefile adds the gtk3 tag; with plain go build, pass -tags gtk3); Windows uses the WebView2 runtime that ships with the OS.

Docker

docker build makes a server image: the terminal-only binary on distroless, run as nonroot, with magpie's files in a volume at /config.

docker build -t magpie .
docker run -d --name magpie -p 127.0.0.1:3425:3425 -p 127.0.0.1:3430:3430 -v magpie-config:/config magpie

3425 is the gateway for agents. Until it is shared (below) it takes any key, Bearer magpie included, from anyone who reaches it, so the ports above are published on the host's loopback only; Docker's -p 3425:3425 would put it on every interface of the host, past its firewall. To reach it from other machines, turn on Settings → Share on local network in the browser UI (or put "lan": true, "lanKey": "sk-magpie-…" in /config/magpie/settings.json): from then on a request from outside the container must carry that key as its API key, and only then publish the port beyond 127.0.0.1.

For the browser UI run the image with magpie web --addr 0.0.0.0:3430 --no-open in place of the default serve, and open http://localhost:3430/?k=<key from docker logs magpie> (set MAGPIE_WEB_KEY to keep one key across restarts). There you add providers and import sign-ins from a file; a subscription sign-in started in the container cannot finish, because the vendor sends the browser back to the container's own loopback, so sign in on a machine where magpie runs with a browser. Keys and sign-ins live in the volume, so a restart keeps them.

Developing

make dev

builds with -tags dev and opens the app with the UI served straight from internal/gui/assets: save app.css, app.js or index.html and the window reloads itself. With fswatch installed (brew install fswatch), a change to a Go file rebuilds and relaunches the app too. The dev build uses its own gateway port (DEV_ADDR, default 127.0.0.1:3426), so a magpie you already run keeps serving your agents. Point it at a scratch home to keep your real agent configs out of it:

HOME=/tmp/magpie-home XDG_CONFIG_HOME=/tmp/magpie-home/.config make dev

MAGPIE_THEME=light|dark forces the palette and MAGPIE_DEBUG=1 prints what the gateway translates.

Use

magpie                          # open the app: a window plus the menu bar icon
magpie tray                     # menu bar icon only (use this in your login items)
magpie tui                      # the same thing, in the terminal
magpie web                      # the app's window in a browser (WSL, a server over SSH); --lan, --addr, --no-open
                                # (a new key each run; MAGPIE_WEB_KEY keeps one, for a page run as a service)
magpie ls                       # list every agent and its current settings
magpie claude opus              # set a model (agent names accept prefixes: cc, oc, gem …)
magpie codex gpt-5.6-sol
magpie codex effort high        # other fields
magpie codex xhigh              # bare effort levels are recognised too
magpie codex deepseek/deepseek-chat   # any catalog model, through the gateway
magpie claude moonshot/kimi-k2.5
magpie claude haiku deepseek/deepseek-v4-flash   # one tier on its own model
magpie claude haiku ""          # back to the main model
magpie gemini auth api-key
magpie opencode anthropic/claude-sonnet-5
magpie oc small anthropic/claude-haiku-4-5
magpie mimo anthropic/claude-sonnet-5

magpie save work                # snapshot everything as a profile
magpie use work                 # switch back
magpie profiles
magpie rm work

magpie sync                     # refresh the models.dev catalog and every live model list

In the app, click any value to open a filtered list; type to search or to enter something that is not listed; esc closes the panel. Profiles are the chips at the bottom: click to apply, × to delete, + save current to add. The Providers tab of the window lists your providers with the agents on each; click a row to change the key or the exposed models, Test it, or click an agent icon to point that agent at one of its models. Add provider shows the presets as tiles: pick one, paste the key.

On macOS, Settings → Preferences → Session terminal chooses which installed app opens a session from the terminal button in Usage → Sessions. The list contains apps registered to open .command files, with the current system default listed once. With no saved choice, magpie follows that default, or Terminal when the default is not a terminal. The Resume button still copies the command.

Keys in the terminal version:

Key Action
↑ ↓ choose agent
← → choose field (model, effort, small …)
↵ open the picker
type filter; enter accepts custom values
s save current setup as a profile
p apply or delete (ctrl+d) a profile
S sync the model catalog
q quit

Agents read their config at startup, so a running session keeps its model until you start a new one.

Moving to another machine

magpie backup                   # writes magpie.magpie-backup, asks for a passphrase twice
magpie backup --no-keys ~/b.magpie-backup   # the same with no API keys in it
magpie restore magpie.magpie-backup         # on the other machine
magpie restore --no-agents b.magpie-backup  # providers, settings, profiles; agents left as they are
magpie restore --no-library b.magpie-backup # the library here left as it is

A backup holds your providers (with their keys, unless --no-keys), the pictures picked for them, the settings, the profiles, every agent's model and the library (unless --no-library): the instruction sets, the MCP servers and the skills with their files (a file over 2 MB is left out). Without keys, a server's environment variables and headers that look like a key go empty. Restoring the library replaces the one there — what it replaces is kept with the library's backups — and writes it into the agents on that machine. It is encrypted on your machine (AES-256-GCM, the key derived from the passphrase with PBKDF2-SHA256); nothing in it can be read without the passphrase. Restoring replaces providers with the same id and adds the rest; one that came without a key keeps the key already there. Agent models are set only for agents installed on that machine. Subscriptions are not in it: sign in to them on each machine. Piped in, the passphrase is the first line of stdin.

Keeping machines in sync

Settings → Sync and backup → WebDAV or S3 sync keeps the same backup on a server and brings every machine up to date with it, every 3 minutes while the gateway runs. Choose one of these:

  • WebDAV: a folder on a WebDAV server such as 坚果云, Nextcloud or a Synology.
  • S3: a bucket on AWS S3, Cloudflare R2, Backblaze B2, MinIO, Garage, a NAS or any other S3-compatible server.

The file is sealed on your machine with the passphrase, so the server only ever stores ciphertext. Each machine writes only over the version it read (a conditional write), so an update that another machine made in between is merged rather than lost.

magpie webdav on https://dav.jianguoyun.com/dav/ user=me@example.com
magpie s3 on s3://my-bucket/magpie endpoint=https://<account>.r2.cloudflarestorage.com access-key-id=…
magpie s3 on s3://backups endpoint=http://nas.local:9000 path-style=yes access-key-id=…
magpie s3                       # where it syncs to and how the last sync went; magpie s3 now, off

For S3:

  • endpoint is empty for AWS.
  • region defaults to us-east-1, or to auto on R2.
  • path-style=yes puts the bucket in the path, which MinIO and most servers you run yourself need.
  • The secret is asked for and saved like the WebDAV password. It is used only with the endpoint and access key it was given for.
  • The bucket must already exist.
  • The access key needs to read and write <prefix>/magpie/. On AWS it also needs to list the bucket.
  • A server without conditional writes is supported. There magpie checks the object's ETag just before each write.

Files

  • ~/.config/magpie/profiles.json — saved profiles
  • ~/.config/magpie/providers.json — your providers, keys included (0600)
  • ~/.config/magpie/stash.json — values magpie replaced, restored on switch-back
  • ~/.config/magpie/plugins.json, plugins/ — the plugins added, and their packages
  • ~/.config/magpie/plugin-auth.json — the plugins' sign-ins (0600)
  • ~/.cache/magpie/bun/ — the Bun plugins run on
  • ~/.cache/magpie/models.json — models.dev catalog (OpenCode's cache at ~/.cache/opencode/models.json is used when present)
  • ~/.cache/magpie/models/<provider>.json — model lists fetched from vendors

XDG_CONFIG_HOME and XDG_CACHE_HOME are respected.

Counting users

Once a day, a running magpie (the app, or magpie serve) sends one event to PostHog so we know how many people use it: a random id made up on your computer (~/.config/magpie/install-id), magpie's version, and your system and architecture. Nothing else goes: no accounts, keys, providers, models, prompts or usage. Turn it off in Settings → Privacy → Count me as a user, or with DO_NOT_TRACK=1 or MAGPIE_NO_STATS=1. Builds from source never send it. The code is internal/stats.

Community

Questions, setups worth sharing, ideas, bugs: come talk to us and other magpie users on Discord. Issues and pull requests are welcome here too.

License

MIT. See LICENSE.

S
Description
Every agent's model. One place. Codex on DeepSeek, Claude Code on Kimi, from the menu bar.
https://usemagpie.ai
Readme MIT
103 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 63.1%
JavaScript 31.8%
HTML 3.2%
CSS 1.7%