5.3 Bridge:从 claude.ai 远程控制本地 CLI

源码位置src/bridge/(33 个文件,replBridge.ts 单文件 98KB) 编译开关feature('BRIDGE_MODE')feature('DAEMON') 远程开关:GrowthBook tengu_ccr_bridge


一句话理解

Bridge 是一个双向通信通道,让你可以从 claude.ai 网页或手机,控制运行在你本地电脑上的 Claude Code CLI。

你的手机 / 网页浏览器
         │
     HTTPS
         │
    Anthropic 服务器(消息中转)
         │
    WebSocket / SSE
         │
   你的电脑上运行的 claude 进程

两种工作模式

模式一:REPL 内嵌模式(/remote-control

在你正在用的 claude 会话里输入 /remote-control

- 当前对话被"镜像"到 claude.ai
- 本地打字 和 网页打字 都能驱动同一个会话
- 关闭网页不影响本地继续运行

代码:src/bridge/initReplBridge.ts + src/bridge/replBridge.ts

模式二:独立 Bridge 服务器(claude remote-control

作为一个长期运行的后台服务:

注册为"环境"→ 持续轮询任务 → 每个任务启动子进程

代码:src/bridge/bridgeMain.ts(112KB!)

三种 Session 分发模式:

模式说明
single-session处理一个 session 后关闭
worktree每个 session 获得独立 git worktree
same-dir所有 session 共享同一目录

启用条件(三层检查)

// src/bridge/bridgeEnabled.ts
isBridgeEnabled() =
  feature('BRIDGE_MODE')             ← 编译时(外部版不可用)
  && isClaudeAISubscriber()          ← 必须是付费订阅用户
  && GrowthBook('tengu_ccr_bridge')  ← Anthropic 远程开关

非订阅用户会收到"请升级订阅"的诊断提示。


两代传输协议

v1 协议(环境层方案)

完整的"环境"生命周期:

1. 注册环境  POST /v1/environments/bridge
2. 轮询工作  GET  /v1/environments/{id}/work/poll(长轮询,指数退避)
3. 确认工作  POST /v1/environments/{id}/work/confirm
4. 心跳      POST /v1/environments/{id}/heartbeat
5. 注销      DELETE /v1/environments/{id}

传输层:HybridTransport(WebSocket 读 + HTTP POST 写)

v2 协议(跳过环境层)

1. 创建 session  POST /v1/code/sessions
2. 获取 JWT      POST /v1/code/sessions/{id}/bridge
3. 建立通道      SSE 读 + CCRClient 写
4. JWT 刷新      到期前 5 分钟主动刷新

v2 完全绕过 Environments API,直接连到 session-ingress,延迟更低。

GrowthBook 门控:tengu_bridge_repl_v2(v1 到 v2 的灰度切换开关)


服务端控制请求

通过 Bridge,claude.ai 可以对本地 CLI 发出控制指令:

请求类型说明
initialize获取 CLI 能力声明
set_model切换模型
set_max_thinking_tokens调整思考 token 上限
set_permission_mode改变权限模式(accept_edits 等)
interrupt中断当前操作(相当于 Ctrl+C)

关键时限:控制请求必须在 10-14 秒内响应,否则服务端关闭连接。


消息去重:BoundedUUIDSet

src/bridge/bridgeMessaging.ts 里有一个精巧的去重机制:

// 环形缓冲区,避免内存无限增长
  private readonly maxSize: number  // 默认 2000
  private readonly queue: string[]  // 环形缓冲区
  private readonly set: Set<string>
  
  add(uuid: string): boolean {
    if (this.set.has(uuid)) return false
    if (this.queue.length >= this.maxSize) {
      const evicted = this.queue.shift()!
      this.set.delete(evicted)
    }
    this.queue.push(uuid)
    this.set.add(uuid)
    return true
  }
}

用途:


崩溃恢复机制

src/bridge/bridgePointer.ts 实现了指针文件机制:

写入:~/.claude/projects/<cwd-hash>/bridge-pointer.json
内容:{ sessionId, environmentId, source }
TTL:4 小时
mtime:运行时定期刷新
清除:干净退出时删除

下次启动 Claude Code 时,如果检测到指针文件(未超时),会提示:

Found an interrupted Bridge session. Would you like to resume it?
  ❯ Yes, resume session abc-123
    No, start fresh

安全设计

机制说明
OAuth 令牌主身份凭证,从系统 keychain 读取,自动刷新
Worker JWT每 session 的短期令牌(几小时有效)
Trusted Device Token设备信任令牌,防止未知设备接管
401 自动恢复检测 401 → OAuth 刷新 → 重获凭证 → 重建传输
命名空间保护isInProtectedNamespace() 防止操作敏感系统目录

replBridge.ts 为何 98KB?

src/bridge/replBridge.ts 是整个项目单文件最大的(98.18KB)。这是因为它承载了整个 Bridge 系统在 REPL 模式下的全部实现

这种"大文件"设计是有代价的:它违反了单一职责原则,但集中管理了所有 Bridge REPL 相关的状态,避免了复杂的跨模块状态同步问题。


下一步