()
| 40 | type BootstrapResponse = z.infer<ReturnType<typeof bootstrapResponseSchema>> |
| 41 | |
| 42 | async function fetchBootstrapAPI(): Promise<BootstrapResponse | null> { |
| 43 | if (isEssentialTrafficOnly()) { |
| 44 | logForDebugging('[Bootstrap] Skipped: Nonessential traffic disabled') |
| 45 | return null |
| 46 | } |
| 47 | |
| 48 | if (getAPIProvider() !== 'firstParty') { |
| 49 | logForDebugging('[Bootstrap] Skipped: 3P provider') |
| 50 | return null |
| 51 | } |
| 52 | |
| 53 | // OAuth preferred (requires user:profile scope — service-key OAuth tokens |
| 54 | // lack it and would 403). Fall back to API key auth for console users. |
| 55 | const apiKey = getAnthropicApiKey() |
| 56 | const hasUsableOAuth = |
| 57 | getClaudeAIOAuthTokens()?.accessToken && hasProfileScope() |
| 58 | if (!hasUsableOAuth && !apiKey) { |
| 59 | logForDebugging('[Bootstrap] Skipped: no usable OAuth or API key') |
| 60 | return null |
| 61 | } |
| 62 | |
| 63 | const endpoint = `${getOauthConfig().BASE_API_URL}/api/claude_cli/bootstrap` |
| 64 | |
| 65 | // withOAuth401Retry handles the refresh-and-retry. API key users fail |
| 66 | // through on 401 (no refresh mechanism — no OAuth token to pass). |
| 67 | try { |
| 68 | return await withOAuth401Retry(async () => { |
| 69 | // Re-read OAuth each call so the retry picks up the refreshed token. |
| 70 | const token = getClaudeAIOAuthTokens()?.accessToken |
| 71 | let authHeaders: Record<string, string> |
| 72 | if (token && hasProfileScope()) { |
| 73 | authHeaders = { |
| 74 | Authorization: `Bearer ${token}`, |
| 75 | 'anthropic-beta': OAUTH_BETA_HEADER, |
| 76 | } |
| 77 | } else if (apiKey) { |
| 78 | authHeaders = { 'x-api-key': apiKey } |
| 79 | } else { |
| 80 | logForDebugging('[Bootstrap] No auth available on retry, aborting') |
| 81 | return null |
| 82 | } |
| 83 | |
| 84 | logForDebugging('[Bootstrap] Fetching') |
| 85 | const response = await axios.get<unknown>(endpoint, { |
| 86 | headers: { |
| 87 | 'Content-Type': 'application/json', |
| 88 | 'User-Agent': getClaudeCodeUserAgent(), |
| 89 | ...authHeaders, |
| 90 | }, |
| 91 | timeout: 5000, |
| 92 | }) |
| 93 | const parsed = bootstrapResponseSchema().safeParse(response.data) |
| 94 | if (!parsed.success) { |
| 95 | logForDebugging( |
| 96 | `[Bootstrap] Response failed validation: ${parsed.error.message}`, |
| 97 | ) |
| 98 | return null |
| 99 | } |
no test coverage detected