provider: removing the account a subscription uses first puts the next one first, and Sign out… takes every account (歧路亡羊 on Discord) — Copilot's Sign out… stopped at "magpie uses wwjxhy first; put another account first" and kept the account

This commit is contained in:
yetone committed 2026-10-05 15:02:17 +08:00
1 parent 9fb51c7742
commit 30d952cf0c
5 files changed
+71 -29

No files matched your search

+52 -6
View File
@@ -2,7 +2,7 @@ package provider
import (
"encoding/json"
"strings"
"path/filepath"
"testing"
)
@@ -34,15 +34,23 @@ func TestForgetOnlyFirstLogin(t *testing.T) {
}
}
// two accounts: Remove on the first still asks for the other first,
// and signing the subscription out takes both
// two accounts: Remove on the first puts the other first, even one
// that was off, and signing the subscription out takes both
saveLogins(t,
savedLogin{Agent: "antigravity", User: "a@x.com", Auth: auth("ra"), On: true, First: true},
savedLogin{Agent: "antigravity", User: "b@x.com", Auth: auth("rb")})
ForgetAccounts()
if err := ForgetLogin("antigravity", "a@x.com"); err != nil {
t.Fatalf("removing the first of two: %v", err)
}
ForgetAccounts()
if ls := Logins("antigravity"); len(ls) != 1 || ls[0].User != "b@x.com" || !ls[0].Active || !ls[0].On {
t.Fatalf("after removing the first: %+v", ls)
}
saveLogins(t,
savedLogin{Agent: "antigravity", User: "a@x.com", Auth: auth("ra"), On: true, First: true},
savedLogin{Agent: "antigravity", User: "b@x.com", Auth: auth("rb"), On: true})
ForgetAccounts()
if err := ForgetLogin("antigravity", "a@x.com"); err == nil || !strings.Contains(err.Error(), "put another account first") {
t.Fatalf("removing the first of two: %v", err)
}
if err := ForgetAccount("antigravity"); err != nil {
t.Fatalf("signing out both: %v", err)
}
@@ -51,3 +59,41 @@ func TestForgetOnlyFirstLogin(t *testing.T) {
t.Fatalf("still listed: %+v", ls)
}
}
// 歧路亡羊 on Discord: Copilot signed in to the editors' own account and
// two of magpie's, one of them first. Removing the first puts the next
// first, and Sign out… takes them all, where it said "magpie uses wwjxhy
// first; put another account first".
func TestForgetFirstCopilotLogin(t *testing.T) {
signIn(t)
writeFile(t, filepath.Join(copilotConfigDir(), "github-copilot", "apps.json"), map[string]any{"github.com": map[string]any{"user": "octo", "oauth_token": "gho_own"}})
tok := func(s string) json.RawMessage { return json.RawMessage(`{"oauth_token":"` + s + `"}`) }
saved := []savedLogin{
{Agent: "copilot", User: "octo"},
{Agent: "copilot", User: "wwjxhy", Auth: tok("gho_w"), On: true, First: true},
{Agent: "copilot", User: "other", Auth: tok("gho_o"), On: true},
}
saveLogins(t, saved...)
ForgetAccounts()
if ls := Logins("copilot"); len(ls) != 3 || ls[0].User != "wwjxhy" || !ls[0].Active {
t.Fatalf("logins before: %+v", ls)
}
if err := ForgetLogin("copilot", "wwjxhy"); err != nil {
t.Fatalf("removing the first: %v", err)
}
ForgetAccounts()
ls := Logins("copilot")
if len(ls) != 2 || !ls[0].Active || ls[0].User == "wwjxhy" || ls[1].User == "wwjxhy" {
t.Fatalf("after removing the first: %+v", ls)
}
saveLogins(t, saved...)
ForgetAccounts()
if err := forgetLogins("copilot"); err != nil {
t.Fatalf("signing out every account: %v", err)
}
ForgetAccounts()
if ls := Logins("copilot"); len(ls) != 0 {
t.Fatalf("still listed: %+v", ls)
}
}
-3
View File
@@ -120,9 +120,6 @@ func TestGrokAccounts(t *testing.T) {
if b, _ := os.ReadFile(filepath.Join(own, "auth.json")); len(b) == 0 {
t.Fatal("the CLI's own sign-in was touched")
}
if err := ForgetLogin("grok", "two@x.ai"); err == nil {
t.Fatal("forgot the account in use first")
}
if err := SetLoginOn("grok", "me@x.ai", false); err != nil {
t.Fatal(err)
}
+7 -3
View File
@@ -173,13 +173,17 @@ func TestKiroSeveralAccounts(t *testing.T) {
home = l.Home
}
}
if err := ForgetLogin("kiro", "two@example.com"); err == nil {
t.Fatal("forgot the account in use first")
// the first removed, the next is put first
if err := ForgetLogin("kiro", "two@example.com"); err != nil {
t.Fatal(err)
}
if ls := Logins("kiro"); len(ls) != 1 || ls[0].User != "me@example.com" || !ls[0].Active {
t.Fatalf("after removing the first: %+v", ls)
}
if err := ForgetLogin("kiro", "me@example.com"); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(home); !os.IsNotExist(err) || len(Logins("kiro")) != 1 {
if _, err := os.Stat(home); !os.IsNotExist(err) || len(Logins("kiro")) != 0 {
t.Fatalf("home %s left: %v, %+v", home, err, Logins("kiro"))
}
}
-3
View File
@@ -192,9 +192,6 @@ func TestPluginAccounts(t *testing.T) {
if a := also(); len(a) != 1 || a[0] != "blue@fake@fakeco" {
t.Fatalf("also on %v", a)
}
if err := ForgetLogin("fakeco", "red@fake"); err == nil {
t.Fatal("the first account was removed")
}
if err := ForgetLogin("fakeco", "blue@fake"); err != nil {
t.Fatal(err)
}
+12 -14
View File
@@ -168,12 +168,12 @@ func setSideLoginOn(agent, user string, on bool, ls []sideLogin) error {
// forgetSideLogin drops an account magpie signed in; gone is told what it
// kept. The agent's own sign-in is only hidden: its files stay as they
// are, and it shows again once the agent signs in anew (ownMark). Hidden
// while first, the next account is put first. One of magpie's that is
// first waits for another to be put first, unless it is the only one: then
// there is none to put first, and it goes, leaving the subscription with no
// account (#874: a removed subscription's Sign out… could never take its
// one account).
// are, and it shows again once the agent signs in anew (ownMark). Either
// one, removed while first, puts the next account first; the only one
// leaves the subscription with no account (#874: a removed subscription's
// Sign out… could never take its one account; 歧路亡羊 on Discord: one of
// magpie's that was first was refused, "put another account first", even
// by the subscription's Sign out…, which takes every account).
func forgetSideLogin(agent, user string, ls []sideLogin, gone func(savedLogin)) error {
own := slices.ContainsFunc(ls, func(l sideLogin) bool { return l.Own && strings.EqualFold(l.User, user) })
first := strings.EqualFold(activeOf(ls), user)
@@ -186,22 +186,20 @@ func forgetSideLogin(agent, user string, ls []sideLogin, gone func(savedLogin))
}
}
}
if first && !own && next != "" {
return fmt.Errorf("magpie uses %s first; put another account first", user)
}
mark := hiddenNoMark
if own {
mark = firstNonEmpty(ownMark(agent), hiddenNoMark)
}
var old savedLogin
err := editSideLogin(agent, user, func(saved []savedLogin, i int) ([]savedLogin, error) {
for j := range saved {
if next != "" && j != i && saved[j].Agent == agent {
saved[j].First = strings.EqualFold(saved[j].User, next)
saved[j].On = saved[j].On || saved[j].First
}
}
if saved[i].own() {
saved[i].Hidden, saved[i].First = mark, false
for j := range saved {
if next != "" && saved[j].Agent == agent {
saved[j].First = strings.EqualFold(saved[j].User, next)
}
}
return saved, nil
}
old = saved[i]