Skip to content

Commit 79ba2a0

Browse files
committed
fix: skip accounts with all workspaces disabled during rotation
Accounts with tracked workspaces could remain eligible for family selection even after every workspace had been disabled. That leaves a workspace-exhausted account in the rotation pool and can block fallback selection unnecessarily. Treat accounts whose tracked workspaces are all disabled as unavailable for selection, while keeping legacy workspace-less accounts eligible.
1 parent 71645a5 commit 79ba2a0

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

lib/accounts.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ export class AccountManager {
535535
const account = this.getAccountByIndex(index);
536536
if (!account) return false;
537537
if (account.enabled === false) return false;
538+
if (!this.hasEnabledWorkspaces(account)) return false;
538539
clearExpiredRateLimits(account);
539540
return (
540541
!isRateLimitedForFamily(account, family, model) &&
@@ -606,6 +607,7 @@ export class AccountManager {
606607
const account = this.accounts[idx];
607608
if (!account) continue;
608609
if (account.enabled === false) continue;
610+
if (!this.hasEnabledWorkspaces(account)) continue;
609611

610612
clearExpiredRateLimits(account);
611613
if (
@@ -636,6 +638,7 @@ export class AccountManager {
636638
const account = this.accounts[idx];
637639
if (!account) continue;
638640
if (account.enabled === false) continue;
641+
if (!this.hasEnabledWorkspaces(account)) continue;
639642

640643
clearExpiredRateLimits(account);
641644
if (
@@ -666,6 +669,7 @@ export class AccountManager {
666669
.map((account): AccountWithMetrics | null => {
667670
if (!account) return null;
668671
if (account.enabled === false) return null;
672+
if (!this.hasEnabledWorkspaces(account)) return null;
669673
clearExpiredRateLimits(account);
670674
const isAvailable =
671675
!isRateLimitedForFamily(account, family, model) &&

test/accounts.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,55 @@ describe("AccountManager", () => {
427427
expect(manager.isAccountAvailableForFamily(3, "codex")).toBe(true);
428428
});
429429

430+
it("treats accounts with all tracked workspaces disabled as unavailable for selection", () => {
431+
const now = Date.now();
432+
const stored = {
433+
version: 3 as const,
434+
activeIndex: 0,
435+
activeIndexByFamily: { codex: 0 },
436+
accounts: [
437+
{
438+
refreshToken: "token-workspace-disabled",
439+
addedAt: now,
440+
lastUsed: now,
441+
workspaces: [
442+
{ id: "workspace-1", name: "Workspace 1", enabled: false },
443+
{ id: "workspace-2", name: "Workspace 2", enabled: false },
444+
],
445+
currentWorkspaceIndex: 0,
446+
},
447+
{
448+
refreshToken: "token-ready",
449+
addedAt: now,
450+
lastUsed: now - 10_000,
451+
},
452+
],
453+
};
454+
455+
const manager = new AccountManager(undefined, stored as never);
456+
457+
expect(manager.isAccountAvailableForFamily(0, "codex")).toBe(false);
458+
expect(manager.getCurrentOrNextForFamily("codex")?.refreshToken).toBe("token-ready");
459+
expect(manager.getNextForFamily("codex")?.refreshToken).toBe("token-ready");
460+
expect(manager.getCurrentOrNextForFamilyHybrid("codex")?.refreshToken).toBe("token-ready");
461+
});
462+
463+
it("keeps workspace-less legacy accounts eligible for selection", () => {
464+
const now = Date.now();
465+
const stored = {
466+
version: 3 as const,
467+
activeIndex: 0,
468+
activeIndexByFamily: { codex: 0 },
469+
accounts: [{ refreshToken: "token-legacy", addedAt: now, lastUsed: now }],
470+
};
471+
472+
const manager = new AccountManager(undefined, stored as never);
473+
474+
expect(manager.hasEnabledWorkspaces(manager.getAccountByIndex(0)!)).toBe(true);
475+
expect(manager.isAccountAvailableForFamily(0, "codex")).toBe(true);
476+
expect(manager.getCurrentOrNextForFamily("codex")?.refreshToken).toBe("token-legacy");
477+
});
478+
430479
it("returns false for invalid account index in availability checks", () => {
431480
const now = Date.now();
432481
const stored = {

0 commit comments

Comments
 (0)