Merge pull request #1454 from kaitranntt/codex/fix-email-fallback-parsing-issue

fix(cliproxy): infer emails from alternate token prefixes
This commit is contained in:
Kai (Tam Nhu) Tran
2026-05-30 16:11:54 -04:00
committed by GitHub
2 changed files with 45 additions and 4 deletions
@@ -280,6 +280,40 @@ describe('account registry integrity', () => {
});
});
it('strips alternate token file prefixes when inferring missing emails', async () => {
const cases = [
{ provider: 'gemini', tokenFile: 'google-user@example.com.json', type: 'gemini' },
{ provider: 'codex', tokenFile: 'openai-user@example.com.json', type: 'codex' },
{ provider: 'agy', tokenFile: 'antigravity-user@example.com.json', type: 'antigravity' },
{
provider: 'ghcp',
tokenFile: 'github-copilot-user@example.com.json',
type: 'github-copilot',
},
] as const;
for (const testCase of cases) {
await withIsolatedHome(async (homeDir) => {
const authDir = path.join(homeDir, '.ccs', 'cliproxy', 'auth');
fs.mkdirSync(authDir, { recursive: true });
fs.writeFileSync(
path.join(authDir, testCase.tokenFile),
JSON.stringify({ type: testCase.type }),
'utf8'
);
const { discoverExistingAccounts, loadAccountsRegistry } = await loadRegistryModule();
discoverExistingAccounts();
const registry = loadAccountsRegistry();
const providerAccounts = registry.providers[testCase.provider];
expect(Object.keys(providerAccounts?.accounts ?? {})).toEqual(['user@example.com']);
expect(providerAccounts?.accounts['user@example.com']?.email).toBe('user@example.com');
expect(providerAccounts?.accounts['user@example.com']?.tokenFile).toBe(testCase.tokenFile);
});
}
});
it('preserves the corrupted registry backup when recovery cannot rewrite accounts.json', async () => {
await withIsolatedHome(async (homeDir) => {
const authDir = path.join(homeDir, '.ccs', 'cliproxy', 'auth');
+11 -4
View File
@@ -7,6 +7,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as lockfile from 'proper-lockfile';
import { CLIProxyProvider } from '../types';
import { PROVIDER_CAPABILITIES } from '../provider-capabilities';
import { PROVIDER_TYPE_VALUES } from '../auth/auth-types';
import { getAuthDir, getCliproxyDir } from '../config/config-generator';
import { AccountsRegistry, AccountInfo, PROVIDERS_WITHOUT_EMAIL } from './types';
@@ -61,15 +62,21 @@ function resolveProviderFromTokenType(typeValue: string): CLIProxyProvider | und
const EMAIL_FILE_NAME_PATTERN = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i;
function stripTokenFileProviderPrefix(baseName: string, provider: CLIProxyProvider): string {
const knownPrefixes = [...PROVIDER_CAPABILITIES[provider].authFilePrefixes, `${provider}-`].sort(
(a, b) => b.length - a.length
);
const prefix = knownPrefixes.find((knownPrefix) => baseName.startsWith(knownPrefix));
return prefix ? baseName.slice(prefix.length) : baseName;
}
function inferEmailFromTokenFileName(
tokenFile: string,
provider: CLIProxyProvider
): string | undefined {
const baseName = tokenFile.replace(/\.json$/i, '');
const providerPrefix = `${provider}-`;
const candidate = baseName.startsWith(providerPrefix)
? baseName.slice(providerPrefix.length)
: baseName;
const candidate = stripTokenFileProviderPrefix(baseName, provider);
if (PROVIDERS_WITHOUT_EMAIL.includes(provider)) {
const scopedCandidate = candidate.slice(candidate.indexOf('-') + 1);