πŸ“„ File detail

utils/apiPreconnect.ts

🧩 .tsπŸ“ 72 linesπŸ’Ύ 2,843 bytesπŸ“ text
← Back to All Files

🎯 Use case

This file lives under β€œutils/”, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, …). On the API surface it exposes preconnectAnthropicApi β€” mainly functions, hooks, or classes. It composes internal code from constants and envUtils (relative imports). What the file header says: Preconnect to the Anthropic API to overlap TCP+TLS handshake with startup. The TCP+TLS handshake is ~100-200ms that normally blocks inside the first API call. Kicking a fire-and-forget fetch during init lets the handshake happen in parallel with action-handler work (~100ms of set.

Generated from folder role, exports, dependency roots, and inline comments β€” not hand-reviewed for every path.

🧠 Inline summary

Preconnect to the Anthropic API to overlap TCP+TLS handshake with startup. The TCP+TLS handshake is ~100-200ms that normally blocks inside the first API call. Kicking a fire-and-forget fetch during init lets the handshake happen in parallel with action-handler work (~100ms of setup/commands/mcp before the API request in -p mode; unbounded "user is typing" window in interactive mode). Bun's fetch shares a keep-alive connection pool globally, so the real API request reuses the warmed connection. Called from init.ts AFTER applyExtraCACertsFromConfig() + configureGlobalAgents() so settings.json env vars are applied and the TLS cert store is finalized. The early cli.tsx call site was removed β€” it ran before settings.json loaded, so ANTHROPIC_BASE_URL/proxy/mTLS in settings would be invisible and preconnect would warm the wrong pool (or worse, lock BoringSSL's cert store before NODE_EXTRA_CA_CERTS was applied). Skipped when: - proxy/mTLS/unix socket configured (preconnect would use wrong transport β€” the SDK passes a custom dispatcher/agent that doesn't share the global pool) - Bedrock/Vertex/Foundry (different endpoints, different auth)

πŸ“€ Exports (heuristic)

  • preconnectAnthropicApi

πŸ–₯️ Source preview

/**
 * Preconnect to the Anthropic API to overlap TCP+TLS handshake with startup.
 *
 * The TCP+TLS handshake is ~100-200ms that normally blocks inside the first
 * API call. Kicking a fire-and-forget fetch during init lets the handshake
 * happen in parallel with action-handler work (~100ms of setup/commands/mcp
 * before the API request in -p mode; unbounded "user is typing" window in
 * interactive mode).
 *
 * Bun's fetch shares a keep-alive connection pool globally, so the real API
 * request reuses the warmed connection.
 *
 * Called from init.ts AFTER applyExtraCACertsFromConfig() + configureGlobalAgents()
 * so settings.json env vars are applied and the TLS cert store is finalized.
 * The early cli.tsx call site was removed β€” it ran before settings.json loaded,
 * so ANTHROPIC_BASE_URL/proxy/mTLS in settings would be invisible and preconnect
 * would warm the wrong pool (or worse, lock BoringSSL's cert store before
 * NODE_EXTRA_CA_CERTS was applied).
 *
 * Skipped when:
 * - proxy/mTLS/unix socket configured (preconnect would use wrong transport β€”
 *   the SDK passes a custom dispatcher/agent that doesn't share the global pool)
 * - Bedrock/Vertex/Foundry (different endpoints, different auth)
 */

import { getOauthConfig } from '../constants/oauth.js'
import { isEnvTruthy } from './envUtils.js'

let fired = false

export function preconnectAnthropicApi(): void {
  if (fired) return
  fired = true

  // Skip if using a cloud provider β€” different endpoint + auth
  if (
    isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
    isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
    isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)
  ) {
    return
  }
  // Skip if proxy/mTLS/unix β€” SDK's custom dispatcher won't reuse this pool
  if (
    process.env.HTTPS_PROXY ||
    process.env.https_proxy ||
    process.env.HTTP_PROXY ||
    process.env.http_proxy ||
    process.env.ANTHROPIC_UNIX_SOCKET ||
    process.env.CLAUDE_CODE_CLIENT_CERT ||
    process.env.CLAUDE_CODE_CLIENT_KEY
  ) {
    return
  }

  // Use configured base URL (staging, local, or custom gateway). Covers
  // ANTHROPIC_BASE_URL env + USE_STAGING_OAUTH + USE_LOCAL_OAUTH in one lookup.
  // NODE_EXTRA_CA_CERTS no longer a skip β€” init.ts applied it before this fires.
  const baseUrl =
    process.env.ANTHROPIC_BASE_URL || getOauthConfig().BASE_API_URL

  // Fire and forget. HEAD means no response body β€” the connection is eligible
  // for keep-alive pool reuse immediately after headers arrive. 10s timeout
  // so a slow network doesn't hang the process; abort is fine since the real
  // request will handshake fresh if needed.
  // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins
  void fetch(baseUrl, {
    method: 'HEAD',
    signal: AbortSignal.timeout(10_000),
  }).catch(() => {})
}