fix: skip paused codex accounts in bar refresh (#1620)

This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-06-30 12:59:54 -04:00
1 parent 396e01ab6f
commit 0527e276b1
2 files changed
+248 -3

No files matched your search

+49 -3
View File
@@ -43,7 +43,7 @@ import {
} from './claude-native-credentials';
import { fetchClaudeQuotaWithToken } from '../../cliproxy/quota/quota-fetcher-claude';
import { fetchCodexQuota } from '../../cliproxy/quota/quota-fetcher-codex';
import { getDefaultAccount } from '../../cliproxy/accounts/query';
import { getDefaultAccount, getProviderAccounts } from '../../cliproxy/accounts/query';
import { getCodexLocalQuota, type CodexLocalQuota } from './codex-local-quota-collector';
import type { ClaudeQuotaResult, CodexQuotaResult } from '../../cliproxy/quota/quota-types';
import type { BarSummaryRow, QuotaWindowDetail } from '../routes/bar-routes';
@@ -618,10 +618,17 @@ function listCodexProfilesFromDisk(): string[] {
const { CodexProfileRegistry } = require('../../codex-auth/codex-profile-registry') as {
CodexProfileRegistry: new () => {
listProfiles: () => string[];
getProfile: (name: string) => { email?: string; account_id?: string };
};
};
const registry = new CodexProfileRegistry();
const profiles = registry.listProfiles();
const pausedIdentities = getPausedCodexAccountIdentities();
const profiles = registry
.listProfiles()
.filter(
(name) =>
!codexProfileMatchesPausedAccount(name, registry.getProfile(name), pausedIdentities)
);
// Add the bare ~/.codex/auth.json (the default `ccsx` invocation) as the
// DEFAULT_PROFILE account when it exists. It is the "default way of running",
// distinct from named `ccsx <profile>` profiles.
@@ -643,11 +650,23 @@ function getDefaultCodexProfileFromDisk(): string | null {
const { CodexProfileRegistry } = require('../../codex-auth/codex-profile-registry') as {
CodexProfileRegistry: new () => {
getDefault: () => string | null;
getProfile: (name: string) => { email?: string; account_id?: string };
listProfiles: () => string[];
};
};
const registry = new CodexProfileRegistry();
const pausedIdentities = getPausedCodexAccountIdentities();
const def = registry.getDefault();
if (def) return def;
if (def && !codexProfileMatchesPausedAccount(def, registry.getProfile(def), pausedIdentities)) {
return def;
}
const fallback = registry
.listProfiles()
.find(
(name) =>
!codexProfileMatchesPausedAccount(name, registry.getProfile(name), pausedIdentities)
);
if (fallback) return fallback;
// Fall back to the bare ~/.codex account (DEFAULT_PROFILE) when no registry
// default is set — it is the default `ccsx` invocation.
if (fs.existsSync(path.join(os.homedir(), '.codex', 'auth.json'))) return DEFAULT_PROFILE;
@@ -657,6 +676,33 @@ function getDefaultCodexProfileFromDisk(): string | null {
}
}
function getPausedCodexAccountIdentities(): Set<string> | null {
try {
const accounts = getProviderAccounts('codex');
if (accounts.length === 0) return null;
const identities = new Set<string>();
for (const account of accounts) {
if (!account.paused) continue;
identities.add(account.id);
if (account.email) identities.add(account.email);
}
return identities;
} catch {
return null;
}
}
function codexProfileMatchesPausedAccount(
profileName: string,
profile: { email?: string; account_id?: string },
pausedIdentities: Set<string> | null
): boolean {
if (!pausedIdentities || pausedIdentities.size === 0) return false;
return [profileName, profile.email, profile.account_id].some(
(identity): identity is string => typeof identity === 'string' && pausedIdentities.has(identity)
);
}
// ============================================================================
// Per-profile collectors with full safety controls
// ============================================================================
@@ -7,6 +7,9 @@
*/
import { beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import {
getNativeAccountRows,
getCachedNativeAccountRows,
@@ -467,6 +470,202 @@ function makeCodexDeps(
// ============================================================================
describe('Codex network path', () => {
it('production profile enumeration skips paused Codex accounts before live network refresh', async () => {
const originalCcsHome = process.env.CCS_HOME;
const originalHome = process.env.HOME;
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-bar-paused-codex-'));
process.env.CCS_HOME = tempHome;
process.env.HOME = tempHome;
try {
const ccsDir = path.join(tempHome, '.ccs');
const cliproxyDir = path.join(ccsDir, 'cliproxy');
fs.mkdirSync(cliproxyDir, { recursive: true });
fs.writeFileSync(
path.join(ccsDir, 'codex-profiles.yaml'),
[
'version: "1.0"',
'default: paused',
'profiles:',
' paused:',
' type: codex',
' created: "2026-01-01T00:00:00.000Z"',
' last_used: null',
' email: paused-codex@example.com',
' account_id: paused-codex@example.com',
' active:',
' type: codex',
' created: "2026-01-02T00:00:00.000Z"',
' last_used: null',
' email: active-codex@example.com',
' account_id: active-codex@example.com',
'',
].join('\n')
);
fs.writeFileSync(
path.join(cliproxyDir, 'accounts.json'),
JSON.stringify(
{
version: 1,
providers: {
codex: {
default: 'paused-codex@example.com',
accounts: {
'paused-codex@example.com': {
email: 'paused-codex@example.com',
tokenFile: 'paused-codex@example.com.json',
paused: true,
},
'active-codex@example.com': {
email: 'active-codex@example.com',
tokenFile: 'active-codex@example.com.json',
},
},
},
},
},
null,
2
)
);
fs.mkdirSync(path.join(cliproxyDir, 'auth'), { recursive: true });
fs.mkdirSync(path.join(cliproxyDir, 'auth-paused'), { recursive: true });
fs.writeFileSync(
path.join(cliproxyDir, 'auth', 'active-codex@example.com.json'),
JSON.stringify({ type: 'codex' })
);
fs.writeFileSync(
path.join(cliproxyDir, 'auth-paused', 'paused-codex@example.com.json'),
JSON.stringify({ type: 'codex' })
);
const fetchedAccountIds: string[] = [];
const deps = makeCodexDeps({
clock: { now: 1_000_000 },
listClaudeProfiles: () => [],
readCredentials: undefined,
getDefaultCodexAccountId: undefined,
readCodexNativeAuth: (profile: string) => ({
accessToken: `token-${profile}`,
accountId: profile === 'active' ? 'active-codex@example.com' : 'paused-codex@example.com',
}),
fetchCodexNetworkQuota: async (accountId: string) => {
fetchedAccountIds.push(accountId);
return { ...codexSuccessQuota(), accountId };
},
});
const rows = await getNativeAccountRows(deps);
expect(fetchedAccountIds).toEqual(['active-codex@example.com']);
expect(rows.find((row) => row.profile === 'paused')).toBeUndefined();
const active = rows.find((row) => row.profile === 'active');
expect(active?.paused).toBe(false);
expect(active?.is_default).toBe(true);
} finally {
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
if (originalHome !== undefined) {
process.env.HOME = originalHome;
} else {
delete process.env.HOME;
}
fs.rmSync(tempHome, { recursive: true, force: true });
}
});
it('production profile enumeration does not live-refresh when all Codex accounts are paused', async () => {
const originalCcsHome = process.env.CCS_HOME;
const originalHome = process.env.HOME;
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-bar-all-paused-codex-'));
process.env.CCS_HOME = tempHome;
process.env.HOME = tempHome;
try {
const ccsDir = path.join(tempHome, '.ccs');
const cliproxyDir = path.join(ccsDir, 'cliproxy');
fs.mkdirSync(cliproxyDir, { recursive: true });
fs.writeFileSync(
path.join(ccsDir, 'codex-profiles.yaml'),
[
'version: "1.0"',
'default: paused',
'profiles:',
' paused:',
' type: codex',
' created: "2026-01-01T00:00:00.000Z"',
' last_used: null',
' email: paused-codex@example.com',
' account_id: paused-codex@example.com',
'',
].join('\n')
);
fs.writeFileSync(
path.join(cliproxyDir, 'accounts.json'),
JSON.stringify(
{
version: 1,
providers: {
codex: {
default: 'paused-codex@example.com',
accounts: {
'paused-codex@example.com': {
email: 'paused-codex@example.com',
tokenFile: 'paused-codex@example.com.json',
paused: true,
},
},
},
},
},
null,
2
)
);
fs.mkdirSync(path.join(cliproxyDir, 'auth-paused'), { recursive: true });
fs.writeFileSync(
path.join(cliproxyDir, 'auth-paused', 'paused-codex@example.com.json'),
JSON.stringify({ type: 'codex' })
);
let networkCalls = 0;
const deps = makeCodexDeps({
clock: { now: 1_000_000 },
listClaudeProfiles: () => [],
readCredentials: undefined,
getDefaultCodexAccountId: undefined,
readCodexNativeAuth: (profile: string) => ({
accessToken: `token-${profile}`,
accountId: 'paused-codex@example.com',
}),
fetchCodexNetworkQuota: async () => {
networkCalls += 1;
return codexSuccessQuota();
},
});
const rows = await getNativeAccountRows(deps);
expect(networkCalls).toBe(0);
expect(deps.localCount()).toBe(0);
expect(rows.find((row) => row.profile === 'paused')).toBeUndefined();
} finally {
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
if (originalHome !== undefined) {
process.env.HOME = originalHome;
} else {
delete process.env.HOME;
}
fs.rmSync(tempHome, { recursive: true, force: true });
}
});
it('network success builds a fresh row from coreUsage — no staleAsOf, health ok, correct windows', async () => {
const clock = { now: 1_000_000 };
const deps = makeCodexDeps({ clock });