first commit

This commit is contained in:
H
2026-04-03 13:01:19 +08:00
commit 538eced414
2575 changed files with 645911 additions and 0 deletions

View File

@@ -0,0 +1,659 @@
# 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<void>
}
```
### 核心函数
```typescript
export async function initBridgeCore(
params: BridgeCoreParams
): Promise<BridgeCoreHandle | null>
```
**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<string | null>
archiveSession: (sessionId: string) => Promise<void>
toSDKMessages?: (messages: Message[]) => SDKMessage[]
onAuth401?: (staleAccessToken: string) => Promise<boolean>
getPollIntervalConfig?: () => PollIntervalConfig
initialMessages?: Message[]
previouslyFlushedUUIDs?: Set<string>
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<void>) | 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<void>
writeBatch(messages: object[]): Promise<void>
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<ReplBridgeHandle | null>
```
### 消息写入
```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<SessionDoneStatus>
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<string, unknown>
updatedPermissions?: PermissionUpdate[]
message?: string
}
```
### 回调接口
```typescript
type BridgePermissionCallbacks = {
sendRequest(
requestId: string,
toolName: string,
input: Record<string, unknown>,
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<void>
}
```
---
## 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<ReplBridgeTransport>
```
---
## 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` | 容量唤醒机制 |