(config: Config, key: string)
| 132 | } |
| 133 | |
| 134 | async function loginWithApiKey(config: Config, key: string): Promise<void> { |
| 135 | if (config.dryRun) { |
| 136 | console.log('Would validate and save API key.'); |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | // Probe both regions and pick the one the key actually authenticates against. |
| 141 | // This doubles as key validation — if neither region accepts it, the key is bad. |
| 142 | const detected = await detectRegion(key); |
| 143 | const cfg: Config = { ...config, region: detected, baseUrl: REGIONS[detected], apiKey: key }; |
| 144 | |
| 145 | // Verify the detection actually authorizes the quota endpoint (defends against |
| 146 | // detectRegion's graceful 'global' fallback when the network is unreachable). |
| 147 | try { |
| 148 | await requestJson<QuotaApiResponse>(cfg, { url: quotaEndpoint(cfg.baseUrl) }); |
| 149 | } catch { |
| 150 | throw new CLIError( |
| 151 | 'API key validation failed.', |
| 152 | ExitCode.AUTH, |
| 153 | 'Check that your key is valid and belongs to a Token Plan.', |
| 154 | ); |
| 155 | } |
| 156 | |
| 157 | // OAuth and api_key are mutually exclusive — drop any stale oauth block. |
| 158 | const existing = readConfigFile() as Record<string, unknown>; |
| 159 | delete existing.oauth; |
| 160 | existing.api_key = key; |
| 161 | existing.region = detected; |
| 162 | await writeConfigFile(existing); |
| 163 | process.stderr.write(`API key saved to ${getConfigPath()}\n`); |
| 164 | |
| 165 | await showDashboardAfterLogin(cfg); |
| 166 | } |
no test coverage detected