diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index 7d1fecb..9e338ff 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -9,6 +9,7 @@ import { renderQuotaTable } from '../../output/quota-table'; import { getConfigPath } from '../../config/paths'; import { readConfigFile, writeConfigFile } from '../../config/loader'; +import { REGIONS, type Region } from '../../config/schema'; import { isInteractive } from '../../utils/env'; import { maskToken } from '../../utils/token'; import type { Config } from '../../config/schema'; @@ -76,26 +77,46 @@ export default defineCommand({ ); } - // Validate the key by calling quota endpoint + // Validate key by probing all regions in parallel. + // A CN key fails against the global endpoint (and vice versa), so we must + // try every region to avoid false "validation failed" errors. if (!config.dryRun) { process.stderr.write('Testing key... '); - try { - const testConfig = { ...config, apiKey: key }; - await requestJson(testConfig, { - url: quotaEndpoint(testConfig.baseUrl), - }); - process.stderr.write('Valid\n'); - } catch { + + const regions = Object.keys(REGIONS) as Region[]; + const results = await Promise.all( + regions.map(async (region) => { + const baseUrl = REGIONS[region]; + try { + const testConfig = { ...config, apiKey: key, baseUrl }; + await requestJson(testConfig, { + url: quotaEndpoint(baseUrl), + }); + return { region, ok: true as const, error: '' }; + } catch (err) { + return { region, ok: false as const, error: err instanceof Error ? err.message : String(err) }; + } + }), + ); + + const match = results.find((r) => r.ok); + if (!match) { + const details = results.map((r) => `${r.region}: ${r.error}`).join('; '); throw new CLIError( - 'API key validation failed.', + `API key validation failed: ${details}`, ExitCode.AUTH, - 'Check that your key is valid and belongs to a Token Plan.', + 'Run with --verbose for request details.', ); } - // Store key in config.json + process.stderr.write(`Valid (${match.region})\n`); + + config.region = match.region; + config.baseUrl = REGIONS[match.region]; + const existing = readConfigFile() as Record; existing.api_key = key; + existing.region = match.region; await writeConfigFile(existing); process.stderr.write(`API key saved to ${getConfigPath()}\n`); diff --git a/src/main.ts b/src/main.ts index 0ad712f..c07bfa2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -95,13 +95,17 @@ async function main() { } if (config.needsRegionDetection) { - const apiKey = config.apiKey || config.fileApiKey; - if (apiKey) { - const detected = await detectRegion(apiKey); - config.region = detected; - config.baseUrl = REGIONS[detected]; - config.needsRegionDetection = false; - await saveDetectedRegion(detected); + // auth login handles its own region detection during key validation + const isAuthLogin = commandPath[0] === 'auth' && commandPath[1] === 'login'; + if (!isAuthLogin) { + const apiKey = config.apiKey || config.fileApiKey; + if (apiKey) { + const detected = await detectRegion(apiKey); + config.region = detected; + config.baseUrl = REGIONS[detected]; + config.needsRegionDetection = false; + await saveDetectedRegion(detected); + } } }