()
| 39 | > |
| 40 | |
| 41 | async function fetchBootstrapAPI(): Promise<BootstrapResponse | null> { |
| 42 | if (isEssentialTrafficOnly()) { |
| 43 | logForDebugging('[Bootstrap] Skipped: Nonessential traffic disabled') |
| 44 | return null |
| 45 | } |
| 46 | |
| 47 | if (getAPIProvider() !== 'firstParty') { |
| 48 | logForDebugging('[Bootstrap] Skipped: 3P provider') |
| 49 | return null |
| 50 | } |
| 51 | |
| 52 | // OAuth preferred (requires user:profile scope — service-key OAuth tokens |
| 53 | // lack it and would 403). Fall back to API key auth for console users. |
| 54 | const initialSession = getAuthRuntime().getCurrentSession() |
| 55 | const hasUsableOAuth = |
| 56 | initialSession.principalSource === 'managed_oauth' && |
| 57 | initialSession.sessionState === 'usable' && |
| 58 | initialSession.scopes.includes('user:profile') && |
| 59 | Boolean(initialSession.accessToken) |
| 60 | if (!hasUsableOAuth && !initialSession.apiKey) { |
| 61 | logForDebugging('[Bootstrap] Skipped: no usable OAuth or API key') |
| 62 | return null |
| 63 | } |
| 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-resolve session each call so the retry picks up refreshed auth. |
| 70 | const session = await getAuthRuntime().resolveSession({ |
| 71 | allowRefresh: true, |
| 72 | }) |
| 73 | const token = |
| 74 | session.principalSource === 'managed_oauth' && |
| 75 | session.sessionState === 'usable' && |
| 76 | session.scopes.includes('user:profile') |
| 77 | ? session.accessToken |
| 78 | : null |
| 79 | let authHeaders: Record<string, string> |
| 80 | if (token) { |
| 81 | authHeaders = { |
| 82 | Authorization: `Bearer ${token}`, |
| 83 | 'anthropic-beta': OAUTH_BETA_HEADER, |
| 84 | } |
| 85 | } else if (session.apiKey) { |
| 86 | authHeaders = { 'x-api-key': session.apiKey } |
| 87 | } else { |
| 88 | logForDebugging('[Bootstrap] No auth available on retry, aborting') |
| 89 | return null |
| 90 | } |
| 91 | |
| 92 | logForDebugging('[Bootstrap] Fetching') |
| 93 | const response = await getIdentityClient().fetchBootstrap({ |
| 94 | headers: { |
| 95 | 'Content-Type': 'application/json', |
| 96 | 'User-Agent': getNcodeUserAgent(), |
| 97 | ...authHeaders, |
| 98 | }, |
no test coverage detected