fix(cliproxy): extract unique accountId from token filename for Kiro/GHCP

When OAuth accounts have empty email fields (Kiro/GHCP), the accountId
was defaulting to 'default', causing multiple accounts to overwrite
each other. Now extracts unique ID from filename pattern:
- kiro-github-<PROFILE_ID>.json → github-<PROFILE_ID>
- ghcp-amazon-<PROFILE_ID>.json → amazon-<PROFILE_ID>

Fixes #258
This commit is contained in:
kaitranntt
2026-01-03 11:51:26 -05:00
parent 9fdf437ace
commit 7bb7ccc27f
+24 -4
View File
@@ -56,6 +56,25 @@ const DEFAULT_REGISTRY: AccountsRegistry = {
providers: {}, providers: {},
}; };
/**
* Extract unique account ID from token filename when email is unavailable
* For Kiro/GHCP OAuth, filenames are: <provider>-<oauth>-<profile_id>.json
* Extracts: <oauth>-<profile_id> as unique identifier
* @example kiro-github-ABC123.json → github-ABC123
* @example ghcp-amazon-XYZ789.json → amazon-XYZ789
* @example kiro-nomail.json → default (no OAuth structure)
*/
export function extractAccountIdFromTokenFile(filename: string, email?: string): string {
if (email) return email;
// Pattern: <provider>-<oauth>-<profile_id>.json → extract <oauth>-<profile_id>
// Requires at least 2 hyphens to distinguish from simple filenames like "kiro-nomail.json"
const match = filename.match(/^[^-]+-([^-]+-[^.]+)\.json$/);
if (match) return match[1];
return 'default';
}
/** /**
* Generate nickname from email * Generate nickname from email
* Takes prefix before @ symbol, sanitizes whitespace * Takes prefix before @ symbol, sanitizes whitespace
@@ -260,8 +279,8 @@ export function registerAccount(
throw new Error('Failed to initialize provider accounts'); throw new Error('Failed to initialize provider accounts');
} }
// Determine account ID // Determine account ID - use email if available, otherwise extract from filename
const accountId = email || 'default'; const accountId = extractAccountIdFromTokenFile(tokenFile, email);
const isFirstAccount = Object.keys(providerAccounts.accounts).length === 0; const isFirstAccount = Object.keys(providerAccounts.accounts).length === 0;
// Generate nickname if not provided // Generate nickname if not provided
@@ -448,7 +467,7 @@ export function discoverExistingAccounts(): void {
continue; continue;
} }
// Extract email if available, fallback to filename // Extract email if available, fallback to filename-based ID
let email = data.email || undefined; let email = data.email || undefined;
// Fallback: extract email from filename (e.g., "kiro-google-user@example.com.json") // Fallback: extract email from filename (e.g., "kiro-google-user@example.com.json")
@@ -459,7 +478,8 @@ export function discoverExistingAccounts(): void {
} }
} }
const accountId = email || 'default'; // Use unified ID extraction: email or filename-based unique ID
const accountId = extractAccountIdFromTokenFile(file, email);
// Initialize provider section if needed // Initialize provider section if needed
if (!registry.providers[provider]) { if (!registry.providers[provider]) {