mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-05 08:22:29 +00:00
fix(cliproxy): infer emails from alternate token prefixes
This commit is contained in:
@@ -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 () => {
|
it('preserves the corrupted registry backup when recovery cannot rewrite accounts.json', async () => {
|
||||||
await withIsolatedHome(async (homeDir) => {
|
await withIsolatedHome(async (homeDir) => {
|
||||||
const authDir = path.join(homeDir, '.ccs', 'cliproxy', 'auth');
|
const authDir = path.join(homeDir, '.ccs', 'cliproxy', 'auth');
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as lockfile from 'proper-lockfile';
|
import * as lockfile from 'proper-lockfile';
|
||||||
import { CLIProxyProvider } from '../types';
|
import { CLIProxyProvider } from '../types';
|
||||||
|
import { PROVIDER_CAPABILITIES } from '../provider-capabilities';
|
||||||
import { PROVIDER_TYPE_VALUES } from '../auth/auth-types';
|
import { PROVIDER_TYPE_VALUES } from '../auth/auth-types';
|
||||||
import { getAuthDir, getCliproxyDir } from '../config/config-generator';
|
import { getAuthDir, getCliproxyDir } from '../config/config-generator';
|
||||||
import { AccountsRegistry, AccountInfo, PROVIDERS_WITHOUT_EMAIL } from './types';
|
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;
|
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(
|
function inferEmailFromTokenFileName(
|
||||||
tokenFile: string,
|
tokenFile: string,
|
||||||
provider: CLIProxyProvider
|
provider: CLIProxyProvider
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const baseName = tokenFile.replace(/\.json$/i, '');
|
const baseName = tokenFile.replace(/\.json$/i, '');
|
||||||
const providerPrefix = `${provider}-`;
|
const candidate = stripTokenFileProviderPrefix(baseName, provider);
|
||||||
const candidate = baseName.startsWith(providerPrefix)
|
|
||||||
? baseName.slice(providerPrefix.length)
|
|
||||||
: baseName;
|
|
||||||
|
|
||||||
if (PROVIDERS_WITHOUT_EMAIL.includes(provider)) {
|
if (PROVIDERS_WITHOUT_EMAIL.includes(provider)) {
|
||||||
const scopedCandidate = candidate.slice(candidate.indexOf('-') + 1);
|
const scopedCandidate = candidate.slice(candidate.indexOf('-') + 1);
|
||||||
|
|||||||
@@ -483,9 +483,8 @@ export async function reconcileExhaustedRotationAccounts(
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await import(
|
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } =
|
||||||
'../accounts/account-safety'
|
await import('../accounts/account-safety');
|
||||||
);
|
|
||||||
restoreExpiredQuotaPauses();
|
restoreExpiredQuotaPauses();
|
||||||
|
|
||||||
const config = loadOrCreateUnifiedConfig();
|
const config = loadOrCreateUnifiedConfig();
|
||||||
@@ -593,9 +592,8 @@ export async function preflightCheck(provider: CLIProxyProvider): Promise<Prefli
|
|||||||
return { proceed: true, accountId: defaultAccount?.id || '' };
|
return { proceed: true, accountId: defaultAccount?.id || '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } = await import(
|
const { pauseAccountForQuotaCooldown, restoreExpiredQuotaPauses } =
|
||||||
'../accounts/account-safety'
|
await import('../accounts/account-safety');
|
||||||
);
|
|
||||||
restoreExpiredQuotaPauses();
|
restoreExpiredQuotaPauses();
|
||||||
|
|
||||||
const config = loadOrCreateUnifiedConfig();
|
const config = loadOrCreateUnifiedConfig();
|
||||||
|
|||||||
@@ -112,9 +112,8 @@ export async function runImageAnalysisCheck(results: HealthCheck): Promise<void>
|
|||||||
* Fix image analysis configuration issues
|
* Fix image analysis configuration issues
|
||||||
*/
|
*/
|
||||||
export async function fixImageAnalysisConfig(): Promise<boolean> {
|
export async function fixImageAnalysisConfig(): Promise<boolean> {
|
||||||
const { updateConfig, loadOrCreateUnifiedConfig } = await import(
|
const { updateConfig, loadOrCreateUnifiedConfig } =
|
||||||
'../../config/config-loader-facade'
|
await import('../../config/config-loader-facade');
|
||||||
);
|
|
||||||
|
|
||||||
const config = loadOrCreateUnifiedConfig();
|
const config = loadOrCreateUnifiedConfig();
|
||||||
let fixed = false;
|
let fixed = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user