Commit Graph
604 Commits
Author SHA1 Message Date
yetone 5df609e525 usage: an Antigravity account shows each group of models' 5-hour and weekly allowance, one row a group (a user on Discord: 这里显示5h 和一周剩余比较好吧,现在是只显示5h,这3个模型都是一样的,没必要分开…多加个7day 条就好) — fetchAvailableModels gives each model only its 5-hour figure, the same for every model drawing on one quota, so the Usage page, the panel and the account rows showed a figure a family (Gemini, Claude, GPT-OSS) and no week. Antigravity's retrieveUserQuotaSummary reports the quota a group ("Gemini Models"; "Claude and GPT models", Claude and GPT-OSS sharing one), each with a 5h and a weekly bucket; magpie now reads it beside the models' (best-effort: without it the windows are as before) and adds each group's windows, named "5 hours" and "7 days" with their pool (Gemini, Claude & GPT), each model's window naming its pool. The Usage page shows Gemini · 5 hours, Gemini · 7 days, Claude & GPT · 5 hours, Claude & GPT · 7 days, the pool's models in each tooltip; the panel four rings (Gemini 5h/7d, Claude 5h/7d); the account rows each pool with its 5h and 7d meters; the TUI the pools' windows in place of the models'. "Every model" lists the models' own windows. The pools' windows are aside for routing, which reads the models' as before; the tray's text is unchanged. TestAntigravityQuotaPools and quota-pools.test.cjs (Chromium, WebKit; en, zh) fail without the change; quota-families, click-scroll, old-webkit, tray-usages, panel-usage pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-02 07:03:17 +08:00
yetone 134a788821 settings: magpie's own update checks can be turned off, and run every 30 minutes, hour, six hours or day (#472: 更新行为可配置) — the app asked the feed five seconds after start and every six hours, a constant, and downloaded any newer version at once; with releases out most days, a version out just after a check waited up to six hours, and there was no way to stop the checks or the downloads they start (Update button only hides the pill). Settings → About has Automatic updates (on by default) and Check every (30 min, 1 h, 6 h, 24 h; 6 h with nothing set), kept in settings.json as noAutoUpdate and updateEvery, and magpie update auto [on|off] [30m|1h|6h|24h] sets them from the terminal. The updater reads them each minute and asks once the interval since the last check, its own or a click on Check, is up, and never by itself while off; the version row, not asked yet, then says automatic updates are off and offers Check. Check every stays in place, dimmed, while off, so the click doesn't shorten the page under it. TestUpdateDue, TestUpdateAskedAt and TestUpdateAutoCmd fail without the change, as does update-auto.test.cjs (Chromium, WebKit; en, zh). go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; update-hide, update-check, whats-new, settings-groups, click-scroll, old-webkit pass. 2026-10-02 06:42:15 +08:00
yetone 03ab46dda3 library, plugins: the installed lists are sorted as the reader picks, and remember it (#481: the installed MCP servers, skills and plugins had no order the user could choose; the plugins came in the order they were installed) — the Library's MCP servers and the installed plugins now have A→Z and Z→A beside their list, A→Z until one is picked, so the plugins are no longer in the order they were installed in; the Library's skills have By source (the groups by repository and their parts, as before, and still the default), A→Z and Z→A, the last two one flat list of every skill by name, the filter still finding a skill by its repository. Each list keeps its own pick in the browser's storage (magpie.sort.<list>, read and written in try/catch), and a pick redraws only its list, the control left where it was on the screen. The skills' heading, the fullest, goes on to a second line in a narrow window rather than past the page's side. Sorting by use or by when a thing was installed waits for the library to keep those. list-sort (Chromium, WebKit; en, zh) fails without the change; library-items, library-all, own-skill, every-skill, the plugin tests, click-scroll and old-webkit pass; go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-02 06:33:46 +08:00
yetone e3bf9d05d4 library: one agent's skills or MCP servers are turned on or off at once, and a hidden agent's taken out of it (#475: turning Copilot CLI's 32 skills off was a chip on every row, and once it was hidden on the Agents page it had no chips at all, so what it had been given stayed with no way to take it out) — By agent, beside "In the library" on the Skills and MCP tabs, opens a sheet of the agents that take them, each with how many it has, a Turn all on and a Turn all off; each writes that one agent, every other keeping what it has, and reports what couldn't be written as a row's chips do. An agent hidden on the Agents page is listed while it has any, tagged Hidden and given nothing new; its instructions row is shown while it reads the shared instructions, so it can be switched off. Skills use the existing skills/agents-all; servers get servers/agents-all (EveryServerAgents), which turns on only the servers the agent can reach (no SSE for Codex, no remote one for Claude Desktop). TestEveryServerAgents and by-agent.test.cjs (Chromium, WebKit; en, zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; every-skill, library-all, library-items, library-toggle-status, remove-all-skills, click-scroll, old-webkit pass. 2026-10-02 06:33:40 +08:00
yetone fe86fb3a4d gui: the page is first drawn in its saved theme, not the system's (seg-contrast failed in Chromium on the release branch: in dark the view shown read at 3.96:1, the other views at 4.69:1) — the saved theme was put on the page by app.js, at the foot of index.html, so a window kept dark under a light system had its header parsed and styled light first, and its buttons' colour transitions (the switch's views, Sync, Settings) then faded them to dark as the page loaded; the test read them mid-way, the more so as #471 made the page longer to parse. theme.js, run in the head after boot.js and before the styles, sets it from bootPrefs (or ?theme=) for the first paint; app.js still sets it as before. seg-contrast.test.cjs now holds app.js back and checks the page was already drawn in its theme (fails without theme.js, Chromium and WebKit, en and zh, dark), and reads colours once the page's transitions are over. old-webkit lets theme.js (ES5, URLSearchParams) run before compat.js; api-key-theme, from the release branch, expects a text button in --ctl-fg as #477 set. seg-contrast, settings-sections, header-zoom-fit, header-centre, click-scroll, old-webkit, api-key-theme, omarchy, agent-grip, web-page, gateway-fold, sessions-toolbar pass; go vet, go build -tags nogui, go test ./internal/gui pass. 2026-10-02 06:12:01 +08:00
yetone 872e26e2d4 models: a favourite's star is filled, and a model not a favourite shows an outline one on hover (#482: 收藏后切换成实心星) — the model picker's star, in the window and the tray panel, was the same outline star whether the model was a favourite or not, only its colour changed, so which state it was in wasn't clear. A favourite's star is now filled in its own amber, light and dark, and shows always; one not a favourite is still an outline, shown on hover. The button says which with aria-pressed as well as its title. favorite-star (window and tray panel, light and dark, en and zh, Chromium and WebKit) fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass; click-scroll, old-webkit, model-pick pass. 2026-10-02 06:07:14 +08:00
yetone 10da2936da gui: Settings is a page per part, under a tab each, the part kept in the address (#471: Settings is one long scroll of some 45 rows in 9 sections) — the page now opens with a tab list in the warm-ups' look (role=tablist, a tab each with aria-controls/aria-labelledby): General (appearance, language, text size, the icons, model names, login, Dock, session terminal), Usage (the menu bar's usage, refresh, logos, what usage shows, currency, number units, the two alerts, then the warm-up and check-in tabs), Network and sharing (proxy, local network), Models (images, web search), Privacy, Observability, Sync and backup, About; the part picked shows its rows alone. Every row, its id and what it saves are as they were; the duplicate heading of a one-section part and the Preferences heading are gone. The part is kept as ?view=settings&tab=<part> (dropped on any other view), so a reload or a link lands there, and remembered (magpie.settingsTab) for Settings opened again without one, as the tray's window/main?view=settings; an unknown one falls back to it. A part whose rows are all hidden on this system has no tab, and one picked falls back to the first shown. The arrows, Home and End move along the tabs, one in the Tab order; a click on a tab moves nothing on the page, the tabs staying where they were; a pill drawn while its part was hidden has its thumb put under its pick when shown (the warm-ups' fix, now shared). The tabs wrap to a line more when they don't fit (English in the 560x420 window, at 110 and 125%, a phone), no left-border stripe. The click hold also holds a click not yet held before taking the next (keys pressed within a frame on a tab list whose part shrank to the window: the second click was taken at the clamped top and the page stayed there). New strings General and Network and sharing are in i18n.js (常规, 网络与共享). Tests that reach a row now open or click its part: api-key-theme, currency, gateway-caller-keys, gateway-connect-keys, lan-docker, number-units, otel, redact-rule-row, s3-sync, search-api, settings-groups, sync-other-kind, tray-inuse, tray-logos, tray-usages, update-check, update-hide, usage-alert, webdav-settings-refresh, whats-new; those needing a scrolled page wheel a shorter window or in smaller steps, as a part is shorter than the whole page was. settings-sections.test.cjs (Chromium, WebKit, en, zh) fails without the change (10 of 10). go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestAccountProxy as on main); settings-sections, settings-groups, click-scroll, old-webkit, text-size and every settings test listed above pass. 2026-10-02 05:50:24 +08:00
yetone 69edf9f6e4 profiles: a chip's ↻ and × act on a second click, and a profile's details close with their own ×, or Escape, without hiding the tray panel (emo172, #478: ↻ overwrote and × deleted a profile on the first click; the details closed only by clicking the chip again, and Escape on them in the tray panel hid the whole panel; closed by hand they came back with the list, closed by Apply they didn't) — a first click on ↻ or × now changes nothing and reads "Overwrite?" or "Delete?" (覆盖?, 删除?) in red, and a second does it; it goes back after 3.5s, on Escape, or when the other one is clicked, with no browser dialog. The details have a × beside Apply, which closes them, the chip where it was. Escape takes back a question, then closes the details, then the panel's list, and only then hides the panel (both the panel's Escape and the list's ran on one press, so it hid the panel with the list open). The details close with the list, so the list opens again on the chips alone, however they were closed, as after Apply. profile-confirm.test.cjs (Chromium, WebKit, en, zh, window and panel) fails without the change; panel-profiles clicks × twice. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; profile-details, panel-profiles, old-webkit, click-scroll pass (click-scroll's routing Replay case flaked once on WebKit, passes alone). 2026-10-02 05:50:23 +08:00
yetone 973bf4eb2c profiles: a profile's details list all four of Claude Code's tiers, one left to follow the main model saying so (#480: opus and fable, following it, were missing from the details, so they read as not set) — a tier is a quiet field, empty while it follows the main model, and the details left every empty quiet field out. A field now says which one it follows (Follows, "model" for the tiers), and the details keep such a field, empty, with it; the panel shows it as "follows the main model (<model>)" (跟随主模型(…)), or "follows the main model" when the main is the agent's default. Other quiet fields (Codex's sign-in, subagents) are still left out while empty, and what applying a profile writes is unchanged. TestDetails and profile-details.test.cjs (Chromium, WebKit, en, zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-02 05:50:23 +08:00
yetone 01d595817c providers: each account of a subscription, and each key, can be kept for some of the provider's models only (#474: 增加账号级别的模型选项, a low-quota but verified account kept for the cheap models) — every account or key a provider had served every model it did, so one couldn't be spared for some. An account's or key's row in its provider's editor now has a models badge: a click opens the provider's models as chips, Save keeps the account for those picked, All models gives it every one again; one kept for some says how many, always shown. magpie provider account-models <id> [account|key [models…|all]] does the same and lists them. It is kept in providers.json as accountModels, an account by its name in lower case, a key by its id; one not in it serves every model, as before. The gateway never sends a model to an account or key whose list leaves it out, in every routing mode, in drag order, in a group and as a fallback, and never falls back to it all the same; the trace says it was left out as set to serve other models. When every account or key there is leaves the model out, the request is refused with a 403 saying so and how to change it, not "none of X's models is ready". TestKeyModelsNarrowCandidates, TestAccountModelsNeverChosen, TestAccountModelsCmd and account-models.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; account-models, click-scroll, old-webkit, account-arrange, account-proxy, account-return, routing-account-order, panel-routing, unlisted-models pass. 2026-10-02 05:50:23 +08:00
yetone 7de2b62191 gui: an agent row shows a faint grip in its margin, and a hidden agent's Show is always there (#479: hiding an agent was found only by right-clicking) — the row's handle is its logo, which turned into a grip and opened Move up, Move down, Hide on a click, but only showed it was a handle with the pointer on it, and a hidden agent's way back, the Show button in the fold at the foot of the list, came up only with the pointer on its row. Every row in the list now carries a faint grip (two columns of three dots, --faint, --muted with the pointer on the row) in its left margin, beside the logo: it is the handle's own ::before, so it takes no room from the row, a drag on it moves the row and a click on it opens the menu. The Show button on a row hidden by hand is drawn without the pointer on it. Rows in the fold carry no grip, since they aren't dragged. agent-grip.test.cjs (window and tray panel, Chromium, WebKit, en, zh) drags a row by its grip, hides one from the grip's menu and shows it again from the fold, and fails without either half of the change. account-arrange, agent-layout, panel-fold, rollup-short, click-scroll, old-webkit, seg-contrast, header-centre, header-zoom-fit pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass but for TestClaudeSignedOut, which fails on its own as well and doesn't build on internal/gui (TestUserPath and TestSubscriptionUsageEachAccount flaked under load, pass alone). 2026-10-02 05:50:17 +08:00
yetone aa8e8cee2f gui: the header's other views and the text buttons read at WCAG AA, and the view shown stands off the switch (#477: in light mode an unselected tab read at 3.25:1, a text button at 3.33:1 on the page; dark passed by 0.08) — both were drawn in --muted (#85858d light, #8b8b94 dark), meant for captions, not controls, and the thumb on the view shown was white on a pill-grey track (1.13:1) or a 10% white on a 4.5% one (1.36:1). A control's quiet text now has its own colour, --ctl-fg (#63636b light, #a1a1aa dark), for the switch's other views, the Accounts and Sessions switches on the same track, and every .text button: 4.79:1 on the light track, 5.4 to 6.0:1 on the page, a card and the inset editor, 6.1:1 and up in dark. The light track is a shade deeper (#e6e6eb) and its thumb's edge a little firmer, 1.24:1 against it; dark's track is 3.5% white and its thumb 15%, 1.61:1. Sizes and weights are unchanged, so the header fits as before (#442/#457). seg-contrast.test.cjs (Chromium, WebKit, en, zh; light, dark, system dark) reads the computed colours, each background laid over the ones under it, and fails without the change. header-centre, header-zoom-fit, text-size, sessions-toolbar, click-scroll, old-webkit pass. 2026-10-02 05:50:17 +08:00
yetone 2d5905ffc8 usage: every count on the Usage page and in the panel follows Settings' number units (#476: with 万 / 亿 picked, the Overview, the Requests summary line, Sessions and the panel's Routing "today" still said K/M/B, two units on one page) — the setting reached only ledShort, which the Requests tab's totals and axis and the panel's Usage tab used, while fmtN, behind the Overview's tiles and chart peak, the Requests summary (#ledSum), every Sessions figure and Routing's "today N tokens", always said K/M/B. fmtN now says a count in 万 and 亿 in Chinese unless K / M / B is picked, ledShort is gone and its callers use fmtN, and the panel's Routing redraws when the setting changes, not at the next request. Small counts (the scan's file counts, a picker's search count) are as they were below 10000 and in 万 above it in Chinese. English is unchanged. number-units.test.cjs (Chromium, WebKit, en, zh) now checks the Overview, the Requests summary, Sessions and the panel's Routing in both units and fails without the change; sessions-overview, sessions-today, panel-routing and api-key-usage expect 万 in Chinese. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; number-units, sessions-overview, sessions-today, panel-routing, api-key-usage, usage-chart-axis, panel-usage, usage-ledger, usage-ledger-chart, currency, sessions-bar-names, click-scroll, old-webkit pass. 2026-10-02 05:46:16 +08:00
yetone 8043e9436d tests: cli-update's tight agent row is measured just above 600px, where the row is still beside its fields — since #440 (946c1d3b) a window 600px wide or less puts an agent's fields under its name, so at 540px, where the test looked for the version to have gone, the name's line spans the whole row and shows the name, the version and the whole pill again, as #440 meant. The version now has to be gone at 604px (the row about 148px), the tightest it gets beside its fields, and at 540px everything has to be back. No behaviour changes. cli-update.test.cjs (Chromium, WebKit) failed on main; click-scroll, old-webkit pass. 2026-10-02 05:29:14 +08:00
yetone 156b72ebf3 gui: the account or key a provider is on reads "In use" (使用中) again, not "Current" (在用) — 3df75514, which added each agent's model list and its 在用 tag for the model an agent is set to, also renamed the badge on the Accounts and keys rows from "In use" to "Current", which its message doesn't mention. The rest of magpie calls what is in use "In use": the Library's active set reads In use, the S3/WebDAV sync's chosen kind "· 使用中", so an account row reading 在用 said the same thing in other words. The rows say "In use" again; the model list's "Current" tag for a model stays. duplicate-pause.test.cjs (Chromium, WebKit, en, zh) failed on main for it. account-arrange, account-return, api-key-usage, agent-models, click-scroll, old-webkit pass; go vet, go build -tags nogui pass. 2026-10-02 05:29:13 +08:00
yetone afe0014ebe routing: the live stage seats a provider's accounts and keys in the order they were dragged into, at once (Koohoko, #217: after dragging, the live routing animation kept the old account order) — the stage held each route's rows still by seating them by name, so a provider's accounts read alphabetically (by email, a key by its name) whatever order the provider's page lists them and the gateway tries them in, and a drag moved nothing there. The trace now tells each account's or key's place in its provider's own list (Weighed.Rank, from the order the provider lists them, the one signed in to first), and the stage and its Accounts and keys list seat a provider's rows by it, read from the providers as they are now, so after a drag the stage moves to the new order within a second of Routing being shown, with no reload or new request; a plugin's accounts, listed by its provider's id, follow it too. The order routing weighs them in, and who goes first, are unchanged. TestWeighedRankIsDraggedOrder and routing-account-order.test.cjs (Chromium, WebKit, en, zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestAccountProxy fails on main as well; TestFXInState flaked once under load, passes alone); account-arrange, click-scroll, old-webkit, routing-steady, routing-columns, routing-shape, panel-routing pass. 2026-10-02 04:32:36 +08:00
yetone f22218a092 gui: after an update the window shows what changed since the version last run (a Discord user: 点更新后能弹个窗显示更新了啥吗, to see whether their issue was fixed) — magpie updated itself silently, and its release notes were only on GitHub. The version last run is now kept in magpie's own folder (last-version); started on a newer release, the window or magpie web's page asks usemagpie.ai's new /api/notes (beside /api/latest; site/worker.js lists the releases' notes from GitHub, cached as the feed is) for every release since, newest first, without their Install section, and shows them once in a What's new dialog. A fresh install, the same version, a downgrade or a build from source shows nothing; a magpie from before the version was kept, already in use, shows its own version's notes; offline nothing is shown and nothing waits on it; until the site is deployed, the update feed's newest notes stand in when they are the current version's. The tray's panel shows no dialog. The notes are drawn as text, never as HTML: headings, bullets, bold, code, http(s) links, and #NNN links to the issue, opened in the browser as the app's other links are. Settings has a What's new row under Version that opens them again, with a waiting update's notes first, marked Not installed yet; /api/update's notes leave out the Install section too. TestShowNotes, TestStripInstall, TestBetween, TestNotesBetween, TestWhatsNewAfterUpgrade, TestWhatsNewFromBefore, TestUpdateNotesWithoutInstall and whats-new.test.cjs (Chromium, WebKit, en, zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; click-scroll, old-webkit, update-check, update-hide pass. 2026-10-02 03:57:55 +08:00
yetone 78aba760aa gui: an agent's instructions opened at the foot of the Library's list leave the page where it is (emo172, #458) — the Library draws its whole page again on a click, so the click's hold (the element clicked, its neighbours and its parents, kept where they were on the screen) found none of them left and held nothing; the page went where the browser took it: down by what opened under the row (scroll anchoring), or clamped as it shut. A held element gone, with every one of its neighbours and parents, is now stood in for by what is at its place in the view's tree, of the same tag and first class, so the row clicked stays put. library-row-open-scroll.test.cjs (Chromium, WebKit, en, zh; a long set in a short window, shorter sets in a tall one) fails without the change (12 of 14); click-scroll, old-webkit, header-zoom-fit pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-02 03:38:15 +08:00
yetone 7e31cf7438 gui: a zoomed header with room to spare is drawn as at 100% (emo172, #457) — under the webview's page zoom (text size 110/125%) the header's buttons, which sit against its right padding, measure a hair past it (0.00004 CSS px in Chromium at 110%); fitTop took that for buttons running off and drew the header in step by step, at 110% all the way to packed, whatever the window's width. The comparison now allows half a pixel. header-zoom-fit.test.cjs zooms Chromium's page as the webview does and fails without the change; header-centre, text-size, brand, click-scroll, old-webkit pass. 2026-10-02 03:38:06 +08:00
关俊江 e230b9f31a feat: support handle-free account reordering in all routing modes (#216) 2026-10-02 03:32:41 +08:00
yetone cbd697693a agents: Claude Code's subagents get no square while there is no model to pick (#468) — before Claude Code runs through magpie its subagents field offers nothing, yet the agents page drew a square that could set nothing; it is left out, as an effort with no levels is. subagent-effort.test.cjs fails without the change. 2026-10-02 03:18:35 +08:00
yetone 4c69ff336b agent: Codex's subagents can start at an effort of their own, and Claude Code's subagents can have a model of their own (#468, #469) — Codex reads [agents] default_subagent_reasoning_effort, the effort a spawned subagent starts at when the spawn names none (openai/codex core/src/agent/child_config.rs, config_toml.rs; in codex 0.159.2), and magpie never wrote it. Codex gets a subagent effort field: a square beside its subagents' one that opens the effort slider, unset said to mean the session's effort, or the subagent model's own default. Codex refuses a spawn at a level the subagents' model lacks, so only one it has is taken, and one a new subagent or main model lacks is dropped. Claude Code takes the Agent call's model, then the agent's frontmatter, then CLAUDE_CODE_SUBAGENT_MODEL, then the session's (2.1.287); magpie wrote that env only by itself, the main model while every tier followed it. It gets a subagents field, offered once routed through magpie, whose model outlives a new main model and a tier of its own; unset, the env is written as before. Claude Code has no setting for its subagents' effort beyond each agent's own frontmatter, and omp's task role already takes a thinking level, so neither changes. TestCodexSubagentEffort, TestClaudeSubagentModel and subagent-effort.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; subagent-effort, omp-roles, claude-effort, panel-effort, agent-layout, model-pick, click-scroll, old-webkit pass. 2026-10-02 03:18:26 +08:00
yetone 818621b75e profiles: a click on a profile shows what it holds, and its Apply applies it (#467 emo172: a saved profile could only be seen by applying it over the setup in use) — a profile's chip applied it at once, and all the page knew of it was a one-line summary of its models. The state now carries each profile's fields by agent, under the agents' names and the fields' labels, in the order magpie lists them, with the Library's servers, skills and instructions each agent gets (not the instructions' text); a value that reads as a key or a token (a field named for one, a known key prefix, a long run of letters and digits) is sent as hidden, never its text. A click on a chip opens its details under its line in the window, and over it in the tray panel with Apply at their foot by the chip, the chip clicked staying where it is; opening them changes nothing, a second click closes them, Apply applies the profile; ↻ and × stay on the chip. In English and Chinese. TestDetails and TestStateProfileDetails fail without the change, as does profile-details.test.cjs (Chromium and WebKit, en and zh, window and panel). go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestFXInState's TempDir cleanup flakes with or without the change); profile-details, panel-profiles, click-scroll, old-webkit pass. 2026-10-02 03:09:42 +08:00
yetone 623307e978 i18n(zh): plainer words where they read as slang, the rest kept (#465) — the provider editor's Forget (dropping the vendor's fetched list) is 清除, not 忘掉; the Providers page's Take a look is 查看, not 看看; an account's forget tooltip says magpie 移除 its sign-in, as its siblings do; and the routing explanations that said {who} 先上 say 排第一, as every other "goes first" there already does. A session's details named its id line 整个会话, the routing affinity option's word for "Session": the line now uses the Session ID key (会话 ID, as the usage ledger's detail has it), and 整个会话 stays where it means the whole session, beside 一轮之内. 知道了 (OK) and 还有 {n} 个 (+{n} more) are kept: standard and clear where they show. plugin-move and provider-picks-stay assert the new words; sessions-manage and sessions-overview fail without the id line's label. plugin-move, provider-picks-stay, sessions-manage, sessions-overview, routing-steady, routing-manual, click-scroll, old-webkit pass (Chromium, WebKit, en, zh); go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-02 02:50:38 +08:00
yetone 16f14fdc10 providers: the models picked in one provider's editor never go to another's (#464: Claude models picked on one relay showed in another relay's editor, and its Forget and Refresh kept them) — a Refresh or Forget took the open draft's picks once the vendor answered and, after the list came back, wrote them into whatever draft was open by then; an editor left for another provider's in between was given the first one's picks, shown there as added by hand, and that editor's own Refresh and Forget then kept them as its own. The redraw already keeps the open draft, picks and all, so nothing is put back by hand any more: the other editor has its own picks only, while in the editor the button was pressed in a pick not yet saved and an id the vendor doesn't list (added by hand) still outlive both. provider-picks-stay.test.cjs (Chromium, WebKit, en, zh) fails without the change, for Refresh and for Forget each; click-scroll, old-webkit, provider-typed, unlisted-models, model-test-one, nokey-editor pass. 2026-10-02 02:50:38 +08:00
yetone a409e1552a groups: the groups magpie finds on its own can be switched off, in the Routing view and with magpie group auto off (蓝猫 on Discord: 路由分组会自动创建,可以关闭掉吗) — a model two or more providers serve always became an auto-<model> group, and one could only be removed at a time. A switch right under the routing groups' head, with what it does in words, and magpie group auto [on|off] now keep noAutoGroups in providers.json: off, no found group is listed, catalogued or served, and the groups the user made or changed stay; on, they are back, along with any the user had removed still removed. An agent set to a found group is moved to its model from one provider (the status line and the CLI say which), and a request that still names group/auto-<model> goes to that provider's model rather than failing. Switching off is refused while a group of the user's holds a found group as a member or classifier, so none is left pointing at nothing. TestAutoGroupsOff, TestAutoStandIn, TestAutoGroupsOffHeld, TestFoundGroupOff, TestReseatFoundGroupsOff and TestGroupsFoundSwitch fail without the change; group-found-off.test.cjs (Chromium, WebKit, en, zh), click-scroll and old-webkit pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath a load flake, passes alone; TestClaudeSignedOut fails alone on main as well). 2026-10-02 02:24:49 +08:00
yetone 456208945b plugins: a plugin can be installed from a git repository — github:owner/repo, owner/repo, a GitHub URL or a git+https:// one (Lemon on Discord: can plugins be installed directly from git?) — magpie plugin add and the Plugins field took only an npm name or a path, and turned a git spec away as "isn't an npm package name". A git spec now goes to bun add as given and is kept as given in plugins.json; the package it installs is named by its repository's package.json, which bun records as the dependency whose spec it is, so the plugin is known (list, on/off, rm, its version, its README) by that name or by the spec. update and the row's Update run bun update on it, which fetches the repository's commit now where bun add keeps the one bun.lock has, the spec unchanged; npm is never asked for a newer version of it, nor auto-updates it. Adding the same package from another URL, or from npm, takes its entry's place. The Plugins field says it takes a GitHub repo (en+zh); an installed git plugin shows its package's name, an Update button, and a page with its README, no npm link and a Source link to the repository. Nothing that installed before is turned away. TestGitPlugin (a fake bun fetching from local bare repositories, the real one loading the plugin when on PATH) and plugin-git.test.cjs fail without the change; TestIsGit checks what counts as a git spec. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; plugin-git, plugin-local, plugin-market, plugin-updates, click-scroll, old-webkit pass. 2026-10-02 02:24:49 +08:00
TryWorld 876bec1b89 feat(gui): collapse overflowing Sessions agent tabs into a picker (#461) 2026-10-02 02:25:17 +08:00
TryWorld 946c1d3bf6 fix(gui): keep agent names visible in narrow windows (#440) 2026-10-02 01:50:38 +08:00
Aliceandraper 0260716c9d feat(usage): 可选的 OTLP 元数据导出(Trace/Metrics,支持 Langfuse) (#412)
* usage: add opt-in OTLP HTTP metadata export

Export gateway traces and optional duration/token histograms asynchronously with a bounded queue, retries, proxy support and a bounded shutdown. Add bilingual Settings controls and MAGPIE_OTEL_* overrides. Keep prompts, replies, sessions and provider credentials local; use the same OTLP path for Langfuse.

Validated: full Go tests (nogui), usage race tests, focused go vet, and Chromium/WebKit settings tests in English and Chinese.

Refs #213

* otel: protect collector credentials in backups and settings

Remove all OTLP headers from keyless backups and restrict both new and existing settings files to 0600 before saving. Clarify SIGTERM export behavior. Addresses the two blocking review findings in #412.

* otel: preserve local credentials and read-only settings on restore

---------

Co-authored-by: raper <raper@raperdeMacBook-Air.local>
2026-10-02 01:51:04 +08:00
yetone 3c04877cfc archive: a request the archive kept is opened and downloaded from the Usage page, with its bodies whole (jorben, #447: 请求存档 reachable from usage history, full bodies, download) — the archive's name was only on the Gateway page's recent calls, gone once a call left the list or the gateway restarted, and the archive kept the first 256 KB of each body, the recent list's cut. The usage ledger's row now names the call's archive (archive in usage.jsonl, kept through the Requests page's packed rows), and its details on Usage → Requests show the Request archive panel, Fetch from archive and Download; with the archive on, a request it has no copy of says "Not archived". The bodies are kept whole up to requestArchiveMaxMB in settings.json (32 MB unless set, at most 1024): the response is teed to a temp file as it is sent, the request body written to one when the call ends, so the upload queue holds file handles, not bodies, and one upload at a time reads them; a body past the limit is kept to it and marked cut, with its whole size. What goes upstream is unchanged, the archive stays off unless turned on, headers and bodies are scrubbed as before, and the temp files are removed after the upload or when the queue is full. The page is sent a body to show only when it is 256 KB or less (a longer one is said by its size), an archive over 80 MB is only said to be; Download streams the object whole, to Downloads in the app (archive/export, -2, -3… for a name taken) and as the browser's download in magpie web (archive/file). TestArchiveWholeBodies, TestArchiveLimit, TestRowKeepsArchive and TestArchivePreviewAndDownload fail without the change; TestArchiveOffUsage, TestArchiveTooLargeToPreview, TestArchiveOldPreview keep the rest. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; request-archive-usage (new), request-archive, click-scroll, old-webkit, usage-ledger, usage-ledger-detail, usage-ledger-content, web-page, sse-body pass. 2026-10-02 00:57:51 +08:00
yetoneandClaude Opus 5.5 03b4cadb1f library: Remove all beside Turn all on/off takes every skill out of the library at once (#449) — a library of many skills could only be emptied a row's Remove at a time. Remove all, in red, asks in the page first, naming the agents that lose the skills, the projects magpie placed them in, the folders that go to magpie's backups and those of the user's own that are only unlinked; Cancel sends nothing. Confirming sends every name in one call, library.RemoveSkills, which takes each as RemoveSkill does, the folders kept aside together under one backup; one that can't be taken out stays, and is said, while the rest go. magpie library skill rm --all does the same, asking y/N on a terminal and, with none, only with --yes. TestRemoveSkills and TestLibrarySkillRmAll fail without the change, as does remove-all-skills.test.cjs (Chromium and WebKit, English and Chinese); click-scroll, old-webkit, every-skill pass. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (bar the known Claude account flake TestClaudeSignedOut).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:47:15 +08:00
czyt bde39aef5e gateway: advertise MAGPIE_PUBLIC_URL in the console and CLIs (#373) 2026-10-02 00:46:45 +08:00
AronnaxandClaude Opus 5.5 ec4ee5a3a4 fix(proc): end what a CLI probe started, on its timeout and when magpie exits (#414)
* fix(proc): end what a CLI probe started, on its timeout and when magpie exits

The probes that ask an agent's CLI something (claude auth status,
cursor-agent about/models/status, the --version checks) ran through
proc.CommandContext, which on its timeout kills only the command itself.
A CLI that is a script (a version manager's wrapper, a shim) left what it
had started running, adopted by init; and a probe still asking when magpie
exited (a one-shot command, an update, a restart) was left whole, with
nothing to end it. With Omarchy's mise wrappers stuck in their exec loop
(omacom/omarchy#7234) that was five processes holding about half a core
each for up to six hours.

proc.ProbeContext gives such a probe a session of its own on Unix, as
StartTree does, and its context ending kills the whole group. Probes still
running are listed, and proc.EndProbes kills them when magpie exits (after
run in main) or puts a new version in its place (update.Reexec). On
Windows a probe is killed alone, as before, a job being joined only once
the command has started.

TestProbeTimeoutEndsWhatItStarted and TestEndProbes fail without the
change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(proc): end probes on a terminal's signals and on a Mac's quit

Review of the first change: a probe leading its own session no longer
gets the Ctrl+C, hang-up or SIGTERM sent to magpie's terminal or service,
and magpie ending on the signal never reached EndProbes, so the probe was
left to init where before it died with magpie. main now catches SIGINT,
SIGTERM and SIGHUP, ends the probes and exits as the signal would have
(128+n). A signal magpie was started ignoring (nohup, a background job)
stays ignored. The account sign-in and plugin commands, which end
themselves on Ctrl+C, go through interruptContext and are left to do so.

On a Mac the desktop app's quit ([NSApp terminate:], also the restart to
update) exits without returning to main, so OnShutdown ends the probes.

TestProbeTimeoutEndsWhatItStarted ended its probe on a 500ms timeout,
which on a Mac could come before the script had written its child's pid
(6 of 10 runs failed in review). It now ends the context once that pid is
read, the same Cancel path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let the app and the TUI quit on a signal their own way

Review of the signal handling: endProbesOnSignal exited ahead of Wails
and bubbletea, which quit on SIGINT and SIGTERM themselves. `magpie tray`
exited 143 without OnShutdown (no downloaded update installed, and
sessions.Saved skipped in main), and `magpie tui` left the terminal on its
alternate screen with the cursor hidden. The app's and the TUI's entry
points now hand the signals back (ownSignals): the app's OnShutdown ends
the probes, and the TUI comes back to main, which ends them too. A
one-shot command keeps the handler.

TestOwnSignalsHandsThemBack fails without the change (magpie exited 143
before the command could quit its own way); TestSignalExits keeps the
one-shot behavior.

childPid waited 3s for a script just written to start its child, which a
busy Mac can exceed (2 of 3 full runs failed in review). It waits 30s now
and, should the probe end first, says the probe's error rather than "the
script started nothing".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep SIGHUP for the app and the TUI, and die of the signal

ownSignals handed SIGHUP back along with SIGINT and SIGTERM, but neither
Wails nor bubbletea handles it: the terminal closing under the TUI or an
app started from a shell ended magpie on the spot, and its probes, in a
session of their own, out of the hang-up's reach, kept running (before
this PR they were in the terminal's group and ended with it). SIGHUP now
stays with endProbesOnSignal. A tray under a pty whose terminal closes
left 2 processes before, none now.

The handler asked whether a command was ending itself on Ctrl+C only
after EndProbes, which can take up to a second; a sign-in done in the
meantime got magpie ended in the middle of its own cleanup. It is asked
first.

It exited 128+n, which a shell takes for a child that handled the signal:
a loop running magpie went on to the next run on Ctrl+C. It now dies of
the signal (handling reset, the signal sent again); Windows, which can't
send one, still exits with the code.

agentProbe and agentCommand share withProxy; the Windows probeGroup
comment no longer says EndProbes ends what a shim started.

TestOwnSignalsKeepsHangUp and TestInterruptWhileEndingProbes fail without
the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: hand signals to the TUI and the app only once they listen

Review: runTUI handed SIGINT and SIGTERM back before tui.Run, whose
newModel asks agents' CLIs (claude auth status, up to 5s) before
bubbletea listens for signals. Ctrl+C, SIGTERM or SIGINT while it loaded
ended magpie by Go's default and left the probe and its child running.
tui.Run now takes ready, called once the model is made, and runTUI passes
ownSignals.

The app had the same window: runGUI handed them back before gui.Run,
which starts the backend (probes included) before Wails' Run starts
listening. ownSignals is now gui.Started, called on ApplicationStarted,
which Wails emits after its signal handler is set up. A tray sent SIGTERM
0.05s after starting, a probe under way, left 2 processes before and
none now; one sent it once started still quits through OnShutdown
(exit 0).

TestTUIKeepsSignalsWhileLoading fails without the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:42:48 +08:00
yetone f232a5df45 sessions: the agent tabs scroll under the app's thin thumb on Windows, not its classic scrollbar (ARNO on Discord: 会话面板是不是被改坏了…现在变得好丑) — the strip, and a provider's region picker that shares its class, set scrollbar-width: thin, and in Chromium (WebView2) that drops every ::-webkit-scrollbar rule, so Windows drew its own bar, arrows and all, under the tabs. The strip now leaves scrollbar-width alone and takes the app's thumb, 6px high; on a Mac the overlay bar is as it was. strip-scrollbar fails without the change; click-scroll, old-webkit, sessions-manage pass. 2026-10-02 00:31:16 +08:00
yetone 7ed38530f6 gui: the header's tabs stay in the middle when closed up, and halfway between the edge and the buttons when in the row (emo172, #442) — at text size 110% on Windows the header's tabs sat about 67px left of the middle: fitTop's cramped both closed the tabs up and put them in the row, where auto margins left a third of the room to their left and two thirds to their right. The closer tabs are now tried in the middle first; only when they don't fit there does a new inrow step put them in the row, with the buttons' auto margin dropped so the room either side is the same, give or take the header's padding against the row's gap. header-centre.test.cjs fails without the change. 2026-10-01 23:38:15 +08:00
yetone 09ee1bbf58 library: every skill on or off at once (#443)
With 30+ skills, turning them all on or off was a row's All at a time.
"Turn all on" and "Turn all off" beside "In the library" give every skill
to every agent shown that can take skills, or take every one from them, in
one write (POST library/skills/agents-all, EverySkillAgents); an agent not
shown keeps what it has, as with a row's All. Off asks first in the page;
each is greyed when there's nothing for it to do. TestEverySkillAgents and
every-skill.test.cjs fail without the change.
2026-10-01 23:38:15 +08:00
yetone 728cb83c47 routing: a request's changed reasoning keeps to its own box, cut with an ellipsis, never drawn over its numbers (#435, azir12345: 文字重叠) — the Requests list's rows share their columns, and the numbers' one is as wide as the longest numbers in the list (three tries · minutes · TTFT · a million tokens), so where a request went could be left next to no room; its reasoning would not shrink and ran on over "15 秒 · 首字 14 秒 · 6.9k token", while a short "· low" still fit. Where it went now gives way first, then the level asked for ("m… → xhigh"), each with an ellipsis, and the level sent stays whole; the reasoning cell clips as a last resort. Safari 15's styles (no subgrid, no container queries) lay each row out by itself and stay as they were. routing-effort-change.test.cjs (Chromium and WebKit, English and Chinese, dark, 1440 to 400px, and with Safari 15's styles) fails without the change; routing-effort-row.test.cjs counts the reasoning's new inner spans as the reasoning. routing-effort, routing-effort-row, routing-kind, routing-served, routing-sessions, routing-columns, remote-group-served, routing-reroute-title, routing-steady, routing-time, routing-flood, request-archive, panel-routing, click-scroll, old-webkit, text-size pass; go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass. 2026-10-01 23:20:57 +08:00
yetone 4547dafe95 models: Provider in model names has a third way, a name the user gave used just as they wrote it (#92 marsxxl: v0.1.591 的 Codex 下拉里自定义名称又带上了 · WorkBuddy / · Claude Code) — 559ce021 (#203) had a custom name keep " · <provider>" after it like any other, so "Opus 5.5" read "Opus 5.5 · Claude Code" in every list Labels feeds (Codex's model_catalog_json and the gateway's Codex /models, Pi's, OpenCode's, ZCode's, OMP's, Muse's), while #92 had been promised the name as written. Both are kept: the setting is now Off / Not on names I set (自定义名称不带供应商) / On. On stays the default and keeps #203's behaviour, so nobody's list changes unasked; own leaves a name the user gave as it is and a vendor's name with its provider's; off is #335's plain names. Stored as plainOwnNames beside plainNames, so a settings.json of before reads as it did (plainNames: true is off). POST /api/settings/plain-names takes mode on|own|off (a body of on alone still works), magpie model suffix on|own|off, a Settings save keeps it, and /v1/models' magpie_label now follows the setting through Labels as the agents' lists do. The row's hint is one line that fits (en, zh), and its zh name says 供应商 as the rest of the app. Prompts and headers are unchanged. TestSuffixMode, TestSuffixModesReachAgentFiles (Codex model_catalog_json and Pi models.json), TestModelsListSuffixModes (/v1/models magpie_label) and model-suffix-own.test.cjs (Chromium and WebKit, en and zh) fail without the change; TestModelName and TestModelNameAndLevelsReachAgentFiles keep #203's assertions. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestConfiguredModelsAdvertiseImageInput load-flaky, passes alone); plain-names, click-scroll, old-webkit pass. 2026-10-01 23:20:57 +08:00
yetone 41dde238dd usage: a sessions bar's name takes the width it needs, its bar what is left (John on Discord: widened, the names stayed cut short with … while the bars grew) — the Sessions overview's projects, models, tools and skills rows held their names to 9em, 10em and 11em whatever the window's width, every extra pixel going to the bar. Each card is now one grid whose rows are subgrids: the names' column is as wide as the longest (minmax(0, max-content)), the bars take the rest and are never under 5em, and every row's bar starts at the same place; narrow, the names are shortened rather than a bar. Without subgrid (before Safari 16) each row sizes its own name the same way. sessions-bar-names.test.cjs fails without the change (196px name shown in 108px at 1600 wide); it, sessions-overview, sessions-calendar-fill, sessions-today, click-scroll and old-webkit pass in Chromium and WebKit, en and zh. 2026-10-01 23:20:56 +08:00
yetone cbf51ec7a4 settings: Number units lets Chinese say a large count in K/M/B instead of 万/亿 (John on Discord asked for K/M/B with the UI in Chinese) — the Requests tab's totals and chart axis and the tray panel's Usage tab shortened every count with 万 and 亿 whenever the language was Chinese (ledShort, the one place either is used). A Number units row under Currency, shown in Chinese only, picks 中文(万 / 亿)(the default, unchanged) or 英文(K / M / B), kept as settings.westernUnits and sent with the rest of Settings; picked, ledShort says it as English does (1.54B, 80M), the Usage page and an open panel redraw at once, and the axis labels are still measured before the plot. number-units.test.cjs fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; number-units, click-scroll, old-webkit, usage-chart-axis, text-size, currency, panel-usage pass. 2026-10-01 23:20:56 +08:00
yetone 0361189f9e test: every package's tests pass on Windows, and none writes outside its sandbox (the user: 把 Windows 上那几个失败的测试修掉) — on a Windows box the suite failed in most packages, and the root package hung for 25 minutes. Tests set HOME, which os.UserHomeDir ignores on Windows (it reads USERPROFILE), so they read and wrote the runner's own home; each now sets USERPROFILE beside HOME, and Devin's, Goose's, Crush's and Claude Desktop's tests set APPDATA/LOCALAPPDATA, where those keep their files there. said() read the command's output only once it returned, and a pipe holds a few KB on Windows, so magpie model --help blocked forever; it reads while the command writes. Unix permission bits (the gateway key store's 0600 too), exec bits and symlinks Windows lacks are checked only off Windows; TOML strings hold Windows paths as literal strings, JSON as marshalled ones; a URL's path is compared as one. The provider and usage packages run in a home of their own (TestMain, as the gateway's already does), since a CLI's identity kept by a refresh still going when its test ended landed in the runner's ~/.config/magpie — and on a Mac in the user's own. Two product fixes came out of it: imagemcp took a file:///C:/… root or image for the path /C:/…, which doesn't exist on Windows, and now makes a Windows path of it (a host for a UNC share); and session discovery could keep a new Cowork session hidden on Linux, where a folder's mtime is a kernel tick and its size doesn't change, so an entry added within the tick of the last listing left both as they were — a listing made within 2s of its folder's last change is read again, as git does for racy files. TestFilePath and TestRacyDirectoryListedAgain fail without the change. On Windows 11 every package passes (32 of 32) and a run of each package in a fresh home leaves nothing in home, APPDATA or LOCALAPPDATA; on Linux (WSL) and macOS go test -tags nogui ./... passes. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build pass. 2026-10-01 23:15:07 +08:00
yetone 38f184ae0e signin: Grok and Command Code sign in from magpie in Docker (xugui on Discord: docker版登不上grok和command code) — Grok's sign-in first installs Grok Build with its installer, curl … | bash, and the image has neither bash nor curl, so it stopped at "installing Grok Build failed (exec: "bash": executable file not found)"; the community Grok plugin, beside the built-in rather than moved onto, wasn't given the CLI at all and said "install Grok Build first". Where no shell and curl/wget can run, Grok Build is now installed as its installer does, from the same x.ai/cli (then its storage bucket) address: the stable version, its binary for the platform, checked with --version, linked as ~/.grok/bin/grok and agent, installer = "internal" noted in its config.toml; the plugin's sign-in installs it first too. The device sign-in itself needs nothing back from the browser and was fine. Command Code's Studio posts the key to magpie's 127.0.0.1 port in the background, which a browser outside the container can't reach and no address carries; while it waits, its sign-in now takes an API key made on commandcode.ai/settings/keys and pasted (as Command Code's own CLI offers), checked with whoami and its plan as Studio's was, and a plugin's browser sign-in offers "Use an API key instead", its own api way (the Command Code plugin has one). No identity, header or prompt changes. TestGrokInstallsWithoutShell, TestCommandCodeSignInPastedKey and TestGrokPluginSignInInstallsCLI fail without the change, signin-paste-key.test.cjs too. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; signin-paste-key, signin-paste-codex, plugin-signin, signin-callback, commandcode-risk, plugin-key-hint, signin-link, click-scroll, old-webkit pass. 2026-10-01 22:25:39 +08:00
yetone cd201d888c sessions: a Sessions page lists an agent's sessions by folder, to resume or delete, and deleted ones wait in magpie's trash to be restored (TJHHHH on Discord: session management like CC Switch's, deleting above all, in the GUI) — sessions were only seen on Usage, with no way to remove one. The new Sessions tab shows, per agent, every session under the folder it ran in, with its title or first prompt, when, how many messages and how big, a filter, its resume command to copy and, in the Mac app, Open in terminal. Picked sessions (or one, by its own button) are deleted after magpie's own dialog: their files (Claude Code's .jsonl, its subagents folder and what it keeps by the id under file-history, session-env and todos; every segment of a Codex rollout; Qoder's, WorkBuddy's, Pi's, oh-my-pi's) are moved into magpie's folder, trash/sessions/<agent>/<when>-<id>, with a manifest, never erased; Trash lists them and Restore puts them back where they were. A session written to in the last minute is left, and said so, as the agent may still be running it. Agents' own indexes (Codex's session_index.jsonl and state database, Claude Code's history.jsonl) are not edited: they are written while the agent runs, and a stale entry there is harmless. OpenCode, ZCode, dsh, Cline and Grok keep sessions in databases magpie doesn't write, and show no delete. With nine tabs the narrowest header gets a further step that draws the tabs in, and past that they scroll sideways rather than push the page wider (560px in English, or at 150% text). The dialog uses the shared confirmAsk/closeConfirmAsk, as the gateway keys' do. TestListAgent, TestDeleteRestoreClaude, TestDeleteRestoreCodex, TestDeleteActiveRefused (fails without the running check) and TestDeleteOnlyKnownAgents run in a temp HOME; sessions-manage.test.cjs (Chromium, WebKit, en, zh) fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; sessions-manage, click-scroll, old-webkit, text-size, session-terminal, sessions-today, brand, rail-tip, web-page, omarchy, model-test-one, gateway-caller-keys, gateway-connect-keys, api-key-usage, api-key-theme pass. 2026-10-01 22:11:46 +08:00
yetone 92760896e3 usage: the chart's side labels keep the card's padding (John on Discord: 这是bug吗?贴边了) — the plot of the Requests tab and the tray panel's Usage tab started a fixed 44px (34px in the panel) from the left, and the labels were drawn to end 6px short of it, so any label wider than that ran out leftward: "8000 万" in the panel reached past the card's 10px padding to its edge, and "200,000" or "$0.50" in the window crossed its padding too. The labels are now measured as drawn and the plot starts where the widest one ends, so every label stands as far in as the tabs above it, in English and Chinese, for tokens, cost in $ and ¥, and requests, small and large, wide and narrow. usage-chart-axis.test.cjs (Chromium and WebKit, en and zh) fails without the change. click-scroll, old-webkit, usage-ledger-chart, panel-usage, usage-ledger*, usage-refresh, usage-alert, api-key-usage, tray-usages, claude-usage-asked, currency pass; go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-01 21:59:55 +08:00
yetone 171d8dc646 web: behind a reverse proxy, magpie web prints MAGPIE_PUBLIC_URL as the page's link (#372) — with MAGPIE_PUBLIC_URL=https://magpie.example.com the link for other machines was http://magpie.example.com:3430/?k=…, its host on the page's port over plain http, which nothing answers. A public URL with no port of its own is now the link, scheme and path kept; one with a port (a container's published ports, http://<host>:3425) still gives that host on the page's port. TestNetworkLinksPublicURL fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-01 21:49:15 +08:00
yetone 58aca0eeb8 providers: an account whose list couldn't be had is asked again a minute on, and opening the panel asks (#422, Q1CHENL: after the in-app update Kiro still offered Auto alone until magpie was restarted by hand) — the update restarts magpie as a new process, whose one try for an account still without its vendor's list was made at start-up, or by the Providers page with 8 seconds for it; one that failed left the account on its fallback (Kiro's Auto) for ten minutes, and only the Providers page asked again, never the panel and its model picker. The retry is now a minute, and GET /api/state (the panel, the window's Agents page) starts FetchNew in the background, no more than once in 15s and never two at once, so the page doesn't wait on vendors. TestFetchNewRetriedSoon fails without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass. 2026-10-01 21:49:14 +08:00
Ns2Kracy bb1aa3acbd feat(gateway): named gateway keys with per-client usage (#344)
* feat(gui): show and filter usage for each API key

* feat(gateway): issue per-user caller keys and attribute usage

* feat(gui): manage gateway users and filter caller-key usage

* test: verify caller-key failover and document user management

* refactor(gateway): use named caller keys without user accounts

* feat(gui): manage named caller keys with existing account styles

* fix: remove user breakdown from CLI and leftover gateway styling

* fix(usage): drop stale user field from request response

* feat(gateway): authenticate LAN calls with named caller keys

* fix(gateway): preserve LAN key rotation in the named caller store

* fix(gui): retain native LAN key copy and rotation controls

* fix(gui): inherit the global theme for API key inputs

* fix(usage): show client API keys using the existing terminology

* fix(gateway): rotate named keys without changing usage identity

* fix(gui): unify gateway keys and selectable connection examples

Keep sharing controls in Settings and manage credentials only in Gateway. Refresh the named-key list on navigation, ignore stale reads after key actions, and generate all API examples from the selected address and enabled key.

* fix(gui): keep caller key names literal in connection menus

* fix(access): use Magpie as default key and drop user migration

* refactor(gui): name caller key source and tests consistently

* refactor(usage): split provider-key accounting out of caller-key PR

* fix(gateway): preserve LAN compatibility and add headless key management

* fix(gui): clarify gateway keys and confirm destructive actions

* test(gateway): guard caller attribution under 120 concurrent requests

* fix(gateway): keep open access, tolerate migration errors and revoke legacy keys

* fix(backup): preserve named gateway keys and honor no-keys restores

* fix(gui): keep local API-key semantics and disambiguate shared credentials

* docs(gateway): keep review scope focused and append browser test documentation

* fix(backup): retain existing default keys when old backups lack their store

* fix(gateway): attribute video creation usage to the caller key

* fix(access): explain default key revocation blocked by read-only settings

* test(usage): preserve provider and caller identities after rebase

* test(gui): reconcile caller key fixtures with merged main

* docs(usage): clarify caller coverage and independent provider accounting

* fix(gui): initialize fast members for model-shortcut groups

Latest main introduced fast-member arrays but omitted the model shortcut's draft. The existing unlisted-models browser regression fails unchanged on main. Initialize fast to an empty list and assert a standard-speed group is saved, without relaxing existing assertions.

* fix(access): retain legacy LAN access when the key store cannot be created

Allow only the configured legacy credential before either migration marker exists. Refuse revoked mirrors, disabled matches and unreadable stores; keep the fallback's caller identity stable when migration later succeeds. Cover read-only configuration directories, remote credential transports and revocation.

* fix(backup): save settings before replacing gateway credentials

Leave the key store untouched when settings validation or writes fail. Restore the previous settings association when the key store refuses its write, and report a rollback failure separately. Cover invalid settings and read-only settings/store failures.

* fix(gui): reset missing caller filters and focus key controls without scrolling

* test(provider): synchronize Zhipu quota fixture observations
2026-10-01 21:28:54 +08:00
Zhu Wang 1adb2b571e feat(routing): group requests by session with names and estimated costs (#396)
* feat(routing): group requests by session with names and estimated costs

* fix(routing): address session view review feedback

Preserve Luna Reserve and other header fallbacks for ordinary Codex turn metadata; reuse metadata and the kind across trace and native accounting. Default to By request and persist the grouping choice. Store only token accounting fields in route history while retaining legacy JSON compatibility.

Add regressions for helper precedence, native kind accounting, history compatibility and grouping preferences.
2026-10-01 20:50:15 +08:00
yetone 986a4474fe zcode: the Start Plan's 405 block is said for what it is, not only as a firewall (#425: ZCode 405 Method Not Allowed on GLM-5.3-Flash from DeepSeek Harness, while zcode2api works with the same account) — zcode.z.ai answers a Start Plan request it turns away with 405 "request has been blocked due to unusual activity" (code 3012 "method not allowed"), which magpie read as Alibaba Cloud's firewall blocking the address and told the user to wait or switch network. zcode2api's own write-ups (a137460387/zcode2api "3012 的根因与修复", gakiyukr/zcode2api-plus request.go) find the endpoint checks what a request carries and takes only one with ZCode's own system prompt, which zcode2api injects; magpie's endpoint, method and protocol are the same as theirs, and magpie doesn't pretend to be ZCode. On a Start Plan account the 405 / 3012 / block page now reads "ZCode's Start Plan turns away requests that don't come from the ZCode app itself, and magpie doesn't pretend to be it; it can also be a network block of this IP. Use an account with a GLM Coding Plan, or add another provider to this group", in place of the generic hint, to the agent and in the trace, where the Routing page gives it on its own line in English or Chinese; the group still goes on to its next member. A Coding Plan account and every other provider keep the generic hint. Requests, headers and prompts are unchanged. TestZCodeStartBlockedSaid and routing-zcode-blocked.test.cjs fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass (TestUserPath, a load flake, passes alone); routing-zcode-blocked, routing-blocked, routing-wb-refused, click-scroll, old-webkit pass. 2026-10-01 20:45:11 +08:00