main
130
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ddd4f43144 |
provider: a routing group can be duplicated, from its menu on the Routing page or with magpie group copy (lc on Discord)
provider.CopyGroup(id, name) saves a copy of the group as it is now: its
models and patterns, routing, stays, rules, classifier, effort, context,
levels, and fast and off members. The copy is deep, so its rules' time
windows aren't shared. Its name is the one given, or "<name> copy", with a
number added past a name in use. Its id comes from the name, numbered past
any id in use, removed ones included. It is listed right after the group
it copies. The copy is a group of the user's and switched on, even when
it copies a found group or a switched-off one. The original is unchanged.
Surfaces:
- GUI: a group row's menu (right-click, or a click on its logos) has
Duplicate under Move up and Move down. It posts /api/groups/copy with
the id and t("{name} copy"), so the name is in the reader's language.
It asks before discarding an editor with unsaved changes. The status
then says "Duplicated {name}". No click scrolls the page.
- CLI: magpie group copy <id> [name]; duplicate, dup and clone work too.
- zh, zh-TW, ja and de strings. docs/reference.md and the site's start
page (en, zh, ja) list the command.
Run:
- TestGroupCopy (internal/gui): the copy has the models, pattern,
routing, rule and off member, is on, and is listed after the original.
The original stays off and unchanged. A second copy is "Fast copy 2" /
fast-copy-2. An unknown id is refused.
- TestGroupCopyCLI (root): the default name, a given name, a found
group's copy as the user's, and the errors.
- group-duplicate.test.cjs: Chromium and WebKit, en/zh/zh-TW/ja/de at
420px, 10/10. It fails 10/10 on the old routing.js. Siblings
group-arrange, removed-groups-menu-scroll, group-bulk-remove and
click-scroll pass 36/36.
- Sandbox CLI (sandbox HOME): group copy and duplicate made "DupTest copy"
and "DupTest copy 2", listed after DupTest.
- gofmt -l clean. go vet darwin/linux/windows on the root, provider and
gui packages: ok.
|
||
|
|
3ee97def30 |
claude: a server or container without Claude Code can have magpie download Anthropic's own build, asked first and checked against its manifest (Jorben)
A Claude subscription signs in and answers through the local `claude`, so
on a server or in the Docker image, which has none, it couldn't be used
(Jorben on Discord: could it be downloaded on first use, as Bun is).
- internal/claudecode: Anthropic's native build, as claude.ai/install.sh
takes it: downloads.claude.ai/claude-code-releases/latest names the
version, its manifest.json the platform's SHA-256 and size (linux
glibc/musl, darwin, win32). A build that doesn't match both is not kept.
It goes to <cache>/claude-code/<version>/claude, one version kept. Find
takes the machine's own claude first, then the download.
- magpie claude-code [install [--yes]|remove]: says the version, platform,
size, the URL it comes from, where it goes, that its checksum is checked
and that it is Anthropic's software under Anthropic's terms, then asks
[y/N]. magpie accounts add claude offers it when there is no Claude Code
(not on Windows, where one in WSL may be the one).
- the gateway's claudeBinary and the sign-in's claudeExecutable take
claudecode.Find; their "not installed" errors name the command.
- Docker docs (en, zh, ja): Claude sign-in needs `docker exec -it magpie
magpie claude-code install` first; kept in /config/cache.
The Dockerfile is unchanged: distroless cc-debian12 has glibc.
Run:
- go test -tags nogui ./... under a temp HOME with pinned caches: all ok
- gofmt -l: none; go vet (darwin), GOOS=linux and GOOS=windows go vet
-tags nogui ./...: clean; darwin, linux, windows builds
- TestClaudeSubscriptionRunsTheDownloadedClaudeCode fails on the old
proc.FindTool lookup ("Claude Code is not installed"); taking the
checksum compare out fails TestInstallRefusesABuildTheManifestDoesntName
- real: magpie claude-code install --yes downloaded Claude Code 2.1.296
darwin-arm64 (230 MB) into a sandbox HOME, checked; it ran --version
- real, in gcr.io/distroless/cc-debian12:nonroot with the image's /config
layout: install downloaded 2.1.296 linux-arm64 (245 MB) to
/config/cache/magpie/claude-code, it ran `2.1.296 (Claude Code)` there,
and magpie accounts add claude went on to print the claude.ai sign-in
link and ask for the pasted address
- the claudecode tests built for linux/arm64 pass in debian:12-slim
|
||
|
|
4225ba1efb |
site: a partner's /go link and the home page's sponsor card say they came from magpie, as ref=magpie (yetone)
goTarget adds ref=magpie to the partner's key page or website, keeping the address's own query and fragment; an address that names its own ref (a referral code) keeps it as it is. The home page's sponsor card links to yylx.io/?ref=magpie. Run: node --test site/*.test.mjs (27 pass), with new cases for the query, the fragment and a partner's own ref. |
||
|
|
c98160c045 |
gateway: a group's rule can pause one of its models at set hours, days or agents, leaving it out of the group then, not first, not a fallback, not kept by affinity (John on Discord)
John asked for a pause in a group's Rules, e.g. a model paused in its vendor's peak hours. A time rule could only send those turns to another member first: the paused-for model stayed behind it for failover, went first when the other was unready, and was kept by affinity mid-turn. What changed: - provider: Rule.Pause (`"pause": true`, typed `pause=<model>`). It holds by time/days and agents only; cleanRules and ParseRule refuse any other condition, and refuse a pause with none. Pause rules are not send-to rules: MatchesBesidesIntent, ruledEntry and Ruled() leave them out. PausedOut(g, ms, agent, at) drops members a pause holds for, from the group's own rules and each nested group's (a group named = all its models; a model named = wherever a nested group has it, #625). - gateway: serve and embeddings run PausedOut on ruleClock before planning, on every request. Every member paused -> 503 naming each rule (pausedError). The trace's GroupRef carries Paused. - GUI: the rule editor's "send to" is now an app-menu action button (send to / pause); a pause hides the request conditions (clearing them), keeps hours, days and agents, and isn't saved without one. The group card's rule reads "→ pause <model>", and the Routing trace says which model was paused by which rule. Strings in zh, zh-TW, ja, de. - CLI/TUI: `magpie group rule add <g> pause=<model> time=… days=…`. - docs: gateway-routing.md step 2; start.html en/zh/ja "Hours and days". Run: - gofmt -l clean; go vet -tags nogui ./...; GOOS=linux and GOOS=windows go vet -tags nogui on gateway, provider, tui, root; go build ./... - go test -tags nogui ./... under a temp HOME with pinned caches: all ok. - rules_pause_test.go (3 tests) fails with the PausedOut call in serve removed (the paused model answers mid-turn, as a fallback, and the all-paused case returns 200). grouprule_pause_test.go covers clean, parse/Line, Ruled, Pauses and nested PausedOut. - GUI: routing-pause.test.cjs in Chromium and WebKit, en/zh at 1100px and en/zh/zh-TW/ja/de at 420px: 14/14; all 7 Chromium cases fail on the old routing.js. routing-*, group-*, i18n-keys, gui-ja, gui-de, click-scroll: 618 pass, 4 fail under --test-concurrency=4 (routing-blocked, -idle-frame, -side-calls, -wb-refused), all of which pass run alone; routing-side-calls failed 1 of 6 alone and passes with the old routing.js too, so it is a load flake not touched here. |
||
|
|
34f2078db8 |
site: a partner's website and key links go through usemagpie.ai/go, which counts the click; fetches of the partner list are counted too
The app's own partner counts only come from users who leave the stats on, so a sponsor's reach and clicks are now also counted where the server sees them. /api/partners hands out each partner's website and keysUrl (and a region's) as usemagpie.ai/go/<id>/site|keys[/<region>]; /go sends a "magpie partner go" event to PostHog (partner, link, region, country) and 302s to partners.js's own URL. One in ten list fetches sends "magpie partners fetch" with weight 10 while someone is listed. Events carry a random id each, no install id or address. Ended entries leave the list but /go still resolves them, since added providers keep the links. Privacy text updated in README (en, zh), docs/reference.md and the site docs (en, zh, ja). Run: node --test site/*.test.mjs (27 pass); a scratch copy with a fake partner served rewritten links, 302'd /go to the real URLs and posted both events; PostHog's /i/v0/e/ answered 200 to the payload shape. |
||
|
|
9bb2a467e4 |
agent: Snow CLI is an agent magpie sets up: a magpie profile in ~/.snow, made the active one, that Snow App takes in with "Sync Snow CLI API config" (#1457)
Snow CLI (npm snow-ai, MIT, MayDay-wpf/snow-cli) keeps one profile per file under ~/.snow/profiles (or $SNOW_CONFIG_DIR), names the active one in active-profile.json, and copies it over config.json at every start, headless `snow --ask` included. Each profile is the whole snowcfg: base URL, key, request method and models. Snow App (MayDay-wpf/snow-app) reads ~/.snow/profiles/*.json when "Sync Snow CLI API config" is pressed. magpie writes profiles/magpie.json from the profile the user was on (their settings kept, Snow's OAuth sign-in left out): baseUrlMode base at the gateway's /v1, requestMethod chat, the agent's key, the model as advancedModel and basicModel, and the catalog's window and output (200000 and 64000 when not known, the output cut to the window). It makes that profile active and copies it to config.json. The profile the user was on, whether there was an active-profile.json at all, and a magpie.json of their own are stashed; Disconnect, `magpie snow default`, or picking a model of one of the user's profiles puts those back. A Snow from before profiles (config.json alone) gets the default.json Snow itself would make, so its next start doesn't take magpie's config for the user's. Check reports a base URL, key or request method moved off magpie's; Sync rewrites the profile on a port move. A WSL distro's ~/.snow is found and written the same way. Registered: agents.go, the WSL kinds, SNOW_CONFIG_DIR in agentenv, the npm update spec (snow-ai), the icon (Snow App's own, trimmed to 96px), README, docs/reference.md, docs/subsystems/agent-wiring.md, the site's agent list and start.html (en, zh, ja). Run: - go test -tags nogui ./... in a scratch HOME: 44 packages ok. New: TestSnow, TestSnowOwnProfile, TestSnowNoProfiles, TestSnowUsersMagpieProfile, TestSnowCheckSync, TestAgentSnowDefault; snow added to TestOwnProviderNamedAsMagpies, the WSL more-agents tests and TestMaxTokensWithinContextWindow. All fail before this commit (no agent "snow"). - go vet -tags nogui for darwin, linux and windows; gofmt -l clean. - Real Snow CLI 0.9.2 in a sandbox HOME against a sandbox magpie (`magpie serve` on 127.0.0.1:3571, a fake OpenAI upstream on 3561): `magpie snow fake/pro` made the magpie profile active, `snow --ask` answered through the gateway (POST /v1/chat/completions, model pro, logged as agent snow), and `magpie snow default` left profiles, active-profile.json and config.json byte-identical to before. - GUI: agent-layout, agent-connect, agent-models-picker, agent-field-names and models-add-and-icons in Chromium and WebKit; a Snow CLI row checked at 520-960px in en, zh, zh-TW, ja and de in both engines. No new GUI strings. |
||
|
|
5b008f5a55 |
i18n: Chinese says 子代理 for subagent everywhere, not 子 agent in some places and 子代理 in others (willz on Discord)
willz asked what the difference between 子 agent and 子代理 is. There is
none: the zh and zh-TW GUI strings used 子 agent for the Agents page's
subagent row and subagent effort ("Subagents", "subagents",
"subagent effort" and its hint), and 子代理 for every other subagent
string (Codex subagents, routing's sealed subagent task, subagent model).
Both locales now say 子代理 throughout; zh-TW already used 子代理 for the
rest, so it takes the same term. The zh site docs (intent.html,
start.html) said 子 Agent / 主 Agent and now say 子代理 / 主代理, matching
the GUI's 主代理. ja already says サブエージェント everywhere; the READMEs
don't name subagents. The four GUI tests that expected 子 agent are
updated.
Run: omp-roles, profile-details, subagent-effort, tier-effort,
codex-subagent-model, routing-sealed-task, agent-tweak-fold,
claude-signin, codex-memories(-settings), gateway-mode, mobile-web,
usage-ledger-detail, gui-zh-tw, gui-ja, gui-de and i18n-keys in Chromium
(148/148) and WebKit (79/79); subagent-effort also at 420px wide in both
engines, with the zh screenshot checked (子代理推理强度).
|
||
|
|
ca3394ff80 |
docs: the community's plugins update by themselves every hour, not every six hours (en, zh, ja)
internal/plugin/updates.go's KeepUpdated first runs 30 seconds after magpie starts, then every updateEvery, which is time.Hour. #681 cut it from six hours. The Plugins page (plugins.js) already says "within the hour". site/public/docs/plugins.html, zh/ and ja/ still said six hours. Other "six hours" mentions were checked and left alone, since the code agrees with them: - the registry.json fetch on the same pages (market.go: 6*time.Hour); - the move retry in docs/subsystems/provider-plugins.md (migrate.go: moveRetry = 6*time.Hour). Bun's own check (bunEvery) is six hours, but no doc states it. No README or docs/ page states the plugin update interval. Run: grep across README*.md, docs/ and site/ for six hours / 6 hours / 六小时 / 6 小时 / 6 時間. No code changed. |
||
|
|
ba0e2a8aa9 |
plugin: Update all and magpie plugin update bring a plugin pinned to an older version to npm's newest, so the update prompt clears (ARNO on Discord)
ARNO: 非官方插件安装后就算更新到最新版,magpie依然提示有更新. A third-party plugin added as name@1.2.3 is pinned. The Plugins page still counts it under "Update all (n)", its row says "vX out" and the Plugins dot is on. Update all (and `magpie plugin update`) reinstalled the pinned version, reported "Plugins updated", and the prompt stayed. The row's own Update already installs the newest version and unpins the spec (name@latest). Update now does the same for a pinned plugin when npm has a newer version than the one installed: it installs that version through add, which keeps the plugin's options and the provider picked for it. A pinned plugin that is already at npm's newest keeps its pin. Git plugins, folders and plugins npm didn't answer for behave as before. The plugin docs (en, zh, ja) now say that updating a pinned plugin yourself unpins it. Reproduced in a sandbox HOME with the real opencode-gemini-auth from npm (magpie web on a spare port, China mirror switch on and off). Pinned at 1.4.15 with 2.0.1 out: before the fix, Update all and `magpie plugin update` left it at 1.4.15 and /api/plugins/updates still listed it. After the fix it is at 2.0.1 as opencode-gemini-auth@latest, the waiting list is empty and Check for updates reports "current". The row's Update (upgrade) path was checked too: unpinned opencode-gemini-auth, and scoped @gitlab/opencode-gitlab-auth 1.3.1 -> 1.3.3 with the mirror on. Both cleared the prompt before the fix as well. TestUpdateAllUpdatesAPinnedPlugin (real bun, fake registry) fails without the change: "Update all left opencode-someones-auth at 1.0.0, want 2.0.0". gofmt -l, go vet (darwin, GOOS=linux, GOOS=windows) on internal/plugin, and go test -tags nogui of internal/plugin, internal/gui and internal/provider under a temp HOME with pinned caches all pass. |
||
|
|
83c963c466 |
gateway: a gateway MAGPIE_ADDR puts on the network asks callers from other machines, a container's host among them, for an enabled gateway key, as sharing does
What changed - lanGuard: a request whose peer isn't loopback (or that carries a forwarding header) is remote. While the gateway is shared, or MAGPIE_ADDR names a host other than loopback/localhost (OnNetwork: 0.0.0.0 in the Docker image, a server's address), a remote request is answered only with an enabled gateway key, on every route behind the guard; otherwise it is refused (403) as before. Before, a MAGPIE_ADDR on the network with sharing off took remote requests with any key. A remote request let in with a key is marked as shared-mode ones are, so quotas, quota cards, refresh and prices answer it. callerGuard follows the same condition. OpenToAnyone is replaced by OnNetwork. - Loopback is unchanged: any key still works on the gateway's own machine and inside the container (HEALTHCHECK included). - CLI keyNote and the console's Connect note no longer describe an open gateway; on the network they say other machines need a gateway key (`magpie gateway-key add <name>`). The console shows the Gateway keys block and network addresses when OnNetwork (gatewayJSON.onNetwork replaces open); the "Open to the network" strings are removed. - Docs: docker.html (en/zh/ja) and remote.html (en/zh/ja) say callers outside the container need a gateway key, how to make one, that sharing isn't needed for it, and what an upgrade means for agents that sent `magpie`; docs/reference.md and providers-accounts.md likewise. A MAGPIE_ADDR pinned to one interface's address now asks a key of this computer's agents calling that address too (documented). Tests - New TestNetworkGatewayGuardsEveryRoute: from 172.17.0.1 and 192.168.1.9 every registered route (model APIs, count_tokens, systemone, the Responses websocket GET, MCP, Codex backend and catalog, /v1/magpie/*, images, embeddings, rerank, videos, Gemini) gets 401 without a valid key; with one, models and quotas answer 200. - TestNetworkGatewayNeedsAKey (was TestExplicitOpenGatewayAccepts...), TestLANGuard, TestProxiedLoopbackNeedsTheKey, TestQuotasOverLAN, TestMCPProxyStaysHere, session route and remote prices tests, TestOnNetwork, serve_cli and gui public_url tests updated. With the old lanGuard restored, TestNetworkGatewayNeedsAKey, TestNetworkGatewayGuardsEveryRoute, TestLANGuard and TestProxiedLoopbackNeedsTheKey fail. Run - go test -tags nogui -count=1 ./internal/gateway ./internal/gui ./internal/provider . under a temp HOME: ok. - gofmt -l clean; GOOS=linux and GOOS=windows go vet -tags nogui on those packages: clean. - GUI: public-url and gateway-caller-keys tests, Chromium and WebKit: 22/22. - Locally built image (OrbStack), -p 127.0.0.1:3481:3425: from the host, /v1/models with no key, `magpie` and another made-up key -> 401; count_tokens and /mcp/x -> 401; a key from `docker exec ... magpie gateway-key add Host` -> models 200, quotas 200; inside the container wget /v1/models -> 200; healthcheck exits 0. `web --addr 0.0.0.0:3430`: /api/state 401 without the web key, 200 with its cookie. |
||
|
|
d86a4e0568 |
site: the docs have Docker and Remote magpie pages in English, Chinese and Japanese, listed in every docs page's contents and footer and linked from the READMEs (HuanCheng Bai on Discord)
Until now, both were only in docs/reference.md. The new pages are at
/docs/docker, /docs/remote and their zh/ and ja/ versions.
- docker: the image, its tags, ports, the /config volume and the nonroot
user. How to run it with the browser UI (`web --addr 0.0.0.0:3430
--no-open`, MAGPIE_WEB_KEY, the ?k= cookie). Signing in through the
Callback URL paste or `docker exec ... magpie accounts add`. What
/config keeps, the env vars, sharing with gateway keys, and a Compose
file for a NAS. Healthcheck and updates.
- remote: sharing the main magpie, then adding it as a Remote magpie
from the app, the TUI (a/w/m) or `magpie provider add remote-magpie
<key> url=... id=office`. What comes over (models, groups, APIs,
images, System One, embeddings, prices, quotas) and the errors magpie
shows, quoted from remote_quotas.go and lan.go.
The facts were checked against the code, not reference.md:
- the Dockerfile ENV, VOLUME, HEALTHCHECK and EXPOSE;
- docker.yml's tags;
- web_cli.go;
- the lanGuard in internal/gateway/lan.go;
- the remote-magpie preset.
The Docker page's warning says a Docker gateway answers anyone until
sharing is on, because MAGPIE_ADDR is 0.0.0.0 there.
The new links in the top nav bar carry a toc-only class:
- docs.js still lists them in the contents;
- the bar keeps today's width, since nine links overflowed it at
1024-1280px.
`.steps li` gets min-width: 0, so a long inline code no longer widens
the grid past a phone's width, where `overflow: hidden` clipped the
text.
Run:
- Real containers from ghcr.io/yetone/magpie:latest (v0.1.1150):
- the documented `docker run ... web` came up healthy, and its log
output is what the page shows;
- with "lan": true, `Bearer magpie` got 401 and a gateway key got 200;
- a second container ran `provider add remote-magpie <key>
url=http://<a>:3425 id=office` and listed office/test-relay/... models.
- `node --test site/*.test.mjs`: 23 pass.
- Playwright, in Chromium and WebKit, at 1280, 1024 and 390, light and
dark:
- no horizontal scroll and no nav overflow on all 20 docs pages;
- every internal link and #anchor resolves. /zh/ and /ja/ are routed
by the worker and return 200 live.
|
||
|
|
7d0e415e62 |
plugins: two plugins that sign in to one provider are both listed, each row says which serves it, and Use for picks the other (neiko on Discord)
neiko on Discord installed a third-party plugin beside a plugin of their
own, added by its folder. Only the third-party one could be seen. Both
plugins' auth hooks name the same provider id. The host's auths() keeps
the last plugin in plugins.json for an id, as OpenCode does, so the
user's own plugin silently lost it:
- its row read "Signs in to nothing magpie can use";
- its provider was gone from the Add sheet;
- keepUnloaded could list the id a second time, from the
plugin-providers.json kept before.
A package-name clash and a display-name clash were tried as well. Both
plugins stay visible in those cases, because a folder plugin's name is
its path.
What changed:
- host.js:
- init gives each loaded plugin the ids it signs in to (provides) and,
for each id another plugin serves, that plugin's spec (servedBy).
- auths() honours plugins.json's new prefer map (id -> spec).
- Only the serving plugin's provider.models hook runs for the id.
- plugin:
- Clashes() turns provides and servedBy into {id, by, with} per spec.
- Prefer() records a pick and restarts the host.
- Remove drops the removed plugin's picks.
- Re-adding a plugin under a new spec carries its picks over.
- remove, off, options and prefer find a plugin by its exact spec
before its package name (indexOf), so acting on one plugin never
reaches another of the same package.
- keepUnloaded no longer re-adds a provider from before when another
plugin serves its id now.
- GUI:
- /api/plugins rows carry clashes, with the provider's listed name.
- POST /api/plugins/prefer {spec, provider}.
- The serving row says "{other} signs in to {name} too; this one
serves it".
- The other row says, in amber, "{name} is served by {other}, which
signs in to it too", with a "Use for {name}" button.
- New strings in zh, zh-TW, ja and de.
- CLI: `magpie plugin` prints the same lines, with the command that
fixes it, and `magpie plugin use <name> <provider>` makes the pick.
- Docs: docs/subsystems/provider-plugins.md, docs/reference.md and
site/public/docs/{,zh/,ja/}plugins.html.
What was run:
- Reproduced under a sandbox HOME with magpie web on 3562: a fake npm
plugin and a folder plugin, both on id "acme".
- Old binary: the local row had providers [] and /api/providers
listed acme twice.
- New binary: both rows carry the clash; prefer moves acme to the
folder plugin; remove clears the pick and leaves the other plugin
serving acme.
- New Go tests:
- TestTwoPluginsOneProvider, a real Bun host: Prefer, restart,
off/on and Remove.
- TestClashes, TestPluginNamedExactlyFirst and
TestKeepUnloadedLeavesAServedID.
- TestPluginsClashRows (gui) and TestPluginListSaysClash (CLI).
- Each fails when its piece is broken by hand: prefer ignored in
host.js, the keepUnloaded id check removed, name before exact spec,
Remove keeping picks, no provides, no clashes in the rows, no
prefer route.
- plugin-clash.test.cjs, in Chromium and WebKit, in en, zh, zh-TW, ja
and de, at 1100 and 560px: 22/22 pass. It times out on the old
plugins.js.
- gui-de, gui-ja, gui-zh-tw, i18n-keys and every plugin-*.test.cjs:
248 tests.
- 245 passed in the 4-wide run.
- plugin-moved-signin (webkit) failed once in that run, then passed
3/3 alone and 26/26 in a smaller 4-wide rerun.
- gofmt -l; go vet ./...; GOOS=linux and GOOS=windows go vet -tags
nogui ./...; go build ./...
- The full go test -tags nogui ./... under a temp HOME with the caches
pinned: 44 packages ok, none failed.
- After rebasing onto
|
||
|
|
a3a8e25792 |
plugins: a repository tagged magpie-plugin shows the icon its package.json gives (yetone: 非官方的插件能否支持 icon)
magpie.icon, an https URL or a data:image URI, is read with the rest of package.json and kept through pluginOwnIcon as an installed plugin's is; the card shows it, else the puzzle piece. Ran: go test ./internal/plugin ./internal/gui ./internal/provider; the plugin GUI tests in Chromium and WebKit (en/zh/ja/de, 980 and 440). |
||
|
|
b4aa62af96 |
docs: the READMEs' Privacy and the site's "What does magpie send about me?" (en, zh, ja) say a partner's daily counts are sent too
|
||
|
|
f9a1ca0355 | docs: a Privacy section in the READMEs and a "What does magpie send about me?" FAQ on the site's Get started (en, zh, ja): the daily event's id, version and system, and the agents, providers and models by magpie's own ids (a provider of the user's own only "custom"), what is never sent, and the Settings → Privacy switches; node --test site/*.test.mjs passes | ||
|
|
10578ce683 |
providers: the add sheet lists Partners first, above Subscriptions, under a heading that says they sponsor magpie; who they are comes from usemagpie.ai/api/partners (site/partners.js), so a paid listing changes with a deploy, not a release (yetone)
The app fetches the list behind the call every 6 hours (10 minutes after a failure), never waiting, and keeps it in the cache folder. Each entry is checked: its own id, never a built-in preset's; a name and an endpoint; https only; at most 12; no decision API, no-key or user endpoint. Its logo goes through FetchIcon. A partner is a preset to the rest of magpie, and one that has left still is to the providers added from it, unsponsored. The window lists a partner in the languages it names only, with its tagline in the page's language; `magpie presets` and the TUI list them first too. MAGPIE_PARTNERS moves the list, off lists none. Run: go test -tags nogui -race ./internal/provider ./internal/gui ./internal/tui . (ok); TestPartners* -race -count=3; node --test internal/gui/tests/partners.test.cjs add-sheet gui-ja gui-de i18n-keys in Chromium and WebKit (en/zh/ja/de, 900 and 440px); node --test site/worker.test.mjs site/install.test.mjs; the built binary in a sandbox HOME against a local feed: listed, an http entry left out, listed from the cache with the site down, `provider add` from it. |
||
|
|
f0a431651e | site: the Agents screenshots show today's page: eight agents on magpie (Claude Code, Codex, Gemini CLI, Antigravity CLI, OpenCode, Zed, Hermes Agent, Kimi Code), Show 21 more, the Sessions and Plugins tabs; en, zh and ja, light and dark; the docs give their new heights | ||
|
|
586f2bce8f |
context: magpie inspects the context window of every request, session and agent
The gateway reads each request's prompt into its parts (system prompt, tools and MCP servers, memory, files, tool results, chat), measured against the model's window and calibrated to the tokens the vendor counted. Routing shows a request's window as a card: a 400-cell grid by part, a hover naming what is largest in a cell, and the contents by part. The Usage page's Context tab scores each agent out of 100 (cache reuse, how lean it starts, how fast it grows, how reliably it answers), tags it (lean, cache-friendly, mcp-heavy, compacts, near-full…), and draws each session's growth with its latest request's card. Older Safari (15) gets media-query and solid-colour fallbacks for the card's container queries and color-mix. |
||
|
|
b7f1c7d2aa | docs: Intent routing's setup names Route by what it asks for, Jev as the classifier with a TypeSafe provider, and the offer under a group whose effort Jev picks (en, zh, ja) | ||
|
|
8b14afea15 | docs: Plugins says magpie asks GitHub for tagged repositories at most every ten minutes (en, zh, ja) | ||
|
|
290c60a31e | docs: Plugins says a GitHub repository tagged magpie-plugin is listed under Discover as unofficial (en, zh, ja) | ||
|
|
780b3fa3da | docs: the FAQ says how to let an agent reach magpie through Surge, Clash or mihomo: rules that send 127.0.0.0/8 and ::1 direct (amor90s, baksili_ on X: Codex got 502 from the proxy and magpie saw nothing) | ||
|
|
df95ac6bc2 |
docs, site: Integrating your app or agent with magpie, at docs/integrating.md and usemagpie.ai/docs/integrate (#918, Magixyne)
A developer of an AI app asked how to work with magpie and whether an app can register itself after one confirmation. The page says what exists: the gateway (find it via /api/hello and settings.json's port, the OpenAI, Anthropic, Responses and Gemini endpoints, /v1/models' fields), how a key magpie-<id> or the User-Agent's first word names the app in Usage, a row on the Agents page by an adapter PR in internal/agent (config-edit, Cindy-style own import link confirmed in the app, or a local API) with what the PR needs, what plugins can and can't do, and what isn't there: no link or API registers an app or installs a plugin, and magpie://import adds providers only. English and Chinese pages, linked from every docs page's nav and footer. TestThirdPartyAppIsCountedAsItself pins the attribution the page promises. go vet and the gateway tests pass; site node tests pass; pages checked in headless Chromium (no errors, TOC, no horizontal scroll at 390px). |
||
|
|
2f135ed4d5 | plugin, gui, docs: Plugins says which plugins are providers and which are middleware, lists the community's ready-made middleware, and edits a middleware's options in its row (the owner) — the Plugins page still read as if every plugin were a subscription, and nothing showed what a middleware does or let you set its options without editing plugins.json. Every Discover card and Installed row now carries a Provider or Middleware chip (both for a plugin that is both), also for one that is off (isMiddleware/middlewareOnly from the server, read from its files); Discover splits into Subscriptions and Gateway middleware ("runs in magpie's gateway on what every agent sends and gets back, whichever provider serves it"), the latter from market entries with "kind": "middleware" and no providers or icon (the built-in market.json lists the five new @magpie-community/middleware-* packages: param-override, model-map, system-prompt, word-guard, think-tags, mostly New API's channel settings with the same JSON). A middleware row has Options: a JSON editor in the row, starting from the package's magpie.options example when none are set, which validates it is an object, saves (POST /api/plugins/options) for the next request, clears, and cancels with Esc. magpie plugin options takes a short name (model-map for @magpie-community/middleware-model-map; plugin.ShortName, also in SetOptions). en/zh/ja/de. Docs: usemagpie.ai/docs/plugins (en/zh/ja) gains Ready-made middleware and Options and the registry's kind; reference.md, both READMEs and docs/subsystems/gateway-middleware.md likewise. Tested with a real DeepSeek account and real Claude Code through magpie web --gateway in a sandbox HOME, with all five installed and options set by short name: a mapped model named back, max_tokens capped, a prompt appended for claude only, word-guard's 400 and param-override's 403 in the agent's shape, <think> stripped from Chat and Responses streams and a whole Anthropic reply, reasoning_content moved into the text; /api/plugins showed the kinds and the example. TestSetOptionsShortName is new; TestBuiltinMarket checks a middleware entry has no providers or icon and no other kind. plugin-middleware.test.cjs (Chromium and WebKit, en and zh: the chips, Discover's two sections, the options editor with its errors, no select, no scroll, no left border) fails without the change; the other plugin tests and click-scroll pass in WebKit (plugin-market's section name updated; plugin-move fails only under parallel load and passes alone). go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass. | ||
|
|
eebd0b4222 | plugin, gateway, gui: a plugin can be gateway middleware — onRequest, onEvent and onResponse run in magpie's gateway on what agents send and get back (moejs) — a plugin could only be a subscription provider, run in the Bun host, a process away from the gateway. A plugin may now declare middleware (package.json magpie.middleware, or a *.middleware.js / .mjs file), run in-process by moejs (pure-Go JS engine): compiled once, one runtime per request from a pool, so every hook of a request shares ctx.state. onRequest(body, ctx) sees the agent's body in its own API before routing; what it returns is the body the next middleware and the gateway get, undefined leaves the bytes as they were, ctx.reject(status, msg) answers with an error in the agent's API's shape. onEvent(ev, ctx) sees each SSE event's JSON (export const events = [...] filters by name; an object changes it, null drops it, undefined passes the bytes through untouched; data: [DONE] is no JSON and passes by). onResponse(body, ctx) sees a whole JSON reply, with ctx.status. ctx has protocol, model, stream, path, agent, the plugin's options and state. A hook that throws, returns a non-object or runs past its limit (250ms request/response, 50ms an event, 1s load; interrupted) leaves the request or reply as it was and is counted and logged (60 lines a minute); a compressed reply passes as-is. Files are reloaded within a second of a change. Only agents' requests run through it (serveAgent at the gateway's agent entry points and the Codex backend), never magpie's own titles, classifier or stand-ins; it sees bodies before redaction. A middleware-only package is skipped by the Bun host. The Plugins page marks a middleware with a chip, its hooks, calls and µs per call (JSON in and out counted), failures in red with the last error, and a load error. en/zh/ja/de; docs/reference.md and usemagpie.ai/docs/plugins#middleware (en/zh/ja) with an alias example. Tested with a real DeepSeek account through magpie web --gateway in a sandbox HOME: an alias middleware served "fast" and named it in message_start, a reject came back as 403 permission_error, a Chat stream uppercased; real Claude Code (sandbox CLAUDE_CONFIG_DIR) did Read, Bash and Glob tool turns through three middlewares with 0 failures: ≈1–1.6µs an event, 0.2–0.7ms an onRequest that parses and rewrites Claude Code's ≈80KB body. Real use found two bugs fixed here: [DONE] parsed as JSON (SyntaxError on every Chat stream) and stats leaving out the JSON time. internal/middleware tests (chain, undefined keeps bytes, reject, fail-open on throw/spin/string with the runtime reused, async hook, load errors, off and Bun skip, reload, split/CRLF/filtered/dropped stream events, [DONE] untouched, whole reply and gzip, 64 in parallel; BenchmarkEvent ≈0.9µs, 11 allocs) and TestMiddlewareThroughGateway (alias routed, events by protocol, redaction still upstream, reject in Anthropic's shape; fails without the change) are new; plugin-middleware.test.cjs (Chromium and WebKit, en and zh: chip, hooks and stats, failures with last error, broken and off, no left border) fails without it. The other plugin GUI tests and click-scroll pass in WebKit. go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass (internal/provider failed once under load and passes alone). | ||
|
|
bd917e7a9a | site: the docs pages take the reader's language and remember the one picked, as the home page does (owner: 为啥不能自动识别用户语言以及自动记住用户上次的选择?) — / already sent a browser preferring Chinese, Japanese or German to /zh/, /ja/ or /de/ and kept the language picked in the menu (the lang cookie), but /docs/start and the other English docs pages were English for everyone and ignored the cookie. Now an English docs page (/docs/<page>, with or without .html) sends a browser whose language the docs are written in (zh, ja) to /docs/<lang>/<page>, query kept, with Vary: Accept-Language, Cookie; lang=en (picking English in the menu) keeps it English, German has no docs and stays English, a page not written in that language yet stays, and /docs/zh/… and /docs/ja/… are never moved. The test fails on the old worker. Under wrangler dev: zh-CN and lang=zh go to /docs/zh/start, intent, plugins; ja to /docs/ja/…; en and zh-CN with lang=en stay; /docs/zh/start stays for everyone. node --test worker.test.mjs passes. | ||
|
|
558abd13ea | site, omarchy: a reload shows the theme the page showed, not one magpie told before the desktop turned (owner: 在 Omarchy 下网页的确可以自动识别 Omarchy 主题切换事件了,但是一刷新又变回了默认的 Omarchy 主题) — with magpie not answering (here the VM had restarted and nothing served 127.0.0.1:3425), the page follows Omarchy only by light/dark: rose-pine set turned it to catppuccin-latte, but localStorage still held miasma, the theme magpie had last told, and a reload painted that. Now when the desktop turns to the other mode while magpie isn't answering, that stale theme is forgotten, so a reload paints what the page showed. Tried in the Omarchy VM in Chromium 152 on a copy of the owner's own profile (its loopback-network grant and storage), omarchy.js served from this change: magpie down, miasma → rose-pine turned the page to catppuccin-latte and a reload kept catppuccin-latte (it went back to miasma before); magpie up, rose-pine came through live and a reload kept rose-pine. | ||
|
|
38cf469fb2 |
i18n: German for the strings main added after #770 (#806)
Max output and its hint and error, the purpose filter and the copy-launch-command button get their German, so gui-de's parity test passes again; the site's download-size line follows the English (a 15 MB download on a Mac). |
||
|
|
21702d01ff |
Add German translation; harden paths against unset env vars (#770)
* Add German translation and harden paths against unset env vars Add a German locale to the GUI, tray menu, usage alerts and website home page, and refuse to start when the home folder is unset or relative so nothing is written under the working folder; the pi plugin no longer adopts the user's PI_CODING_AGENT_DIR, and an inherited APPIMAGE no longer selects portable mode. Scripts refuse to run from the wrong folder or empty a shared out folder, and a new internal/testenv sandboxes every test package from the user's real files, which also fixes the Claude picker and Termux installer tests on Windows. * Makefile: know magpie's folder by its go.mod, so paths with spaces work The check compared $(CURDIR) with $(dir $(abspath …)), which make splits on spaces, so a checkout under 'My Projects' or 'John Doe' refused every target. It now reads go.mod relative to the working folder and wants magpie's module there: make -C and a symlinked Makefile work, make -f from another folder still stops before any recipe runs. * i18n: German for the strings main added since, with a parity test 75 strings main added (group order, patterns, slow-start wait, usage caps, RTK savings …) get their German, and two that main dropped go. gui-de.test.cjs checks, as gui-ja does, that de has every zh string with its placeholders and tags, and that the window reads in German when picked or on a German system. * appdir: ignore relative folder variables when reading, keep them for children CheckEnv no longer unsets anything. magpie reads the XDG folders, APPDATA/LOCALAPPDATA and every agent's folder variable through appdir.Getenv/LookupEnv, which pass over a relative value; the environment stays as it is, so the agents magpie starts get the variable as the user set it. That covers what proc.UserPath lends from the login shell too, which is set after main's check and reached the desktop app unchecked. On Windows a path rooted on the current drive (\Users\x, Git Bash's /c/x) counts as usable, and magpie version and help answer without a home, before run makes any folder. * Sandbox the tests main added since internal/wslrun gets testenv's TestMain, and four tests main added that set HOME alone set their home with testenv.SetHome, so on Windows they don't read the runner's profile through USERPROFILE. * agentenv: PI_CONFIG_DIR and OPENCODE_DB take relative names on purpose omp puts $PI_CONFIG_DIR under the home and OpenCode a relative $OPENCODE_DB in its data folder, so neither depends on the working folder: appdir no longer passes them over. * i18n: German for the Library's multi-select and System One's decision API The 19 strings main added since (#791's skill multi-select, the decision API snippets) get their German; the one main replaced (four APIs → five APIs) goes. * appdir: tidy the import into magpie's group; say what Go makes of /c/x The appdir import joins the group of magpie's own imports where a file has one. rooted's comment now says that Go takes a Git Bash path like /c/x as \c\x on the current drive, not C:\x. |
||
|
|
0e7a1f6b6a | cli: magpie <agent> default takes magpie out and puts back what the agent had before, as the Agents page's Disconnect does (__jingling on X: magpie claude default took the model they had set away with magpie's) — the agent-wide default set its first field to the agent's own default, which for Claude Code is Claude Code as installed: settings.json's model was deleted with magpie's endpoint and tiers, so a user on opus before magpie was left on no model at all. On an agent magpie is wired into it now runs Disconnect (Unwire, then each field magpie set back to its default), which puts back the model, endpoint and token stashed when magpie was wired in; Codex goes back to its own model the same way. magpie <agent> <field> default is as it was, the field to the agent's own default. The README and usemagpie.ai said under 15 MB for the desktop app and 7 MB for the terminal build; v0.1.869's are 27–30 MB (CLI) and 31–35 MB (desktop on Linux and Windows), a 13–16 MB download on a Mac, and say so now (en, zh, ja). In a sandbox HOME: settings.json with // mine, model opus and theme dark, magpie claude deepseek/deepseek-chat, then magpie claude default: model opus back, the env cleared, the comment and theme kept (on origin/main: no model). TestAgentDefaultPutsBackTheModelBefore fails on the old code. go vet, builds (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. | ||
|
|
1d404db710 | site: the Japanese docs show magpie in Japanese (the owner: 日文文档也要有日文截图) — the ja pages of Getting started, Intent routing and Import showed the English window. Each of their nine shots is taken again with the GUI in Japanese (v0.1.851): once in magpie's own dark and light look (/img/<name>-ja-dark|light.png, drawn by Chromium on the Mac like the en and zh ones) and once for every one of the 22 Omarchy themes (/img/omarchy/<theme>/<name>-ja.webp, lossless, in the VM's Omarchy). omarchy.js takes -ja as it takes -zh, with the sizes of the Japanese shots that differ. Alt texts name the tabs as the Japanese UI does. Locally, with the light look and three Omarchy themes, all nine images on the three pages are the Japanese ones, loaded, at their own ratio. | ||
|
|
209dc69b59 | providers: Tencent Cloud is one preset, its regions Plan · China, Pay as you go · China and Pay as you go · Global (Discord, Jorben: Tencent Cloud's Token Plan, TokenHub and TokenHub China were three tiles of one vendor) — tencent-token-plan, tencent-tokenhub-cn and tencent-tokenhub become tencent-cloud's regions plan, cn and intl, as Xiaomi MiMo's are, each made as its own preset made it: the plan's /plan endpoints with no Responses, its own models and no catalog; TokenHub's hosts with Responses, models.dev's tencent-tokenhub and its own keys page and docs. A region now carries its Website, Catalog and Models, and a saved provider's region is found by its endpoints (regionOf, by exact URL before path, since TokenHub China and global share their paths): normalize gives it that region's keys page, docs and catalog (a catalog the user picked is kept), and planModels its models. The old ids are aliases, as qianfan-token-plan is of baidu-qianfan: Preset and FromPreset take them, FromPreset builds at their region, normalize rewrites a saved provider's preset to tencent-cloud, and a provider's own id never changes, so its key, models, routing groups, usage and prices stay where they were; magpie://import links by an old id land at its region, and region= now also gives a region's docs and catalog. Imports from other apps match a region's endpoints. The editor's docs link follows the region picked with the segmented control, en and zh ("按量 · 中国", "按量 · 国际"); the add sheet has one Tencent Cloud tile; the site's import docs list tencent-cloud with its regions and import.html still names the old ids. TestTencentCloud (each region, old ids, FromPreset, imports, import links) and TestTencentCloudOldPresetsKeepWorking (a providers.json saved by an older magpie under all three old presets, with a routing group over them, read, re-saved as the editor saves and switched between regions) fail on the old code; TestTencentTokenHubRoutes routes through old ids and tencent-cloud alike. tencent-cloud.test.cjs (WebKit en and zh: one tile, the three regions, no select, the docs and Get a key links following the region, an old id's provider opening at its region and saved under its id) fails on the old app.js; add-sheet, zhipu-team, system-one, strip-scrollbar and click-scroll pass (Chromium doesn't launch in this sandbox). Not tried with a Tencent account. go vet, builds (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. | ||
|
|
8031edc78b |
agent: AtomCode support — one provider account, one model table per catalog model, as its own sign-in writes (#736)
* agent: AtomCode is wired like its own CodingPlan sign-in — one provider account, one model table per catalog model
AtomCode (atomgit.com/atomgit_atomcode/atomcode, AtomGit's open Rust terminal agent) keeps its settings in ~/.atomcode/config.toml ($ATOMCODE_HOME). Its account schema, what its own CodingPlan sign-in writes, is one [provider_accounts."magpie"] table at the gateway's /v1 (protocol openai, gateway.TokenFor("atomcode") as the key, so a request is its agent's even when the UA is overridden) and one [models."magpie/<provider>/<model>"] table per catalog model: the model string goes to the gateway as it is to resolve, with the model's context window (AtomCode compacts as it nears it; a 128000 one is told when the model's isn't known, and an output above the told window is cut to it), its output limit, vision and reasoning_effort_levels; reasoning_effort rides on the table once the user picks one. Picking a model points default_provider and default_model at it — default_model wins when both are set, as atomcode reads it — and stashes the defaults the user had; stepping out puts them back. The picker offers the user's own model tables and the catalog via magpie; the Library gives AtomCode its ATOMCODE.md, its mcp.json (omp's shape) and skills/; its UA is atomcode/<version>.
Found against a real atomcode 5.2.1: the documented legacy [providers.*] tables work, but its own sign-in writes the account schema, so magpie writes that; reasoning_effort, max_tokens and supports_vision were verified to reach the request from the model table, and default_model to win over default_provider. Verified end to end on a real install: atomcode --provider "magpie/…" answered through the gateway, and the Agents page shows the tile, its picker and the Usage attribution. TestAtomcodeWiring, TestAtomcodeRestoresOwnDefaults and TestAtomcodeSync fail on the old code; TestDriftUnwiredEveryAgent and TestUnsetAgentsReadEmpty cover it from All(). go vet, builds (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui -count=1 ./internal/agent ./internal/library ./internal/agentenv pass; the plugin-host and timing failures of the full ./... run fail on main without this change too.
* tweak
* agent: AtomCode's config is matched by its own spellings, and Sync keeps the effort — AtomCode rewrites its config through serde on every /model and /cd, and that writes the account header bare ([provider_accounts.magpie]) where magpie wrote it quoted, so after the first rewrite Check read the wiring as gone, Sync and every pick appended a second account table (edited TOML is invalid: table magpie already exists) and clearing left the unquoted table and its api_key behind. The account is now matched by exact name in its three spellings (quoted, bare, literal — magpie-2 stays out) and the model tables by their decoded keys (kimiModelKey, replacing atomcodeModelName), through a new SetTOMLTablesMatching that takes exact names beside prefixes; SetTOMLTables delegates with none, unchanged. Sync carries each magpie model table's reasoning_effort over when rewriting — it ran at every gateway start and catalog change and dropped both an effort picked in magpie and one picked with AtomCode's /effort, and picking another model cleared every table's. The effort Set validates against the levels AtomCode knows (low, medium, high, xhigh, max — its clamp_effort_to_levels drops none, minimal and ultra), a model without levels offers none now, and the low/medium/high fallback is gone with it. supports_vision is written explicitly false, where AtomCode otherwise guesses from the model name. The user's legacy [providers.*] entries join the picker, a stashed default_model naming one comes back on Disconnect, and picking one sets default_provider and takes default_model out, as the legacy shape reads. site docs (en, zh, ja) list AtomCode. TestAtomcodeSync rewrites the config into the bare and single-quoted spellings before Sync and again before clearing and fails on the old code (drift reported, effort dropped, unquoted table left behind); TestAtomcodeLegacyProvidersArePickableAndRestored and TestAtomcodeEffortLevelsMatchAgentSupport are new. Reported by yetone on #736. go vet, builds (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui -count=1 ./internal/agent ./internal/edit pass.
* agent: TestAtomcodeWiring picks an effort through the field too — the write side of the validation the last commit added (a level AtomCode knows lands as reasoning_effort on the model table, one it does not know is refused) had no coverage left: the test only read back what AtomCode's own /effort had written. provider.SetModelEfforts gives fake/m1 levels to offer.
* agent: Disconnect puts AtomCode's default_provider back only where AtomCode still has it — the restore checked the stash's value was not magpie's but not that the table it names was still there, so a table the user had deleted in between came back as a dangling default (the account schema's default_provider names a model table, the legacy shape a [providers.*] one; own answers for both). The default_model branch checked already. TestAtomcodeRestoresOwnDefaults deletes the stashed provider's model table before clearing and fails on the old code.
* agent: AtomCode joins the CLI updates — npm @atomgit.com/atomcode, the brew cask atomcode, and the curl script's ~/.local/bin/atomcode updating itself with atomcode upgrade, so the Agents page shows its version and a newer one as the other agents' do (#736). TestHowInstalled covers the npm prefix, the cask and the self-updating installer.
* agent: AtomCode's self-updater waits for its install paths to be confirmed — the branch recognized a binary under ~/.local/bin as `atomcode upgrade`, on a docs page, while the README shows another path and the command has not been driven outside the help text; the reviewer asked for it removed until the standalone command and the script's real default are settled. npm and the brew cask stay, with their tests; a comment marks what is pending.
---------
Co-authored-by: googya <118465+googya@users.noreply.github.com>
|
||
|
|
6942904d59 |
site: the Omarchy screenshots are drawn in the reader's Omarchy theme and turn with it (owner: 我发现一个很严重的问题,就是里面截图的样式不会跟着变)
Before, the site's magpie shots in Omarchy mode came in two sets only, tokyo-night and catppuccin-latte, picked once by light or dark, so a page following rose-pine, gruvbox or white still showed tokyo-night's magpie, and a theme switch afterwards left the shots as they were. Every shot (agents, usage, add, import, routing, providers, panel, picker, intent trace/rules/hit, nested routing; en and zh) is now shot in each of Omarchy 4's 22 themes, in /img/omarchy/<theme>/<name>[-zh].webp. The shots are magpie's own GUI under each theme's /api/omarchy payload in the omarchy VM, same scenes and sizes as before. They are lossless WebP, pixel-identical to the PNGs, 29 MB for all 462 where the PNGs came to 77 MB. The 42 flat -dark/-light PNGs are removed; only omarchy.js used them. omarchy.js's shots() runs on every paint, not once. It takes the theme's own set when it is one of Omarchy's, else catppuccin-latte or tokyo-night by the theme's mode. A shot already swapped loads the next one off-screen and swaps when it is in, so nothing goes blank meanwhile. Checked in the VM's Chromium on the site served from this tree, magpie's theme answered as rose-pine, white, gruvbox, then a theme of the reader's own (light). On / and /docs/zh/intent every visible shot came from that theme's folder, loaded, at its own ratio, and none was blank during a switch. The custom theme took catppuccin-latte. |
||
|
|
a370877ca4 |
agents: VS Code's built-in chat (Copilot Chat) — switched on, magpie's models join Chat's model picker as a Custom Endpoint group in chatLanguageModels.json, with no key to enter and no Copilot sign-in needed (wani on Discord: 可以添加vscode自带的agent的配置吗)
VS Code Stable takes other vendors' models through its Custom Endpoint provider since 1.122 (customoai, the older OpenAI Compatible one, is Insiders only), signed in to GitHub or not. The provider groups live in chatLanguageModels.json beside settings.json in the default profile's User folder (~/Library/Application Support/Code/User, ~/.config/Code/User, %APPDATA%\Code\User), a JSONC array VS Code watches. magpie adds {"vendor":"customendpoint","name":"magpie","apiType":"chat-completions","models":[...]} with each catalog model at the gateway's /v1/chat/completions, toolCalling true (agent mode lists only tool-calling models), vision, contextWindow and maxOutputTokens (capped at a quarter of the window, as VS Code keeps the output budget out of the prompt's), and the model's efforts as supportsReasoningEffort. A group's apiKey is a secret property that VS Code reads from its secret storage only (languageModels.ts _resolveConfiguration), so it can't be written to the file; each model instead sends Authorization: Bearer magpie in requestHeaders, which CustomEndpointOAIEndpoint lets through and which stops it sending its own key header. The model a new chat starts on is chat.defaultModel in settings.json (matched by model id, so the catalog id); its old value is stashed and put back. The group is the agent's Joined: picking one of VS Code's own (auto) keeps magpie's models listed, and switching off (Unwire) takes the group out and puts chat.defaultModel back. Sync rewrites the group as the catalog changes, Check says when its url or header no longer reach the gateway, the Notice says where the models are and to reload the window if they don't show, and requests are credited to it by GitHubCopilotChat/<version>, a User-Agent a model's headers can't change.
internal/edit: SetJSONItem/GetJSONItem/DelJSONItem edit one element of a JSONC file's top-level array, found by its members, keeping comments, trailing commas and the other elements; SetJSON/DelJSON take a backslash-escaped dot as part of a key, as gjson does, for VS Code's flat "chat.defaultModel".
TestVSCode (connect keeps the user's settings and groups with their comments, the group's url/header/toolCalling/window and no apiKey or discovery url, Sync, Check, an own model keeping the group, disconnect back to the user's last own model), TestVSCodeRoundTrip (on and off leaves both files byte for byte; no files at all) and TestVSCodeUA; TestJSONItem, TestDelJSONItemAmongOthers and TestJSONEscapedDot. go vet ./internal/..., go build -tags nogui ./..., GOOS=windows and GOOS=linux builds pass; go test of internal/agent, edit, usage and gateway (each sandboxes HOME in TestMain) and internal/gui's page and agent connect tests pass; the full go test under a temp HOME was not run (the sandbox refused the HOME override). No GUI code changed (the generic connected wording fits), so no webkit test. VS Code itself is not installed here: not tried against a real VS Code.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
5fb7799b11 |
agents: Mister Morph (morph) — picking a magpie model points ~/.morph/config.yaml's llm at the gateway ($MISTER_MORPH_CONFIG names another file) (#728)
Morph has no list of providers to add magpie to, so magpie takes llm over: inference_provider openai_response_compatible (the Responses API, morph's own default), endpoint the gateway's /v1 (morph adds no second /v1), api_key magpie's token, model the catalog id, context_window_tokens the window magpie knows (morph knows one only from its own list), and headers.User-Agent mistermorph so the gateway credits its requests to morph. llm.provider is left alone: morph derives it from inference_provider. What the user had under those keys is stashed and put back when they pick a model of their own or reset; comments, other headers and other keys stay. An effort field sets llm.reasoning_effort. Found by ~/.morph, not by the morph command (a common name). Also on WSL. In NO_PICKER, as morph's /models switches profiles, not models. TestMorph and TestMorphContextWindow. go vet, builds (GOOS=linux/darwin -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass, but for TestPluginProxy, TestPluginSOCKSProxy and TestCodexArchivedSameSizeRewriteInvalidatesDiscovery, which fail on main too. With the installed morph against a sandbox gateway and a mock vendor, morph run's requests went to /v1/responses, were relayed as chat completions and were credited to morph; magpie morph default restored the user's file. Not run against a real model. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
23c26c2dba | site, omarchy: the site in Omarchy's style follows a theme set in Omarchy without a reload (owner: 现在系统换完主题后网页需要手动刷新一下才能切换到对应主题) — a browser that hasn't let usemagpie.ai reach this computer (Chromium's local network access, which nothing on the page asked for) left the page on Omarchy's default for light or dark, chosen from prefers-color-scheme once at load, and a page that had been let through could lose magpie for 30 s when one answer was slow. Now: while not let through, the page repaints as prefers-color-scheme turns (omarchy-theme-set turns GNOME's color-scheme, so Chromium's), and a "Follow my Omarchy theme" / 跟随 Omarchy 主题 button beside the chip asks the browser in one click; a permission turned to granted (the prompt, site settings) starts following at once, denied stops it; once magpie has answered, one failed or slow answer (5 s, was 3 s) keeps the page following and it asks again in 3 s (two in a row mark it absent, still asked every 3 s), and two permissions turning together poll once. The gateway's /v1/magpie/omarchy no longer asks omarchy-font-current and hyprctl again on every theme change, only when the font's or Hyprland's own files change, since a theme set reloads Hyprland and hyprctl asked meanwhile can stall (2 s timeouts each); TestCurrent checks a theme switch keeps the font without asking and fonts.conf changing asks again (fails on the old code). Tried in the Omarchy VM's Chromium with the new omarchy.js/css: not let through, the page went tokyo-night ↔ catppuccin-latte with the desktop; the button raised Chromium's "Access other apps and services on this device" prompt and the page went live on vantablack once granted, no reload; with access allowed, catppuccin-latte, tokyo-night, rose-pine and vantablack came through within 1.5–3 s each, no failed request. go vet, builds (darwin, GOOS=linux -tags nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. The prompt's Allow itself wasn't pressed (granted through the profile and CDP instead). | ||
|
|
c8278a8691 | docs: plugins are OpenCode's provider plugins or pi's packages — the Plugins page's intro, magpie plugin's help, the README and the plugin docs (en, zh, ja) said OpenCode only, though pi packages that register a provider run since v0.1.772; the docs gain a pi packages section (what makes a package pi's, what is loaded and left alone, sign-in through onPrompt/onAuth and ctx.ui, pi's files under <config>/pi or PI_CODING_AGENT_DIR, Anthropic Messages requests), the pi-package search and a FAQ entry | ||
|
|
ba2128b7e5 |
Explain sealed subagent routing and clarify quota messages (#703)
* routing: explain sealed subagent eligibility and clarify quota messages * routing: address review wording and document consistency |
||
|
|
bc141ad834 | update: install.sh and magpie update take --proxy and --mirror, and magpie update mirror keeps one for the app's updates too (akic404 on Discord: 安装和update如果没有梯子也是麻烦事,能否在install.sh和magpie update后加子选项?--proxy等可自定义或默认ghproxy) — install.sh had no options and fetched with curl alone, and magpie update took none; without a way out to GitHub both failed. Now both take --proxy <url> (http, https, socks5, socks5h; install.sh also MAGPIE_PROXY=) for the feed and the download, and --mirror <prefix> (or MAGPIE_MIRROR=), a GitHub download mirror the release file is fetched from as <prefix>https://github.com/…; only github.com / githubusercontent.com URLs are put through it. No mirror is used unless one is given: magpie names none and has none by default. The feed with each file's SHA-256 is always fetched from usemagpie.ai, never through the mirror, so a file the mirror changed is refused and deleted ("… from the mirror X does not match its checksum from https://usemagpie.ai; not installed"); macOS still checks the app's signing team. magpie update mirror <prefix>|off saves Settings' updateMirror (http(s) only), which the app's own background update follows too; magpie update --mirror off skips it once; the Settings page's save keeps it. The app's update already followed Settings → Proxy (#294); magpie update --proxy overrides it for one run. The environment's ALL_PROXY / all_proxy is now honoured when HTTPS_PROXY / HTTP_PROXY are unset (Go's ProxyFromEnvironment ignores it), with NO_PROXY as curl reads it. magpie update takes --flag value or --flag=value, and an unknown word is an error instead of an update. The help, README and the docs' FAQ (en/zh/ja) say how. install.test.mjs's proxy, mirror and tampered-mirror tests, TestAllProxyFromTheEnvironment, TestUpdateThroughAMirror, TestUpdateThroughAProxyGiven and TestUpdateFlags fail on the old code and pass; TestSettingsSaveKeepsWhatItDoesNotSend covers updateMirror. A dry run of install.sh as Linux amd64 into a temp HOME installed the real 0.1.773 with its SHA-256 checked, refused a local mirror serving a wrong file, and with --proxy to a closed port failed reaching the site through it. go vet, builds (darwin, GOOS=linux, GOOS=windows), sh -n, node --test site/install.test.mjs and go test -tags nogui -count=1 ./... under a temp HOME pass but TestClaudeIdentityServedWhileAsked, a timing test that passes alone. Not tried through a real proxy server or a real public mirror; no install.ps1 exists for Windows, and the app has no field for the mirror. | ||
|
|
9416a6040a | site: the Omarchy intent hit shot reads as its caption — the classifier in 964 ms, glm-5.3 in 5.6 s (owner: 改一改吧) — the shot said 961/972 ms and 5.5 s; re-shot in tokyo-night and catppuccin-latte, retried until the routing card said 964 ms and 5.6 s, en and zh from the same request. Same sizes, so SHOTS is unchanged. | ||
|
|
ba1e3f2803 | site: the Omarchy picker shot shows what its alt says — Codex's picker listing DeepSeek, Kimi and GLM models (owner: 其他页面的截图也检查一下有没有对不上的) — the old shot opened the picker on Routing groups and covered the Codex row, so neither Codex nor Kimi/GLM showed; it is re-shot in tokyo-night and catppuccin-latte with the Codex row (DeepSeek V4 Pro) above the menu and the list at DEEPSEEK, KIMI and ZHIPU GLM, 880x1320. SHOTS picker follows. Checked in the omarchy VM's Chromium: index.html takes /img/omarchy/picker-*.png at its own ratio in both themes and the hero lays out as before. The other Omarchy shots (add, agents, import, panel, providers, routing, usage, nested routing, intent) already matched their alts. | ||
|
|
c96a4ba9ac |
site: /download links work again — since the release notes change (e5f8ff60) latest() returns { rel, degraded }, and /download still read .assets off that, so every download link (/download, /download/mac, /download/linux, /download/windows…) answered 500 (Cloudflare 1101) while /api/latest worked. /download now takes the release from got.rel. A test for /download/linux (302 to the asset) and an unknown platform (404) fails on the old code and passes; node --test site/*.test.mjs passes.
|
||
|
|
96e3ef6948 |
site: the update feed reads only what this worker kept in the edge cache — e5f8ff60 changed the shape of the cached newest release and release list but kept their names, so the entries the previous worker had put there were read as the new shape and /api/latest and /api/notes answered 500 until they ran out (up to 5 minutes after the deploy). The cache names now carry a version (__v2/), a hit is used only in the shape expected, and a cache that can't be read or written is a miss, not an error. A worker test seeds the old entries (and malformed new ones): it fails on e5f8ff60 and passes. node --test site/ passes.
|
||
|
|
ed586dab15 | site: the Omarchy intent-hit shot shows what its caption says — Live's aihubmix/glm-5.3 answering, deepseek-flash telling the intent (owner: 能否对上) — the Omarchy version of intent-routing-hit had Zhipu GLM answer and DeepSeek's deepseek-v4-flash classify, while the page's caption (en/zh/ja) and its plain shot name a custom provider Live with aihubmix/glm-5.3, deepseek/deepseek-v4-pro and deepseek/deepseek-flash. Reshot in the omarchy VM with Live (a pretend relay) holding those three, Coder in that order, rule 2 "writing or fixing tests" sending the turn to live/aihubmix/glm-5.3 and live/deepseek/deepseek-flash telling it in about 0.96 s; tokyo-night and catppuccin-latte, en and zh. The English shot is taller (1220), omarchy.js has its size. Checked in the VM: /docs/intent, /docs/zh/intent and /docs/ja/intent load it at the image's own ratio in both themes. | ||
|
|
e5f8ff6026 | site, gui: release notes survive GitHub limiting usemagpie.ai, and a failure to get them says so, apart from a release without any (#661) — /api/notes answered 503 {"error":"no releases"} with Cache-Control public, max-age=300 whenever GitHub's API limited the worker's shared IP, and /api/latest fell back to the release page's redirect with notes "" (kept 300 s too), so the update window had nothing to show. The worker now: never keeps an error (503 is no-store); takes the notes from GitHub's releases feed (releases.atom, not API-limited) when the API says no, turning its HTML back into the markdown the app draws and putting back the <!-- lang:zh --> marker GitHub strips (before the first Chinese heading after an English one); keeps the last whole answer for 30 days in the edge cache and serves it (merged with the feed's newer releases) when GitHub fails; keeps a degraded answer 60 s instead of 300; and sends a GITHUB_TOKEN secret to the API where one is set (none is yet). In the app, /api/whatsnew says error (and the release page's url) when the notes couldn't be had, and Settings' What's new opens a dialog saying "Couldn't load the release notes" with Try again and "Open the release page" (through the app), or "No release notes provided." with the release page and no retry for a version with none, instead of a passing toast; en/zh. Tests that fail on the old code and pass: site/worker.test.mjs (API limited: notes from the feed in either language, briefly kept; all of GitHub down: 503 no-store, then the last whole answer; the token sent), TestWhatsNewTellsFailureFromNone, and whats-new-failed.test.cjs (WebKit, en/zh: failure with retry and link, retry showing the notes, none without retry, no click moves the page). The feed parser was tried on the real releases.atom (v0.1.730–739: English and Chinese split as the API's bodies are). go vet, builds (darwin, GOOS=linux, GOOS=windows), go test -tags nogui -count=1 ./... under a temp HOME, node --test site/ and whats-new.test.cjs pass. | ||
|
|
a9368c301f | site: the nested-routing and intent-routing shots in Omarchy's look too (owner: 把嵌套路由和意图路由的截图也补上) — on Omarchy the docs' Start page (en/zh/ja) shows nested-routing, and the Intent page shows intent-rules, intent-routing-hit and intent-trace (en/zh, ja takes the English) as magpie draws them there, in tokyo-night (dark) and catppuccin-latte (light). They were shot in the omarchy VM's Chromium against a sandbox magpie with pretend vendors: the Coder group with an intent rule judged by Groq llama-3.1-8b-instant, a request routed by it (the trace's timings real), and Coder holding the group Fast. omarchy.js now matches shot names with a hyphen (intent-rules-zh-dark: name intent-rules, -zh) and knows these four, each with its own Chinese height. Checked in the VM: /docs/start, /docs/zh/start, /docs/ja/start, /docs/intent, /docs/zh/intent and /docs/ja/intent under ?omarchy=tokyo-night and catppuccin-latte load /img/omarchy/<name>(-zh)-<mode>.png with the image's own ratio. | ||
|
|
901084e3cc | site: on Omarchy the screenshots are magpie on Omarchy (owner: 各个截图能否也换成 magpie 在 Omarchy 下面的截图?) — magpie's own windows shot in the omarchy VM (magpie web, a sandbox HOME, pretend vendors behind a local proxy) in tokyo-night for the dark look and catppuccin-latte for the light one: add, agents, import, routing and usage in English and Chinese, panel, picker and providers in English, 26 images under img/omarchy/. In the Omarchy look omarchy.js puts them in place of the page's own, keeping each image's width and taking the new one's height; nested-routing and the intent shots keep the page's. Tried in the VM's Chromium against the site served locally: home, start (en, zh, ja) and import (en, zh) in both themes load the Omarchy shots at their own proportions, intent keeps its own. | ||
|
|
22e305b481 | site: the Omarchy chip shows on the first visit too — Omarchy was found (the font loaded) before <body> existed, so the chip had nowhere to go and nothing drew it later; render now waits for the body. Tried in the omarchy VM's Chromium with a fresh profile: tokyo-night and the chip on the first load. | ||
|
|
8f385e113b | site: usemagpie.ai draws itself in Omarchy's look on Omarchy, in the theme Omarchy has on (owner: 整个官网和文档页面能否智能识别是不是 Omarchy 在访问,如果是 Omarchy 在访问,那么整个页面风格能否变成 Omarchy 风格?而且能够适配当前 Omarchy 就更牛逼了) — every page (home, /import, the docs in en/zh/ja) loads omarchy.js in a Linux desktop browser only; it tells Omarchy by its font (a page may load local("omarchy"), which Omarchy installs system-wide and nothing else has), remembers it, and from then on paints before the page shows: the theme's palette, the system monospace (Omarchy's font), square corners, no shadows or glass, a solid bar, buttons and what's chosen in the accent, images framed in it. Until magpie tells the theme it takes Omarchy's default (tokyo-night, or catppuccin-latte on a light desktop). magpie's gateway answers GET /v1/magpie/omarchy (name, mode, stamp and the CSS variables magpie's own windows use) to this machine only, readable only by usemagpie.ai (CORS + Private Network Access; MAGPIE_SITE_ORIGIN adds an origin to try the site locally), 404 off Omarchy. The page asks it by itself only once the browser allows it to reach this computer; a chip in the corner (Omarchy's logo and the theme) asks once on a click, lists the 22 built-in themes to pick one, and turns the look off; ?omarchy=1|0|<theme> sets it anywhere. Followed while the page shows: a theme set in Omarchy's menu comes through in about 3 s. Tried in the omarchy VM's Chromium 152 with this magpie (nogui serve): detection, the home page and zh docs in tokyo-night, the chip's menu, following the computer, and omarchy-theme-set catppuccin-latte repainting the open page light. TestOmarchyThemeForTheSite covers 404 off Omarchy, the theme and its CORS for usemagpie.ai only, LAN refused and the preflight. go vet, builds (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. The permission prompt from the public site wasn't clicked through in a real browser. |