mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 14:15:56 +00:00
feat(cliproxy): support duplicate-email codex accounts
- keep Codex team and personal auth files as separate identities - resolve quota and live monitor stats by token file-backed account id - surface duplicate-aware account labels across the dashboard and variant UI
This commit is contained in:
1 parent
43ea5554e6
commit
c73f33872a
23 files changed
+781
-143
No files matched your search
@@ -116,4 +116,41 @@ describe('registerAccount optional nickname flow', () => {
|
||||
|
||||
expect(match).toBeNull();
|
||||
});
|
||||
|
||||
it('treats duplicate Codex emails as distinct accounts and rejects bare-email lookups', async () => {
|
||||
const result = await withIsolatedHome(async (homeDir) => {
|
||||
writeTokenFile(homeDir, 'codex-04a0f049-kaidu.kd@gmail.com-team.json');
|
||||
writeTokenFile(homeDir, 'codex-kaidu.kd@gmail.com-free.json');
|
||||
const { registerAccount, getProviderAccounts, findAccountByQuery } = await loadAccountManager();
|
||||
const team = registerAccount(
|
||||
'codex',
|
||||
'codex-04a0f049-kaidu.kd@gmail.com-team.json',
|
||||
'kaidu.kd@gmail.com'
|
||||
);
|
||||
const free = registerAccount(
|
||||
'codex',
|
||||
'codex-kaidu.kd@gmail.com-free.json',
|
||||
'kaidu.kd@gmail.com'
|
||||
);
|
||||
|
||||
return {
|
||||
team,
|
||||
free,
|
||||
accounts: getProviderAccounts('codex'),
|
||||
ambiguousLookup: findAccountByQuery('codex', 'kaidu.kd@gmail.com'),
|
||||
teamLookup: findAccountByQuery('codex', 'kaidu.kd@gmail.com#04a0f049-team'),
|
||||
freeLookup: findAccountByQuery('codex', free.id),
|
||||
};
|
||||
});
|
||||
|
||||
expect(result.team.id).toBe('kaidu.kd@gmail.com');
|
||||
expect(result.free.id).toBe('kaidu.kd@gmail.com#free');
|
||||
expect(result.accounts.map((account) => account.id).sort()).toEqual([
|
||||
'kaidu.kd@gmail.com#04a0f049-team',
|
||||
'kaidu.kd@gmail.com#free',
|
||||
]);
|
||||
expect(result.ambiguousLookup).toBeNull();
|
||||
expect(result.teamLookup?.id).toBe('kaidu.kd@gmail.com#04a0f049-team');
|
||||
expect(result.freeLookup?.id).toBe('kaidu.kd@gmail.com#free');
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,7 @@ let buildCodexQuotaWindows: typeof import('../../../src/cliproxy/quota-fetcher-c
|
||||
let buildCodexCoreUsageSummary: typeof import('../../../src/cliproxy/quota-fetcher-codex').buildCodexCoreUsageSummary;
|
||||
let fetchCodexQuota: typeof import('../../../src/cliproxy/quota-fetcher-codex').fetchCodexQuota;
|
||||
let getUnknownCodexWindowLabels: typeof import('../../../src/cliproxy/quota-fetcher-codex').getUnknownCodexWindowLabels;
|
||||
let registerAccount: typeof import('../../../src/cliproxy/account-manager').registerAccount;
|
||||
|
||||
function createCodexAccount(
|
||||
accountId: string,
|
||||
@@ -40,6 +41,7 @@ beforeEach(async () => {
|
||||
);
|
||||
mock.module('../../../src/cliproxy/config-generator', () => configGenerator);
|
||||
mock.module('../../../src/cliproxy/account-manager', () => accountManager);
|
||||
({ registerAccount } = accountManager);
|
||||
|
||||
({
|
||||
buildCodexQuotaWindows,
|
||||
@@ -409,6 +411,55 @@ describe('Codex Quota Fetcher', () => {
|
||||
expect(result.actionHint).toContain('ccs cliproxy auth codex');
|
||||
});
|
||||
|
||||
it('uses the registry token file for duplicate-email Codex accounts', async () => {
|
||||
createValidCodexAccount(
|
||||
'kaidu.kd@gmail.com',
|
||||
'workspace-team',
|
||||
'codex-04a0f049-kaidu.kd@gmail.com-team.json'
|
||||
);
|
||||
createValidCodexAccount(
|
||||
'kaidu.kd@gmail.com',
|
||||
'workspace-free',
|
||||
'codex-kaidu.kd@gmail.com-free.json'
|
||||
);
|
||||
|
||||
registerAccount(
|
||||
'codex',
|
||||
'codex-04a0f049-kaidu.kd@gmail.com-team.json',
|
||||
'kaidu.kd@gmail.com'
|
||||
);
|
||||
const freeAccount = registerAccount(
|
||||
'codex',
|
||||
'codex-kaidu.kd@gmail.com-free.json',
|
||||
'kaidu.kd@gmail.com'
|
||||
);
|
||||
|
||||
const fetchSpy = mock((input: RequestInfo | URL, init?: RequestInit) =>
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
plan_type: 'free',
|
||||
rate_limit: {
|
||||
primary_window: { used_percent: 10, reset_after_seconds: 3600 },
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
)
|
||||
) as typeof fetch;
|
||||
global.fetch = fetchSpy;
|
||||
|
||||
const result = await fetchCodexQuota(freeAccount.id);
|
||||
const requestInit = fetchSpy.mock.calls[0]?.[1] as RequestInit | undefined;
|
||||
const headers = new Headers(requestInit?.headers);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(headers.get('ChatGPT-Account-Id')).toBe('workspace-free');
|
||||
});
|
||||
|
||||
it('maps 403 responses to forbidden metadata', async () => {
|
||||
createValidCodexAccount('forbidden@example.com', 'workspace-forbidden');
|
||||
|
||||
|
||||
@@ -288,4 +288,45 @@ describe('buildCliproxyStatsFromUsageResponse', () => {
|
||||
});
|
||||
expect(stats.requestsByProvider).toEqual({ codex: 1, 'ccs-internal-managed': 2 });
|
||||
});
|
||||
|
||||
it('derives duplicate-email Codex account ids from auth file metadata', () => {
|
||||
const usage = createInternallyBucketedUsage([
|
||||
createDetail({ auth_index: 'codex-team', source: 'kaidu.kd@gmail.com' }),
|
||||
createDetail({
|
||||
auth_index: 'codex-free',
|
||||
source: 'kaidu.kd@gmail.com',
|
||||
timestamp: '2025-03-26T10:01:00.000Z',
|
||||
failed: true,
|
||||
}),
|
||||
]);
|
||||
const authFiles: CliproxyManagementAuthFile[] = [
|
||||
{
|
||||
auth_index: 'codex-team',
|
||||
provider: 'codex',
|
||||
email: 'kaidu.kd@gmail.com',
|
||||
name: 'codex-04a0f049-kaidu.kd@gmail.com-team.json',
|
||||
},
|
||||
{
|
||||
auth_index: 'codex-free',
|
||||
provider: 'codex',
|
||||
email: 'kaidu.kd@gmail.com',
|
||||
name: 'codex-kaidu.kd@gmail.com-free.json',
|
||||
},
|
||||
];
|
||||
|
||||
const stats = buildCliproxyStatsFromUsageResponse(usage, { authFiles });
|
||||
|
||||
expect(stats.accountStats['codex:kaidu.kd@gmail.com#04a0f049-team']).toMatchObject({
|
||||
provider: 'codex',
|
||||
source: 'kaidu.kd@gmail.com#04a0f049-team',
|
||||
successCount: 1,
|
||||
failureCount: 0,
|
||||
});
|
||||
expect(stats.accountStats['codex:kaidu.kd@gmail.com#free']).toMatchObject({
|
||||
provider: 'codex',
|
||||
source: 'kaidu.kd@gmail.com#free',
|
||||
successCount: 0,
|
||||
failureCount: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user