Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
4.9 kB · 136 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137/** * HTTP utility constants and helpers */
import axios from 'axios'import { OAUTH_BETA_HEADER } from '../constants/oauth.js'import { getAnthropicApiKey, getClaudeAIOAuthTokens, handleOAuth401Error, isClaudeAISubscriber,} from './auth.js'import { getClaudeCodeUserAgent } from './userAgent.js'import { getWorkload } from './workloadContext.js'
// WARNING: We rely on `claude-cli` in the user agent for log filtering.// Please do NOT change this without making sure that logging also gets updated!export function getUserAgent(): string { const agentSdkVersion = process.env.CLAUDE_AGENT_SDK_VERSION ? `, agent-sdk/${process.env.CLAUDE_AGENT_SDK_VERSION}` : '' // SDK consumers can identify their app/library via CLAUDE_AGENT_SDK_CLIENT_APP // e.g., "my-app/1.0.0" or "my-library/2.1" const clientApp = process.env.CLAUDE_AGENT_SDK_CLIENT_APP ? `, client-app/${process.env.CLAUDE_AGENT_SDK_CLIENT_APP}` : '' // Turn-/process-scoped workload tag for cron-initiated requests. 1P-only // observability — proxies strip HTTP headers; QoS routing uses cc_workload // in the billing-header attribution block instead (see constants/system.ts). // getAnthropicClient (client.ts:98) calls this per-request inside withRetry, // so the read picks up the same setWorkload() value as getAttributionHeader. const workload = getWorkload() const workloadSuffix = workload ? `, workload/${workload}` : '' return `claude-cli/${MACRO.VERSION} (${process.env.USER_TYPE}, ${process.env.CLAUDE_CODE_ENTRYPOINT ?? 'cli'}${agentSdkVersion}${clientApp}${workloadSuffix})`}
export function getMCPUserAgent(): string { const parts: string[] = [] if (process.env.CLAUDE_CODE_ENTRYPOINT) { parts.push(process.env.CLAUDE_CODE_ENTRYPOINT) } if (process.env.CLAUDE_AGENT_SDK_VERSION) { parts.push(`agent-sdk/${process.env.CLAUDE_AGENT_SDK_VERSION}`) } if (process.env.CLAUDE_AGENT_SDK_CLIENT_APP) { parts.push(`client-app/${process.env.CLAUDE_AGENT_SDK_CLIENT_APP}`) } const suffix = parts.length > 0 ? ` (${parts.join(', ')})` : '' return `claude-code/${MACRO.VERSION}${suffix}`}
// User-Agent for WebFetch requests to arbitrary sites. `Claude-User` is// Anthropic's publicly documented agent for user-initiated fetches (what site// operators match in robots.txt); the claude-code suffix lets them distinguish// local CLI traffic from claude.ai server-side fetches.export function getWebFetchUserAgent(): string { return `Claude-User (${getClaudeCodeUserAgent()}; +https://support.anthropic.com/)`}
export type AuthHeaders = { headers: Record<string, string> error?: string}
/** * Get authentication headers for API requests * Returns either OAuth headers for Max/Pro users or API key headers for regular users */export function getAuthHeaders(): AuthHeaders { if (isClaudeAISubscriber()) { const oauthTokens = getClaudeAIOAuthTokens() if (!oauthTokens?.accessToken) { return { headers: {}, error: 'No OAuth token available', } } return { headers: { Authorization: `Bearer ${oauthTokens.accessToken}`, 'anthropic-beta': OAUTH_BETA_HEADER, }, } } // TODO: this will fail if the API key is being set to an LLM Gateway key // should we try to query keychain / credentials for a valid Anthropic key? const apiKey = getAnthropicApiKey() if (!apiKey) { return { headers: {}, error: 'No API key available', } } return { headers: { 'x-api-key': apiKey, }, }}
/** * Wrapper that handles OAuth 401 errors by force-refreshing the token and * retrying once. Addresses clock drift scenarios where the local expiration * check disagrees with the server. * * The request closure is called again on retry, so it should re-read auth * (e.g., via getAuthHeaders()) to pick up the refreshed token. * * Note: bridgeApi.ts has its own DI-injected version — handleOAuth401Error * transitively pulls in config.ts (~1300 modules), which breaks the SDK bundle. * * @param opts.also403Revoked - Also retry on 403 with "OAuth token has been * revoked" body (some endpoints signal revocation this way instead of 401). */export async function withOAuth401Retry<T>( request: () => Promise<T>, opts?: { also403Revoked?: boolean },): Promise<T> { try { return await request() } catch (err) { if (!axios.isAxiosError(err)) throw err const status = err.response?.status const isAuthError = status === 401 || (opts?.also403Revoked && status === 403 && typeof err.response?.data === 'string' && err.response.data.includes('OAuth token has been revoked')) if (!isAuthError) throw err const failedAccessToken = getClaudeAIOAuthTokens()?.accessToken if (!failedAccessToken) throw err await handleOAuth401Error(failedAccessToken) return await request() }}