# Claude Code 桥接系统详解 ## 概述 桥接系统(Bridge)是 Claude Code CLI 与 IDE 扩展(VS Code, JetBrains)之间的双向通信层。它使 IDE 能够: 1. 在远程设备上启动 Claude Code 会话 2. 将用户输入转发到 Claude Code 3. 显示 Claude Code 的输出和工具执行结果 4. 处理权限请求和响应 --- ## 核心架构 ``` ┌──────────────────────────────────────────────────────────────┐ │ IDE Extension (VS Code / JetBrains) │ │ - 用户界面 │ │ - 输入处理 │ │ - 输出展示 │ └──────────────────────────────────────────────────────────────┘ │ │ WebSocket / HTTP ▼ ┌──────────────────────────────────────────────────────────────┐ │ Bridge Layer │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ bridgeMain.ts │ │ │ │ 入口点,管理桥接生命周期 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ bridgeMessaging.ts │ │ │ │ 消息处理、入口路由、流量控制 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ replBridge.ts │ │ │ │ REPL 会话桥接 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ sessionRunner.ts │ │ │ │ 会话执行管理 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ jwtUtils.ts │ │ │ │ JWT 认证工具 │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ bridgePermissionCallbacks.ts │ │ │ │ 权限回调处理 │ │ │ └────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Claude Code CLI (子进程) │ │ - 命令处理 │ │ - 工具执行 │ │ - API 调用 │ └──────────────────────────────────────────────────────────────┘ ``` --- ## 1. bridgeMain.ts - 桥接主入口 **文件位置**: `src/bridge/bridgeMain.ts` ### 核心类型 ```typescript export type BridgeState = 'ready' | 'connected' | 'reconnecting' | 'failed' export type ReplBridgeHandle = { bridgeSessionId: string environmentId: string sessionIngressUrl: string writeMessages(messages: Message[]): void writeSdkMessages(messages: SDKMessage[]): void sendControlRequest(request: SDKControlRequest): void sendControlResponse(response: SDKControlResponse): void sendControlCancelRequest(requestId: string): void sendResult(): void teardown(): Promise } ``` ### 核心函数 ```typescript export async function initBridgeCore( params: BridgeCoreParams ): Promise ``` **BridgeCoreParams 输入参数**: ```typescript type BridgeCoreParams = { dir: string // 工作目录 machineName: string // 机器名称 branch: string // Git 分支 gitRepoUrl: string | null // Git 仓库 URL title: string // 会话标题 baseUrl: string // API 基础 URL sessionIngressUrl: string // 会话入口 URL workerType: string // 工作器类型 getAccessToken: () => string | undefined createSession: (opts: {...}) => Promise archiveSession: (sessionId: string) => Promise toSDKMessages?: (messages: Message[]) => SDKMessage[] onAuth401?: (staleAccessToken: string) => Promise getPollIntervalConfig?: () => PollIntervalConfig initialMessages?: Message[] previouslyFlushedUUIDs?: Set onInboundMessage?: (msg: SDKMessage) => void onPermissionResponse?: (response: SDKControlResponse) => void onInterrupt?: () => void onSetModel?: (model: string | undefined) => void onSetMaxThinkingTokens?: (maxTokens: number | null) => void onSetPermissionMode?: (mode: PermissionMode) => {...} onStateChange?: (state: BridgeState, detail?: string) => void onUserMessage?: (text: string, sessionId: string) => boolean perpetual?: boolean initialSSESequenceNum?: number } ``` ### 桥接生命周期 1. **注册桥接环境** ```typescript const reg = await api.registerBridgeEnvironment(bridgeConfig) environmentId = reg.environment_id environmentSecret = reg.environment_secret ``` 2. **创建会话** ```typescript const createdSessionId = await createSession({...}) currentSessionId = createdSessionId ``` 3. **启动工作轮询循环** ```typescript void startWorkPollLoop(pollOpts) ``` 4. **处理入口消息** ```typescript transport.onData(data => { handleIngressMessage(data, recentPostedUUIDs, recentInboundUUIDs, ...) }) ``` 5. **清理** ```typescript await doTeardownImpl() ``` --- ## 2. bridgeMessaging.ts - 消息处理 **文件位置**: `src/bridge/bridgeMessaging.ts` ### 消息入口处理 ```typescript export function handleIngressMessage( data: string, recentPostedUUIDs: BoundedUUIDSet, recentInboundUUIDs: BoundedUUIDSet, onInboundMessage: ((msg: SDKMessage) => void | Promise) | undefined, onPermissionResponse?: ((response: SDKControlResponse) => void) | undefined, onControlRequest?: ((request: SDKControlRequest) => void) | undefined, ): void ``` **消息类型判断**: ```typescript export function isSDKMessage(value: unknown): value is SDKMessage export function isSDKControlResponse(value: unknown): value is SDKControlResponse export function isSDKControlRequest(value: unknown): value is SDKControlRequest ``` ### 可桥接消息过滤 ```typescript export function isEligibleBridgeMessage(m: Message): boolean { // 排除虚拟消息 if ((m.type === 'user' || m.type === 'assistant') && m.isVirtual) { return false } return ( m.type === 'user' || m.type === 'assistant' || (m.type === 'system' && m.subtype === 'local_command') ) } ``` ### 服务器控制请求处理 ```typescript export function handleServerControlRequest( request: SDKControlRequest, handlers: ServerControlRequestHandlers, ): void ``` **支持的请求类型**: - `initialize` - 初始化 - `set_model` - 设置模型 - `set_max_thinking_tokens` - 设置最大思考 token - `set_permission_mode` - 设置权限模式 - `interrupt` - 中断 ### 标题文本提取 ```typescript export function extractTitleText(m: Message): string | undefined // 从用户消息中提取标题 ``` ### BoundedUUIDSet - UUID 环形缓冲区 ```typescript export class BoundedUUIDSet { constructor(capacity: number) add(uuid: string): void has(uuid: string): boolean clear(): void } ``` **用途**: - 消息回音过滤 - 重复消息去重 --- ## 3. replBridge.ts - REPL 会话桥接 **文件位置**: `src/bridge/replBridge.ts` ### 核心接口 ```typescript export type ReplBridgeTransport = { setOnConnect(callback: () => void): void setOnData(callback: (data: string) => void): void setOnClose(callback: (code: number | undefined) => void): void connect(): void write(message: object): Promise writeBatch(messages: object[]): Promise close(): void getStateLabel(): string getLastSequenceNum(): number isConnectedStatus(): boolean } ``` ### 传输类型 #### 1. HybridTransport (v1) - WebSocket 读取 - HTTP POST 写入 - 目标: Session-Ingress #### 2. SSETransport (v2) - Server-Sent Events 读取 - HTTP POST 写入 - 目标: CCR (/worker/*) ### 初始化流程 ```typescript export async function initReplBridge( params: InitBridgeOptions ): Promise ``` ### 消息写入 ```typescript writeMessages(messages: Message[]) { // 过滤可桥接消息 const filtered = messages.filter(m => isEligibleBridgeMessage(m) && !initialMessageUUIDs.has(m.uuid) && !recentPostedUUIDs.has(m.uuid) ) // 转换并发送 const sdkMessages = toSDKMessages(filtered) void transport.writeBatch(events) } ``` --- ## 4. jwtUtils.ts - JWT 认证 **文件位置**: `src/bridge/jwtUtils.ts` ### JWT 解析 ```typescript export function decodeJwtPayload(token: string): unknown | null { // 剥离 sk-ant-si- 前缀 const jwt = token.startsWith('sk-ant-si-') ? token.slice('sk-ant-si-'.length) : token // 解析 payload (不验证签名) const parts = jwt.split('.') if (parts.length !== 3 || !parts[1]) return null return jsonParse(Buffer.from(parts[1], 'base64url').toString('utf8')) } ``` ### Token 刷新调度器 ```typescript export function createTokenRefreshScheduler({ getAccessToken, onRefresh, label, refreshBufferMs = 5 * 60 * 1000, // 5 分钟 }): { schedule: (sessionId: string, token: string) => void scheduleFromExpiresIn: (sessionId: string, expiresInSeconds: number) => void cancel: (sessionId: string) => void cancelAll: () => void } ``` **刷新策略**: - 过期前 5 分钟刷新(默认) - 指数退避重试 - 最多 3 次连续失败 --- ## 5. sessionRunner.ts - 会话执行管理 **文件位置**: `src/bridge/sessionRunner.ts` ### 核心类型 ```typescript type SessionSpawnerDeps = { execPath: string scriptArgs: string[] env: NodeJS.ProcessEnv verbose: boolean sandbox: boolean debugFile?: string permissionMode?: string onDebug: (msg: string) => void onActivity?: (sessionId: string, activity: SessionActivity) => void onPermissionRequest?: (sessionId: string, request: PermissionRequest, accessToken: string) => void } export function createSessionSpawner(deps: SessionSpawnerDeps): SessionSpawner ``` ### 会话句柄 ```typescript export type SessionHandle = { sessionId: string done: Promise activities: SessionActivity[] accessToken: string lastStderr: string[] currentActivity: SessionActivity | null kill(): void forceKill(): void writeStdin(data: string): void updateAccessToken(token: string): void } ``` ### 活动提取 ```typescript function extractActivities( line: string, sessionId: string, onDebug: (msg: string) => void ): SessionActivity[] ``` **活动类型**: ```typescript type SessionActivity = | { type: 'tool_start'; summary: string; timestamp: number } | { type: 'text'; summary: string; timestamp: number } | { type: 'result'; summary: string; timestamp: number } | { type: 'error'; summary: string; timestamp: number } ``` ### 子进程管理 ```typescript const child: ChildProcess = spawn(deps.execPath, args, { cwd: dir, stdio: ['pipe', 'pipe', 'pipe'], env, windowsHide: true, }) ``` **环境变量设置**: ```typescript const env: NodeJS.ProcessEnv = { ...deps.env, CLAUDE_CODE_OAUTH_TOKEN: undefined, CLAUDE_CODE_ENVIRONMENT_KIND: 'bridge', CLAUDE_CODE_SESSION_ACCESS_TOKEN: opts.accessToken, CLAUDE_CODE_USE_CCR_V2: opts.useCcrV2 ? '1' : undefined, CLAUDE_CODE_WORKER_EPOCH: String(opts.workerEpoch), } ``` --- ## 6. bridgePermissionCallbacks.ts - 权限回调 **文件位置**: `src/bridge/bridgePermissionCallbacks.ts` ### 权限响应类型 ```typescript type BridgePermissionResponse = { behavior: 'allow' | 'deny' updatedInput?: Record updatedPermissions?: PermissionUpdate[] message?: string } ``` ### 回调接口 ```typescript type BridgePermissionCallbacks = { sendRequest( requestId: string, toolName: string, input: Record, toolUseId: string, description: string, permissionSuggestions?: PermissionUpdate[], blockedPath?: string ): void sendResponse(requestId: string, response: BridgePermissionResponse): void cancelRequest(requestId: string): void onResponse( requestId: string, handler: (response: BridgePermissionResponse) => void ): () => void // 返回取消订阅函数 } ``` --- ## 7. replBridgeHandle.ts - REPL 桥接句柄 **文件位置**: `src/bridge/replBridgeHandle.ts` ### 句柄类型 ```typescript export type ReplBridgeHandle = { bridgeSessionId: string environmentId: string sessionIngressUrl: string writeMessages(messages: Message[]): void writeSdkMessages(messages: SDKMessage[]): void sendControlRequest(request: SDKControlRequest): void sendControlResponse(response: SDKControlResponse): void sendControlCancelRequest(requestId: string): void sendResult(): void teardown(): Promise } ``` --- ## 8. replBridgeTransport.ts - 传输层实现 **文件位置**: `src/bridge/replBridgeTransport.ts` ### 工厂函数 ```typescript export function createV1ReplTransport( transport: HybridTransport ): ReplBridgeTransport export function createV2ReplTransport(opts: { sessionUrl: string ingressToken: string sessionId: string initialSequenceNum: number }): Promise ``` --- ## 9. 入口消息处理流程 ``` IDE Extension │ │ 1. 用户输入 ▼ ┌─────────────────┐ │ WS 连接 │ └─────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ handleIngressMessage() │ │ │ │ 1. 解析 JSON │ │ 2. 检查 UUID (回音/重复过滤) │ │ 3. 分发消息类型 │ │ - user → onInboundMessage │ │ - control_response → onPermission │ │ - control_request → onControlRequest │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Claude Code CLI (子进程) │ │ │ │ - 执行工具 │ │ - AI 推理 │ │ - 返回结果 │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ writeMessages() / writeSdkMessages() │ │ │ │ 1. 过滤消息 │ │ 2. 转换为 SDK 格式 │ │ 3. 通过 transport.writeBatch() 发送 │ └─────────────────────────────────────────┘ │ ▼ IDE Extension (WebSocket 订阅) │ ▼ 显示输出 ``` --- ## 10. 权限请求流程 ``` Claude Code CLI │ ▼ (can_use_tool 请求) ┌─────────────────────────────────────────┐ │ BridgePermissionCallbacks │ │ │ │ sendRequest(requestId, toolName, input) │ └─────────────────────────────────────────┘ │ │ WebSocket / HTTP ▼ ┌─────────────────────────────────────────┐ │ IDE Extension │ │ │ │ 显示权限对话框 │ │ 用户允许/拒绝 │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ sendResponse(requestId, response) │ │ │ │ response: { │ │ behavior: 'allow' | 'deny', │ │ updatedInput?, │ │ updatedPermissions? │ │ } │ └─────────────────────────────────────────┘ │ ▼ onResponse() 回调 │ ▼ CLI 继续执行或中止 ``` --- ## 11. 关键配置 ### 轮询配置 ```typescript const DEFAULT_POLL_CONFIG = { poll_interval_ms_not_at_capacity: 5000, // 5 秒 poll_interval_ms_at_capacity: 120000, // 2 分钟 reclaim_older_than_ms: 5 * 60 * 1000, // 5 分钟 non_exclusive_heartbeat_interval_ms: 30000, // 30 秒 session_keepalive_interval_v2_ms: 120000, // 2 分钟 } ``` ### 重连策略 ```typescript const MAX_RECONNECT_ATTEMPTS = 5 const INITIAL_BACKOFF_MS = 1000 const MAX_BACKOFF_MS = 30000 ``` ### UUID 缓冲区容量 ```typescript const recentPostedUUIDs = new BoundedUUIDSet(2000) const recentInboundUUIDs = new BoundedUUIDSet(2000) ``` --- ## 相关源码文件 | 文件 | 功能 | |------|------| | `bridgeMain.ts` | 桥接主入口和生命周期管理 | | `bridgeMessaging.ts` | 消息处理、路由、流量控制 | | `replBridge.ts` | REPL 会话桥接 | | `replBridgeTransport.ts` | 传输层实现 | | `sessionRunner.ts` | 子进程会话管理 | | `jwtUtils.ts` | JWT 解析和刷新调度 | | `bridgePermissionCallbacks.ts` | 权限回调接口 | | `replBridgeHandle.ts` | 句柄类型定义 | | `bridgeApi.ts` | 桥接 API 客户端 | | `pollConfig.ts` | 轮询配置 | | `flushGate.ts` | 刷新门控 | | `capacityWake.ts` | 容量唤醒机制 |