provider: a Copilot account signed in from magpie gets its allowance card, the editors' sign-in or not (#386)

The Usage page's Copilot card was asked only when copilotLogin(cfg) found the
editors' or the standalone CLI's own GitHub sign-in
(~/.config/github-copilot/apps.json or hosts.json, else ~/.copilot/config.json).
An account signed in from magpie itself is kept in logins.json, and
copilotLogin never looks there: with no editor and no Copilot CLI signed in on
the machine, no Copilot allowance was fetched at all. The Usage page, the menu
bar's usage picker and the gateway's GET /v1/magpie/quotas had no Copilot card,
while `magpie accounts` did show one (that path reads copilotLoginList through
builtinLogins, login_usage.go) and quotas.json kept a fresh reading written by
it — the account was known and readable, only never asked where the page looks.

copilotLoginList() covers both the editors'/CLI's own account and the accounts
magpie signed in (copilot_accounts.go), and loginQuota resolves each account's
own token by user, so the fetch is scheduled from that list now, as Kiro's,
ZCode's, Qoder's, Zed's and WorkBuddy's already are. The single-account branch
this replaces called copilotSubscriptionUsage with the editors' token for the
same account, which perLogin reaches the same way.

TestCopilotQuotaWithoutEditorsSignIn fails without the change ("no Copilot
card: an account signed in from magpie alone was left out") and passes with it.
go vet ./internal/provider/ and the internal/provider package's tests pass.
This commit is contained in:
Bo Zou authored and GitHub committed 2026-10-01 14:43:13 +08:00
1 parent d5bb8eedd9
commit f56d105495
2 files changed
+64 -10

No files matched your search

+9 -10
View File
@@ -252,16 +252,15 @@ func fetchSubscriptionUsage() []SubscriptionQuota {
fetches = append(fetches, withUser(p.Account.User, func() SubscriptionQuota { return codexSubscriptionUsage(viaLogin("codex", p.Account.User), auth) }))
}
}
cfg := os.Getenv("XDG_CONFIG_HOME")
if cfg == "" {
cfg = filepath.Join(home, ".config")
}
if app, ok := copilotLogin(cfg); ok && !hidden["copilot"] {
if ls := copilotLoginList(); len(ls) > 1 {
fetches = append(fetches, perLogin(via("copilot"), ls, "Copilot", "githubcopilot")...)
} else {
fetches = append(fetches, withUser(app.User, func() SubscriptionQuota { return copilotSubscriptionUsage(viaLogin("copilot", app.User), app.Token) }))
}
// Every Copilot account magpie knows, the editors' or the CLI's own
// sign-in or not: one signed in from magpie alone is enough
// (copilotLoginList reads both, copilot_accounts.go). Asking only
// when copilotLogin found the editors' token left an account magpie
// signed in itself without a card on the Usage page, and out of the
// gateway's GET /v1/magpie/quotas, however fresh its reading was
// (subscription_usage_test.go, TestCopilotQuotaWithoutEditorsSignIn).
if ls := copilotLoginList(); len(ls) > 0 && !hidden["copilot"] {
fetches = append(fetches, perLogin(via("copilot"), ls, "Copilot", "githubcopilot")...)
}
}
if moved("kiro") {
@@ -2,6 +2,10 @@ package provider
import (
"context"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
)
@@ -119,3 +123,54 @@ func TestChosenWindows(t *testing.T) {
t.Fatalf("antigravity: %+v", got)
}
}
// A Copilot account magpie signed in itself gets its card even when the
// editors' own sign-in isn't on the machine: every account magpie knows is
// asked, not only the one the editors' or the CLI's token belongs to.
func TestCopilotQuotaWithoutEditorsSignIn(t *testing.T) {
home := signIn(t)
t.Setenv("COPILOT_HOME", filepath.Join(home, ".copilot")) // not the machine's own CLI, when it has one
// the editors' own sign-in gone: what magpie keeps is all that's left
for _, dir := range []string{filepath.Join(home, ".config", "github-copilot"), filepath.Join(home, ".copilot")} {
if err := os.RemoveAll(dir); err != nil {
t.Fatal(err)
}
}
if _, ok := copilotLogin(filepath.Join(home, ".config")); ok {
t.Fatal("the editors' own sign-in is still readable")
}
if err := addCopilotLogin("hubot", "Pro+", "ghu_hubot"); err != nil {
t.Fatal(err)
}
if ls := copilotLoginList(); len(ls) != 1 || ls[0].User != "hubot" {
t.Fatalf("copilotLoginList = %+v", ls)
}
old := CopilotUserURL
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != "token ghu_hubot" {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.Write([]byte(`{"copilot_plan":"individual_pro","quota_snapshots":{"premium_interactions":{"has_quota":true,"entitlement":7000,"quota_remaining":6996}}}`))
}))
defer srv.Close()
CopilotUserURL = srv.URL
t.Cleanup(func() { CopilotUserURL = old })
var card *SubscriptionQuota
for _, q := range fetchSubscriptionUsage() {
if q.Provider == "copilot" {
card = &q
}
}
if card == nil {
t.Fatal("no Copilot card: an account signed in from magpie alone was left out")
}
if card.User != "hubot" || card.Plan != "individual_pro" || len(card.Windows) != 1 {
t.Fatalf("Copilot card = %+v", *card)
}
if w := card.Windows[0]; w.Name != "Premium requests" || w.Display != "4 / 7000" {
t.Fatalf("window = %+v", w)
}
}