远程桥接 / Remote Control
概述
Bridge(桥接)模式将本地 CLI 连接到 Anthropic 的远程会话基础设施(CCR, Claude Code Remote),使用户可以通过 claude.ai/code 从浏览器控制本地终端。
两种传输模式
1. 基于环境 (env-based)
通过 Environments API 进行 poll/dispatch:
- CLI 注册为一个 "环境"(environment)到 Anthropic 服务器
- 服务器通过
pollForWorkAPI 分发工作 - 客户端领取(acknowledge)工作、执行、发送心跳、返回结果
核心 API(src/bridge/bridgeApi.ts):
| 端点 | 方法 | 用途 |
|---|---|---|
POST /v1/environments/bridge |
registerBridgeEnvironment | 注册当前终端为可用的执行环境 |
GET .../work/poll |
pollForWork | 轮询待处理的工作 |
POST .../work/{id}/ack |
acknowledgeWork | 确认领取工作 |
POST .../work/{id}/heartbeat |
heartbeatWork | 发送心跳(延长租约) |
POST .../work/{id}/stop |
stopWork | 停止工作 |
DELETE .../environments/bridge/{id} |
deregisterEnvironment | 注销环境 |
认证:使用 OAuth token 或 Bridge Access Token。
2. 无环境 (env-less)
直接通过 OAuth → Worker JWT 交换来连接远程会话:
- 通过 OAuth 获取访问令牌
- 直接连接到远程会话 WebSocket(
/v1/sessions/ws/{sessionId}/subscribe) - 使用 SDK 消息格式进行双向通信
核心组件(src/remote/):
| 文件 | 路径 | 用途 |
|---|---|---|
| SessionsWebSocket.ts | src/remote/SessionsWebSocket.ts |
WebSocket 客户端 |
| sdkMessageAdapter.ts | src/remote/sdkMessageAdapter.ts |
SDK 消息转换适配器 |
核心组件
bridgeApi.ts(src/bridge/bridgeApi.ts)
HTTP 客户端,封装了所有 Bridge API 调用:
- OAuth 认证:自动 401 重试 + token 刷新(通过
onAuth401回调) - BridgeFatalError:不可重试的错误(认证失败、权限不足、会话过期)
- ID 验证:
validateBridgeId()防止路径遍历攻击 - 错误处理:对 401/403/404/410/429 状态码分别处理
export class BridgeFatalError extends Error {
readonly status: number
readonly errorType: string | undefined
}
bridgeMain.ts(src/bridge/bridgeMain.ts)
主桥接逻辑,约 3000 行。负责:
- 环境注册与生命周期管理
- 工作(work)的 poll/dispatch 循环
- 会话(session)的创建、运行、恢复
- 多会话支持(
--spawn,--capacity,--create-session-in-dir) - 优雅关闭(SIGTERM → SIGKILL 宽限期)
- 退避策略(连接退避、通用退避、stopWork 退避)
export type BackoffConfig = {
connInitialMs: number // 连接初始延迟
connCapMs: number // 连接最大延迟 (2min)
connGiveUpMs: number // 连接放弃时间 (10min)
generalInitialMs: number // 通用初始延迟
generalCapMs: number // 通用最大延迟 (30s)
generalGiveUpMs: number // 通用放弃时间 (10min)
shutdownGraceMs?: number // SIGTERM→SIGKILL 宽限期
}
replBridge.ts(src/bridge/replBridge.ts)
REPL 集成桥接,约 2400 行。在 REPL(交互式终端)模式下将 CLI 连接到远程会话:
- 通过
HybridTransport实现消息转发 - 支持 CCR v1/v2 协议(
createV1ReplTransport/createV2ReplTransport) - 消息入口(ingress)处理
- 控制请求/响应(
SDKControlRequest/SDKControlResponse) - 容量唤醒(capacity wake)信号
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>
}
SessionsWebSocket.ts(src/remote/SessionsWebSocket.ts)
WebSocket 客户端,用于直接连接远程会话:
协议:
- 连接到
wss://api.anthropic.com/v1/sessions/ws/{sessionId}/subscribe?organization_uuid=... - 发送认证消息:
{ type: 'auth', credential: { type: 'oauth', token: '...' } } - 接收 SDK 消息流
重连机制:
RECONNECT_DELAY_MS = 2000:重连延迟 2 秒MAX_RECONNECT_ATTEMPTS = 5:最大重连次数PING_INTERVAL_MS = 30000:30 秒心跳间隔PERMANENT_CLOSE_CODES = new Set([4003]):4003(未授权)为永久关闭,不重连MAX_SESSION_NOT_FOUND_RETRIES = 3:4001(会话未找到)有限重试(压缩期间可能短暂出现)
type SessionsWebSocketCallbacks = {
onMessage: (message: SessionsMessage) => void
onClose?: () => void
onError?: (error: Error) => void
onConnected?: () => void
onReconnecting?: () => void
}
sdkMessageAdapter.ts(src/remote/sdkMessageAdapter.ts)
SDK 消息格式转换器。将 CCR 发送的 SDK 格式消息(SDKMessage)转换为 CLI 内部的消息类型(Message):
convertAssistantMessage():SDKAssistantMessage→AssistantMessageconvertStreamEvent():SDKPartialAssistantMessage→StreamEvent- 处理多种消息类型:assistant、system、compact_boundary、status、tool_progress、result 等
remotePermissionBridge.ts
在远程会话中处理权限请求桥接(位于 src/hooks/useSSHSession.ts、useRemoteSession.ts、useDirectConnect.ts),将远程权限提示通过 WebSocket 转发给用户。
AskUserQuestion 远程转发
AskUserQuestion 走独立问答通道(详见 docs/architecture/safety-and-permissions.md 附录),桥接连接时会把问题作为 can_use_tool control_request 转发给远程用户(claude.ai),与本地 overlay 竞速应答:
- 远程
allow+updatedInput.answers→ 按题映射回填;通用allow降级为每题首个选项。 - 远程
deny→ 拒绝该问题。 - 任一端先应答即
cancelRequest另一端,避免残留 prompt。
实现位于 src/screens/REPL.tsx 对 questionService 事件(asked / replied / rejected)的订阅。
认证机制
| 机制 | 说明 |
|---|---|
| OAuth Token | 通过 OAuth 2.0 流程获取的访问令牌 |
| Bridge Access Token | 桥接模式专用的访问令牌,通过 workSecret.ts 中的 decodeWorkSecret() 解码 |
| Trusted Device Token | X-Trusted-Device-Token 头部,用于权限提升 |
| Token 刷新 | handleOAuth401Error 在 401 时自动刷新 |
WebSocket 协议(CCR v1/v2)
认证流程
Client → Server: { type: "auth", credential: { type: "oauth", token: "..." } }
Server → Client: { type: "auth_ok" } 或 { type: "auth_error" }
消息格式
- CCR v1:通过
replBridgeTransport.ts的createV1ReplTransport处理 - CCR v2:通过
createV2ReplTransport处理,使用buildCCRv2SdkUrl()构建 URL
心跳保活
- 标准 WebSocket ping:30 秒间隔
- 会话活动信号(
sendSessionActivitySignal())在压缩等长时间操作期间发送,防止 WebSocket 因 idle 超时被断开
其他组件
| 文件 | 路径 | 用途 |
|---|---|---|
| bridgeConfig.ts | src/bridge/bridgeConfig.ts |
桥接配置管理 |
| bridgeMessaging.ts | src/bridge/bridgeMessaging.ts |
桥接消息处理逻辑 |
| capacityWake.ts | src/bridge/capacityWake.ts |
容量唤醒信号 |
| codeSessionApi.ts | src/bridge/codeSessionApi.ts |
代码会话 API |
| trustedDevice.ts | src/bridge/trustedDevice.ts |
受信任设备管理 |
| workSecret.ts | src/bridge/workSecret.ts |
Work Secret 编解码 |
| sessionIdCompat.ts | src/bridge/sessionIdCompat.ts |
会话 ID 兼容性转换 |
| pollConfig.ts | src/bridge/pollConfig.ts |
轮询间隔配置 |
| types.ts | src/bridge/types.ts |
桥接模块类型定义 |