π File detail
services/api/bootstrap.ts
π§© .tsπ 142 linesπΎ 4,634 bytesπ text
β Back to All Filesπ― Use case
This file lives under βservices/β, which covers long-lived services (LSP, MCP, OAuth, tool execution, memory, compaction, voice, settings sync, β¦). On the API surface it exposes fetchBootstrapData β mainly functions, hooks, or classes. Dependencies touch HTTP client, lodash-es, src, and schema validation. It composes internal code from constants and utils (relative imports).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import axios from 'axios' import isEqual from 'lodash-es/isEqual.js' import { getAnthropicApiKey, getClaudeAIOAuthTokens,
π€ Exports (heuristic)
fetchBootstrapData
π External import roots
Package roots from from "β¦" (relative paths omitted).
axioslodash-essrczod
π₯οΈ Source preview
import axios from 'axios'
import isEqual from 'lodash-es/isEqual.js'
import {
getAnthropicApiKey,
getClaudeAIOAuthTokens,
hasProfileScope,
} from 'src/utils/auth.js'
import { z } from 'zod'
import { getOauthConfig, OAUTH_BETA_HEADER } from '../../constants/oauth.js'
import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'
import { logForDebugging } from '../../utils/debug.js'
import { withOAuth401Retry } from '../../utils/http.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { logError } from '../../utils/log.js'
import { getAPIProvider } from '../../utils/model/providers.js'
import { isEssentialTrafficOnly } from '../../utils/privacyLevel.js'
import { getClaudeCodeUserAgent } from '../../utils/userAgent.js'
const bootstrapResponseSchema = lazySchema(() =>
z.object({
client_data: z.record(z.unknown()).nullish(),
additional_model_options: z
.array(
z
.object({
model: z.string(),
name: z.string(),
description: z.string(),
})
.transform(({ model, name, description }) => ({
value: model,
label: name,
description,
})),
)
.nullish(),
}),
)
type BootstrapResponse = z.infer<ReturnType<typeof bootstrapResponseSchema>>
async function fetchBootstrapAPI(): Promise<BootstrapResponse | null> {
if (isEssentialTrafficOnly()) {
logForDebugging('[Bootstrap] Skipped: Nonessential traffic disabled')
return null
}
if (getAPIProvider() !== 'firstParty') {
logForDebugging('[Bootstrap] Skipped: 3P provider')
return null
}
// OAuth preferred (requires user:profile scope β service-key OAuth tokens
// lack it and would 403). Fall back to API key auth for console users.
const apiKey = getAnthropicApiKey()
const hasUsableOAuth =
getClaudeAIOAuthTokens()?.accessToken && hasProfileScope()
if (!hasUsableOAuth && !apiKey) {
logForDebugging('[Bootstrap] Skipped: no usable OAuth or API key')
return null
}
const endpoint = `${getOauthConfig().BASE_API_URL}/api/claude_cli/bootstrap`
// withOAuth401Retry handles the refresh-and-retry. API key users fail
// through on 401 (no refresh mechanism β no OAuth token to pass).
try {
return await withOAuth401Retry(async () => {
// Re-read OAuth each call so the retry picks up the refreshed token.
const token = getClaudeAIOAuthTokens()?.accessToken
let authHeaders: Record<string, string>
if (token && hasProfileScope()) {
authHeaders = {
Authorization: `Bearer ${token}`,
'anthropic-beta': OAUTH_BETA_HEADER,
}
} else if (apiKey) {
authHeaders = { 'x-api-key': apiKey }
} else {
logForDebugging('[Bootstrap] No auth available on retry, aborting')
return null
}
logForDebugging('[Bootstrap] Fetching')
const response = await axios.get<unknown>(endpoint, {
headers: {
'Content-Type': 'application/json',
'User-Agent': getClaudeCodeUserAgent(),
...authHeaders,
},
timeout: 5000,
})
const parsed = bootstrapResponseSchema().safeParse(response.data)
if (!parsed.success) {
logForDebugging(
`[Bootstrap] Response failed validation: ${parsed.error.message}`,
)
return null
}
logForDebugging('[Bootstrap] Fetch ok')
return parsed.data
})
} catch (error) {
logForDebugging(
`[Bootstrap] Fetch failed: ${axios.isAxiosError(error) ? (error.response?.status ?? error.code) : 'unknown'}`,
)
throw error
}
}
/**
* Fetch bootstrap data from the API and persist to disk cache.
*/
export async function fetchBootstrapData(): Promise<void> {
try {
const response = await fetchBootstrapAPI()
if (!response) return
const clientData = response.client_data ?? null
const additionalModelOptions = response.additional_model_options ?? []
// Only persist if data actually changed β avoids a config write on every startup.
const config = getGlobalConfig()
if (
isEqual(config.clientDataCache, clientData) &&
isEqual(config.additionalModelOptionsCache, additionalModelOptions)
) {
logForDebugging('[Bootstrap] Cache unchanged, skipping write')
return
}
logForDebugging('[Bootstrap] Cache updated, persisting to disk')
saveGlobalConfig(current => ({
...current,
clientDataCache: clientData,
additionalModelOptionsCache: additionalModelOptions,
}))
} catch (error) {
logError(error)
}
}