Article Detail

Claude Code 的 Bridge 层:IDE 集成是如何工作的

2026-05-14MDX POCzh

Claude Code 的 Bridge 层:IDE 集成是如何工作的

Claude Code 最初只是一个终端应用。你在终端里打字,它在终端里回应。但如今大部分开发者并不整天待在终端里——真正的战场在 VS Code、JetBrains 和浏览器里。

Bridge 层就是连接 Claude Code 和这些环境的部分。它大约有 31 个文件,位于 src/bridge/,默认被一个功能开关关闭。当它启用时,Claude Code 就不再只是一个终端 REPL,而变成了一个 IDE 和网页 UI 可以对话的服务。

架构总览

Bridge 连接了三方:CLI(运行 Claude Code)、服务端 session-ingress 层、和客户端(IDE 扩展或网页 UI)。

IDE / claude.ai  ──WebSocket/SSE──→  Session-Ingress  ──→  CLI (replBridge)
   ←── POST / CCRClient 写入 ────  Session-Ingress  ←──  CLI

有两代传输协议,这本身说明系统演进得很快:

版本 读取路径 写入路径
v1(基于环境) WebSocket 到 Session-Ingress HTTP POST 到 Session-Ingress
v2(无环境) 通过 SSETransport 的 SSE 流 CCRClient 到 /worker/* 端点

两者都被 ReplBridgeTransport 接口封装,系统其他部分不需要关心具体用的是哪个。

传输协议

<>

最初的设计。Claude Code 向 session-ingress 服务注册一个环境,获取 WebSocket URL,然后轮询任务:

  1. 注册环境 → 获取 WorkSecret(含会话配置的 base64url 编码令牌)
  2. 轮询任务 → 确认 → 启动会话
  3. 打开 WebSocket 接收入站消息
  4. 通过 HTTP POST 回复出站消息

WorkSecret 是关键载荷。它将 session-ingress 令牌、API 基础 URL、git 源和认证令牌打包到一个 base64 编码的 blob 中。解码后就有了建立会话所需的一切。

<>

更新的设计,设置更简单。不需要环境注册:

  1. 通过 API 直接创建会话
  2. POST 到 /bridge 端点获取 JWT
  3. 打开 SSE 流接收入站消息
  4. 通过 CCRClient 写入出站消息

v2 路径大幅降低了设置复杂度。没有轮询、没有环境注册——直接连接。

认证

Bridge 有多层认证模型,包含几种令牌类型:

  1. OAuth 令牌 — 用户必须是 claude.ai 订阅者(isClaudeAISubscriber()
  2. JWT — 以 sk-ant-si- 开头的 Session-ingress 令牌,包含 exp 声明。到期前主动刷新。
  3. 受信设备令牌X-Trusted-Device-Token 头,用于高安全级别的会话
  4. 环境密钥 — 打包所有会话配置的 base64url 编码 WorkSecret

还有开发环境覆盖:CLAUDE_BRIDGE_OAUTH_TOKENCLAUDE_BRIDGE_BASE_URL 让工程师可以针对测试环境调试。

消息流

入站(服务器 → CLI)

消息从 IDE/网页 UI 通过 session-ingress 流向 CLI:

  • user 消息 — 在网页 UI 中输入的提示词,入队到 REPL 输入队列
  • control_request — 生命周期命令:初始化、设置模型、中断、设置权限模式、设置最大思考令牌数
  • control_response — 从 IDE 对话框返回的权限决定

出站(CLI → 服务器)

消息从 CLI 流回客户端:

  • assistant 消息 — Claude 的回复
  • user 消息 — 回显用于同步
  • result 消息 — 单轮完成通知
  • 系统事件、工具启动、活动状态

去重由 BoundedUUIDSet(容量 2000)处理,它会跟踪最近消息的 UUID 以拒绝回显和重复投递。这防止了分布式消息传递中不可避免的 Bug。

生命周期

Bridge 会话经过一个明确定义的生命周期:

  1. 资格检查isBridgeEnabled() 检查 GrowthBook 功能开关(tengu_ccr_bridge)和 OAuth 订阅者状态
  2. 会话创建createBridgeSession() 向 API 发送 POST 请求
  3. 传输初始化:v1 的 HybridTransport 或 v2 的 SSETransport + CCRClient
  4. 消息泵:通过传输读取入站,通过批处理写出站
  5. 令牌刷新createTokenRefreshScheduler() 在 JWT 到期前主动刷新
  6. 拆卸teardown() 刷新待处理消息、关闭传输、归档会话

生成模式

运行 claude remote-control 时,Bridge 支持三种生成模式:

模式 行为
single-session 当前目录一个会话,结束后拆卸
worktree 持久化服务器,每个会话获得隔离的 git worktree
same-dir 持久化服务器,会话共享工作目录

worktree 模式对多会话场景最有用。每个会话在自己的 git worktree 中操作,多个用户或 IDE 会话可以同时在不同的分支上工作而不会冲突。

通过 Bridge 的权限流

当 IDE 发送工具执行请求时,权限流走的路径与终端 REPL 不同:

IDE 发送 can_use_tool 控制请求


CLI 处理权限检查链

    ├── 自动模式 → CLI 自动决定允许/拒绝

    └── 交互式模式:


        CLI 向 IDE 发送 control_request → IDE 显示权限对话框

            用户点击 允许 / 拒绝 / 总是允许


        IDE 返回 control_response { behavior: 'allow' | 'deny', updatedInput? }


        CLI 根据响应执行(或跳过)工具

在自动模式下,CLI 默默决定。在交互模式下,权限对话框出现在 IDE 中——一个原生对话框,而不是终端提示。这与终端有根本性的不同体验:在终端中,用户已经在看终端了;在 IDE 中,权限请求需要以不打断编辑流程的通知或对话框形式出现。

功能开关架构

整个 Bridge 都在 feature('BRIDGE_MODE') 后面,默认值为 false。这是一个编译时的功能开关,使用 bun:bundle

import { feature } from 'bun:bundle'
// feature('BRIDGE_MODE') 默认返回 false

开关在每个入口点都做了防护:

位置 防护
CLI 入口点 feature('BRIDGE_MODE') && args[0] === 'remote-control'
REPL bridge hooks 所有 useAppState 调用都通过三元运算符防护
UI 组件 Bridge 关闭时提前返回 null
配置项 只在 Bridge 启用时才展开

这是精心设计的死代码消除。当 BRIDGE_MODE 为 false 时,打包器在构建时会直接剥离整个分支。Bridge 代码可以引用 OAuth 库、WebSocket 处理器和 SSE 传输,而不会影响纯终端版本的大小。

为什么这很重要

Bridge 的引用遍布整个代码库。引用 Bridge 类型或函数的文件存在于 hooks、组件、设置、工具和命令中。如果功能开关有任何泄漏,缺失的导入或运行时错误就可能让终端 REPL 崩溃。安全性来自三个设计选择:

  1. 所有 Bridge 文件都存在 — 即使 Bridge 关闭,导入也能解析
  2. 导入时无副作用 — Bridge 模块定义函数,但不在导入时执行 Bridge 逻辑
  3. 运行时防护isBridgeEnabled() 返回 falsegetReplBridgeHandle() 返回 nulluseReplBridge 短路

还有一个存根文件(src/bridge/stub.ts)提供了 isBridgeAvailable()false 和静默的空操作句柄,让任何未来代码都能安全回退而无需触碰功能开关。

Chrome 扩展集成

与主 Bridge 分开,还有两个 Chrome 特定的入口点:

--claude-in-chrome-mcp

启动一个 MCP 服务器,桥接 Claude Code 和 Chrome 扩展。使用 StdioServerTransport(基于 stdin/stdout 的 MCP),支持原生 socket 和 WebSocket 桥接(wss://bridge.claudeusercontent.com)。由另一个 GrowthBook 开关控制(tengu_copper_bridge)。

--chrome-native-host

实现 Chrome 的原生消息协议——4 字节长度前缀 + JSON over stdin/stdout。创建一个 Unix 域套接字服务器,在 Chrome 扩展和本地 Claude Code 实例之间代理 MCP 消息。

两个 Chrome 路径都使用动态导入,所以除非显式调用,否则没有启动成本。它们完全独立于 BRIDGE_MODE 功能开关。

关键类型

理解 Bridge 需要理解几个核心类型:

  • BridgeConfig — 完整 Bridge 配置(目录、认证、URL、生成模式、超时)
  • WorkSecret — 解码后的工作载荷(令牌、API URL、git 源、MCP 配置)
  • SessionHandle — 运行中的会话句柄(终止、活动、stdin、令牌更新)
  • ReplBridgeHandle — REPL Bridge API(写消息、控制请求、拆卸)
  • BridgeState'ready' | 'connected' | 'reconnecting' | 'failed'
  • SpawnMode'single-session' | 'worktree' | 'same-dir'

激活 Bridge

Bridge 不是你在配置里翻转一下就能打开的。它需要:

  1. 环境变量export CLAUDE_CODE_BRIDGE_MODE=true
  2. 认证:登录 claude.ai 并具有有效订阅
  3. GrowthBook 开关:你的组织已启用 tengu_ccr_bridge
  4. IDE 扩展:安装了 VS Code 或 JetBrains 扩展
  5. 网络:能够连接到 session-ingress 的 WebSocket/SSE

然后运行 claude remote-control,终端会出现一个二维码——扫描它就能连接你的 IDE。

一些观察

Bridge 是那种工作起来看不见、出了问题就很让人困惑的系统。双传输设计(v1 vs v2)说明团队在协议上迭代得很快,v2 的简化(不需要环境注册)正是那种只有把东西跑在生产环境里才能学到的经验。

功能开关的纪律性值得学习。每个 Bridge 特性——每个 hook、每个组件、每个工具——都被 feature('BRIDGE_MODE') 或等效的运行时防护包裹。没有 Bridge 部分激活的中间状态。这就是在共享代码库中开发功能而不危及核心产品的方式。

Chrome 集成是个很好的补充。它是一个完全独立的子系统,有自己的入口点、自己的认证、自己的传输。它与主 Bridge 共存而不冲突,这本身就是架构模块化的证明。