Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
14 kB · 340 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341import { profileCheckpoint } from '../utils/startupProfiler.js'import '../bootstrap/state.js'import '../utils/config.js'import type { Attributes, MetricOptions } from '@opentelemetry/api'import memoize from 'lodash-es/memoize.js'import { getIsNonInteractiveSession } from 'src/bootstrap/state.js'import type { AttributedCounter } from '../bootstrap/state.js'import { getSessionCounter, setMeter } from '../bootstrap/state.js'import { shutdownLspServerManager } from '../services/lsp/manager.js'import { populateOAuthAccountInfoIfNeeded } from '../services/oauth/client.js'import { initializePolicyLimitsLoadingPromise, isPolicyLimitsEligible,} from '../services/policyLimits/index.js'import { initializeRemoteManagedSettingsLoadingPromise, isEligibleForRemoteManagedSettings, waitForRemoteManagedSettingsToLoad,} from '../services/remoteManagedSettings/index.js'import { preconnectAnthropicApi } from '../utils/apiPreconnect.js'import { applyExtraCACertsFromConfig } from '../utils/caCertsConfig.js'import { registerCleanup } from '../utils/cleanupRegistry.js'import { enableConfigs, recordFirstStartTime } from '../utils/config.js'import { logForDebugging } from '../utils/debug.js'import { detectCurrentRepository } from '../utils/detectRepository.js'import { logForDiagnosticsNoPII } from '../utils/diagLogs.js'import { initJetBrainsDetection } from '../utils/envDynamic.js'import { isEnvTruthy } from '../utils/envUtils.js'import { ConfigParseError, errorMessage } from '../utils/errors.js'// showInvalidConfigDialog is dynamically imported in the error path to avoid loading React at initimport { gracefulShutdownSync, setupGracefulShutdown,} from '../utils/gracefulShutdown.js'import { applyConfigEnvironmentVariables, applySafeConfigEnvironmentVariables,} from '../utils/managedEnv.js'import { configureGlobalMTLS } from '../utils/mtls.js'import { ensureScratchpadDir, isScratchpadEnabled,} from '../utils/permissions/filesystem.js'// initializeTelemetry is loaded lazily via import() in setMeterState() to defer// ~400KB of OpenTelemetry + protobuf modules until telemetry is actually initialized.// gRPC exporters (~700KB via @grpc/grpc-js) are further lazy-loaded within instrumentation.ts.import { configureGlobalAgents } from '../utils/proxy.js'import { isBetaTracingEnabled } from '../utils/telemetry/betaSessionTracing.js'import { getTelemetryAttributes } from '../utils/telemetryAttributes.js'import { setShellIfWindows } from '../utils/windowsPaths.js'
// initialize1PEventLogging is dynamically imported to defer OpenTelemetry sdk-logs/resources
// Track if telemetry has been initialized to prevent double initializationlet telemetryInitialized = false
export const init = memoize(async (): Promise<void> => { const initStartTime = Date.now() logForDiagnosticsNoPII('info', 'init_started') profileCheckpoint('init_function_start')
// Validate configs are valid and enable configuration system try { const configsStart = Date.now() enableConfigs() logForDiagnosticsNoPII('info', 'init_configs_enabled', { duration_ms: Date.now() - configsStart, }) profileCheckpoint('init_configs_enabled')
// Apply only safe environment variables before trust dialog // Full environment variables are applied after trust is established const envVarsStart = Date.now() applySafeConfigEnvironmentVariables()
// Apply NODE_EXTRA_CA_CERTS from settings.json to process.env early, // before any TLS connections. Bun caches the TLS cert store at boot // via BoringSSL, so this must happen before the first TLS handshake. applyExtraCACertsFromConfig()
logForDiagnosticsNoPII('info', 'init_safe_env_vars_applied', { duration_ms: Date.now() - envVarsStart, }) profileCheckpoint('init_safe_env_vars_applied')
// Make sure things get flushed on exit setupGracefulShutdown() profileCheckpoint('init_after_graceful_shutdown')
// Initialize 1P event logging (no security concerns, but deferred to avoid // loading OpenTelemetry sdk-logs at startup). growthbook.js is already in // the module cache by this point (firstPartyEventLogger imports it), so the // second dynamic import adds no load cost. void Promise.all([ import('../services/analytics/firstPartyEventLogger.js'), import('../services/analytics/growthbook.js'), ]).then(([fp, gb]) => { fp.initialize1PEventLogging() // Rebuild the logger provider if tengu_1p_event_batch_config changes // mid-session. Change detection (isEqual) is inside the handler so // unchanged refreshes are no-ops. gb.onGrowthBookRefresh(() => { void fp.reinitialize1PEventLoggingIfConfigChanged() }) }) profileCheckpoint('init_after_1p_event_logging')
// Populate OAuth account info if it is not already cached in config. This is needed since the // OAuth account info may not be populated when logging in through the VSCode extension. void populateOAuthAccountInfoIfNeeded() profileCheckpoint('init_after_oauth_populate')
// Initialize JetBrains IDE detection asynchronously (populates cache for later sync access) void initJetBrainsDetection() profileCheckpoint('init_after_jetbrains_detection')
// Detect GitHub repository asynchronously (populates cache for gitDiff PR linking) void detectCurrentRepository()
// Initialize the loading promise early so that other systems (like plugin hooks) // can await remote settings loading. The promise includes a timeout to prevent // deadlocks if loadRemoteManagedSettings() is never called (e.g., Agent SDK tests). if (isEligibleForRemoteManagedSettings()) { initializeRemoteManagedSettingsLoadingPromise() } if (isPolicyLimitsEligible()) { initializePolicyLimitsLoadingPromise() } profileCheckpoint('init_after_remote_settings_check')
// Record the first start time recordFirstStartTime()
// Configure global mTLS settings const mtlsStart = Date.now() logForDebugging('[init] configureGlobalMTLS starting') configureGlobalMTLS() logForDiagnosticsNoPII('info', 'init_mtls_configured', { duration_ms: Date.now() - mtlsStart, }) logForDebugging('[init] configureGlobalMTLS complete')
// Configure global HTTP agents (proxy and/or mTLS) const proxyStart = Date.now() logForDebugging('[init] configureGlobalAgents starting') configureGlobalAgents() logForDiagnosticsNoPII('info', 'init_proxy_configured', { duration_ms: Date.now() - proxyStart, }) logForDebugging('[init] configureGlobalAgents complete') profileCheckpoint('init_network_configured')
// Preconnect to the Anthropic API — overlap TCP+TLS handshake // (~100-200ms) with the ~100ms of action-handler work before the API // request. After CA certs + proxy agents are configured so the warmed // connection uses the right transport. Fire-and-forget; skipped for // proxy/mTLS/unix/cloud-provider where the SDK's dispatcher wouldn't // reuse the global pool. preconnectAnthropicApi()
// CCR upstreamproxy: start the local CONNECT relay so agent subprocesses // can reach org-configured upstreams with credential injection. Gated on // CLAUDE_CODE_REMOTE + GrowthBook; fail-open on any error. Lazy import so // non-CCR startups don't pay the module load. The getUpstreamProxyEnv // function is registered with subprocessEnv.ts so subprocess spawning can // inject proxy vars without a static import of the upstreamproxy module. if (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE)) { try { const { initUpstreamProxy, getUpstreamProxyEnv } = await import( '../upstreamproxy/upstreamproxy.js' ) const { registerUpstreamProxyEnvFn } = await import( '../utils/subprocessEnv.js' ) registerUpstreamProxyEnvFn(getUpstreamProxyEnv) await initUpstreamProxy() } catch (err) { logForDebugging( `[init] upstreamproxy init failed: ${err instanceof Error ? err.message : String(err)}; continuing without proxy`, { level: 'warn' }, ) } }
// Set up git-bash if relevant setShellIfWindows()
// Register LSP manager cleanup (initialization happens in main.tsx after --plugin-dir is processed) registerCleanup(shutdownLspServerManager)
// gh-32730: teams created by subagents (or main agent without // explicit TeamDelete) were left on disk forever. Register cleanup // for all teams created this session. Lazy import: swarm code is // behind feature gate and most sessions never create teams. registerCleanup(async () => { const { cleanupSessionTeams } = await import( '../utils/swarm/teamHelpers.js' ) await cleanupSessionTeams() })
// Initialize scratchpad directory if enabled if (isScratchpadEnabled()) { const scratchpadStart = Date.now() await ensureScratchpadDir() logForDiagnosticsNoPII('info', 'init_scratchpad_created', { duration_ms: Date.now() - scratchpadStart, }) }
logForDiagnosticsNoPII('info', 'init_completed', { duration_ms: Date.now() - initStartTime, }) profileCheckpoint('init_function_end') } catch (error) { if (error instanceof ConfigParseError) { // Skip the interactive Ink dialog when we can't safely render it. // The dialog breaks JSON consumers (e.g. desktop marketplace plugin // manager running `plugin marketplace list --json` in a VM sandbox). if (getIsNonInteractiveSession()) { process.stderr.write( `Configuration error in ${error.filePath}: ${error.message}\n`, ) gracefulShutdownSync(1) return }
// Show the invalid config dialog with the error object and wait for it to complete return import('../components/InvalidConfigDialog.js').then(m => m.showInvalidConfigDialog({ error }), ) // Dialog itself handles process.exit, so we don't need additional cleanup here } else { // For non-config errors, rethrow them throw error } }})
/** * Initialize telemetry after trust has been granted. * For remote-settings-eligible users, waits for settings to load (non-blocking), * then re-applies env vars (to include remote settings) before initializing telemetry. * For non-eligible users, initializes telemetry immediately. * This should only be called once, after the trust dialog has been accepted. */export function initializeTelemetryAfterTrust(): void { if (isEligibleForRemoteManagedSettings()) { // For SDK/headless mode with beta tracing, initialize eagerly first // to ensure the tracer is ready before the first query runs. // The async path below will still run but doInitializeTelemetry() guards against double init. if (getIsNonInteractiveSession() && isBetaTracingEnabled()) { void doInitializeTelemetry().catch(error => { logForDebugging( `[3P telemetry] Eager telemetry init failed (beta tracing): ${errorMessage(error)}`, { level: 'error' }, ) }) } logForDebugging( '[3P telemetry] Waiting for remote managed settings before telemetry init', ) void waitForRemoteManagedSettingsToLoad() .then(async () => { logForDebugging( '[3P telemetry] Remote managed settings loaded, initializing telemetry', ) // Re-apply env vars to pick up remote settings before initializing telemetry. applyConfigEnvironmentVariables() await doInitializeTelemetry() }) .catch(error => { logForDebugging( `[3P telemetry] Telemetry init failed (remote settings path): ${errorMessage(error)}`, { level: 'error' }, ) }) } else { void doInitializeTelemetry().catch(error => { logForDebugging( `[3P telemetry] Telemetry init failed: ${errorMessage(error)}`, { level: 'error' }, ) }) }}
async function doInitializeTelemetry(): Promise<void> { if (telemetryInitialized) { // Already initialized, nothing to do return }
// Set flag before init to prevent double initialization telemetryInitialized = true try { await setMeterState() } catch (error) { // Reset flag on failure so subsequent calls can retry telemetryInitialized = false throw error }}
async function setMeterState(): Promise<void> { // Lazy-load instrumentation to defer ~400KB of OpenTelemetry + protobuf const { initializeTelemetry } = await import( '../utils/telemetry/instrumentation.js' ) // Initialize customer OTLP telemetry (metrics, logs, traces) const meter = await initializeTelemetry() if (meter) { // Create factory function for attributed counters const createAttributedCounter = ( name: string, options: MetricOptions, ): AttributedCounter => { const counter = meter?.createCounter(name, options)
return { add(value: number, additionalAttributes: Attributes = {}) { // Always fetch fresh telemetry attributes to ensure they're up to date const currentAttributes = getTelemetryAttributes() const mergedAttributes = { ...currentAttributes, ...additionalAttributes, } counter?.add(value, mergedAttributes) }, } }
setMeter(meter, createAttributedCounter)
// Increment session counter here because the startup telemetry path // runs before this async initialization completes, so the counter // would be null there. getSessionCounter()?.add(1) }}