fix(cliproxy): infer emails from alternate token prefixes

This commit is contained in:
Kai (Tam Nhu) Tran
2026-05-30 14:57:12 -04:00
parent 23c819cabc
commit c0cd44ecaa
4 changed files with 51 additions and 13 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);
+4 -6
View File
@@ -483,9 +483,8 @@ export async function reconcileExhaustedRotationAccounts(
return [];
}
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await import(
'../accounts/account-safety'
);
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } =
await import('../accounts/account-safety');
restoreExpiredQuotaPauses();
const config = loadOrCreateUnifiedConfig();
@@ -593,9 +592,8 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
return { proceed: true, accountId: defaultAccount?.id || '' };
}
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await import(
'../accounts/account-safety'
);
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } =
await import('../accounts/account-safety');
restoreExpiredQuotaPauses();
const config = loadOrCreateUnifiedConfig();
@@ -112,9 +112,8 @@ export async function runImageAnalysisCheck(results: HealthCheck): Promise<void>
* Fix image analysis configuration issues
*/
export async function fixImageAnalysisConfig(): Promise<boolean> {
const { updateConfig, loadOrCreateUnifiedConfig } = await import(
'../../config/config-loader-facade'
);
const { updateConfig, loadOrCreateUnifiedConfig } =
await import('../../config/config-loader-facade');
const config = loadOrCreateUnifiedConfig();
let fixed = false;