File size: 8,632 Bytes
064bfd6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | import { profileCheckpoint } from '../utils/startupProfiler.js'
import '../bootstrap/state.js'
import '../utils/config.js'
import memoize from 'lodash-es/memoize.js'
import { getIsNonInteractiveSession } from 'src/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,
} 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 } from '../utils/errors.js'
// showInvalidConfigDialog is dynamically imported in the error path to avoid loading React at init
import {
gracefulShutdownSync,
setupGracefulShutdown,
} from '../utils/gracefulShutdown.js'
import {
applySafeConfigEnvironmentVariables,
} from '../utils/managedEnv.js'
import { configureGlobalMTLS } from '../utils/mtls.js'
import {
ensureScratchpadDir,
isScratchpadEnabled,
} from '../utils/permissions/filesystem.js'
import { configureGlobalAgents } from '../utils/proxy.js'
import { setShellIfWindows } from '../utils/windowsPaths.js'
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')
// 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
}
}
})
/**
* The OSS build does not initialize product telemetry after trust.
*/
export function initializeTelemetryAfterTrust(): void {
return
}
|