桥接 Claude Code:在任何地方办公,并让你的 Agent 们互相对话


最近大家都在聊怎么在终端之外跑 coding agent。Claude Code 官方有桌面端、网页版、手机远程控制——都不错。但当我开始在一台 Mac mini 上跨多个项目跑多个 Claude Code 会话之后,我想要的东西官方都给不了:在我天天在用的任何一个聊天 app 里续接任意会话;在一个页面里驾驭所有会话;还有一件我没见别人做过的事——让会话之间互相说话

所以我造了个 bridge。这篇文章讲它能干什么、是怎么搭起来的,以及它靠一场 iCloud 事故证明自己价值的那一天——四个 AI 会话自己协同完成了整场救援,bridge 自己的会话任总指挥。

什么算一个 bridge

我的工作定义:bridge 是一个常驻的小守护进程,把一个活的 Claude Code 会话和任意消息界面配成一对。

机制简单到不好意思:

  • 每个通道守护把收到的消息写进只追加的 inbox 日志,一行一条 JSON
  • Claude Code 会话上挂一个 monitor 盯着 inbox——每来一行就唤醒 agent,跟你在终端里打字一模一样
  • agent 把回复写进 outbox 目录;守护捡走,送回你手里那个 app
  • claim 文件把每个通道绑死到一个会话,两个会话永远不会抢同一部“电话”

就这些。文件、tail、JSON。没有消息队列,没有数据库,没有服务器——全跑在 Mac 上,公网访问走 SSH 反向隧道到一台小云主机,它只见得到加密流量。哪个环节死了,launchd 把它拉起来。

简单就是设计目标。因为接口就是“往文件追加一行”,任何东西都能当通道——一个 bridge 就这样长成了四个,然后长出了更怪的东西。

bridge 架构总览

一页看全系统。北向:我人在的聊天界面。南向:干活的 Claude Code 会话和 Claude SDK bot——左边是“随时随地远程结对编程”,右边是“用聊天做网络运维”。中间是 bridge:守护进程、纯文件、claim/fanout,以及让 agent 们互相呼叫的组播交换机(绿色)——那条不可逾越的守卫红线就印在上面。

把你在用的每个界面都桥上

bridge 的第一件事很朴素但改变生活:我再也不用守在电脑前。

通道 传输方式 Markdown 收图 流式输出
Web 本地 HTTP + WebSocket,公网走 SSH 隧道 完整(marked.js) 多图 即时(WS)
飞书 WebSocket 长连接 + OpenAPI 富卡片 支持 打字机卡片
Telegram bot long-polling 大部分语法 支持 占位 + 编辑
企微 官方 bot WSS 部分 支持(AES) 分块流式

干活干到一半出门,会话不会暂停——它跟着我走。地铁上用 Telegram 回一个设计决策,午饭时在飞书批一个重构,手表上收到“测试全过”的推送。回复会扇出:我在哪个通道说话,其他活跃通道全部收到镜像副本,对话永远不分叉。

每条回复都带一行状态 footer——项目名、模型、上下文用量、订阅额度——我永远知道是哪个 agent 在说话、它还剩多少油。这就是“一个聊天机器人”和“我的会话在我口袋里”的区别。

同一个会话,手机上的三个界面

一个会话,三个 app:Web、Telegram、飞书。Telegram 和飞书里那行「🪞 你在 Web 说」就是扇出镜像在工作——在任何一个通道说话,其他通道全部同步,我永远不用想“我刚才那句是在哪个 app 说的?”

推送通知把 agent 的汇报送上锁屏

Push,最懒的通道(topic 名已打码):事情办完,锁屏和手表自己震。agent 向我汇报——我从不去轮询它。

Web 驾驶舱

web 通道从“浏览器里聊个天”长成了一个完整驾驶舱。先校准一下,官方方案今天长这样:

Claude Code 官方 remote control 预览版

Claude Code 自带的 remote control(research preview):干净、极简的单会话聊天。偶尔看一眼很好——但这份极简正是我一直“长不下”的原因。下面的一切,都因为我想要一间驾驶舱,而不是一个猫眼。

这背后有一条产品哲学:**好的产品经理是懒的、有同理心的。**懒:每天都要做的事,必须一次点击完成。同理心:界面要在用户开口之前,替他回答远程用车时真正的疑问——它听到我了吗?它还在干活吗?现在是谁在说话?——下面每张截图,都是这些问题的一个答案。

web 驾驶舱全功能标注图

驾驶舱一图全览(URL 已打码)。从上到下:项目活动状态跑马灯;每条回复自带的 Claude Code Usage HUD;CLI 执行 /web 后落进对话的启动卡——同一个会话的本机/局域网/局域名/公网四个入口;右上工具条——分屏、主题、色系、服务、会话组、Lark、TG、Web、Hub;输入行一排——会话切换、快捷指令、附件上传、WEB/CLI 一键切换、语音输入、文本框放大。

  • **一页切所有项目。**一个文件夹按钮在同一个 tab 里切换所有跑着的 Claude Code 项目。还有一个 hub 导航页——就是封面图——所有项目、bot、SSH 隧道带忙闲灯一屏看完。
  • **一键仪式。**开工和收工 SOP 是打磨了很久的长咒语,现在它们是按钮。
  • **快捷短语。**一天要发五十次的回复——“OK”“你有什么建议?”“按你建议来”——一次点击。
  • **分屏。**一个浏览器窗口两个项目并排,都是活的。
  • **顶栏心跳。**状态跑马灯实时滚动会话此刻在干什么,干活时绿点呼吸。
  • **内嵌实时终端。**真正的 Claude Code TUI 流进网页(tmux capture-pane 出、send-keys 进——绝不 attach,那会毁掉本地终端的尺寸)。能滚、能选、能直接打字,手机上也行。
  • **整支船员队伍在标签页里。**一个项目跑着一支角色舰队——PM、Coder、Tester、Reviewer、SRE——各占一个 tmux pane,终端视图给每个船员一个标签。
  • **隧道监控。**每条 SSH 隧道在 hub 自报健康;watchdog 自动救活死掉的转发。

“开工”按钮把整段开工咒语落进对话

一键仪式。点“开工”,整段开工咒语——读日志、汇报昨天、提议今天——直接落进对话。咒语按项目从磁盘现读,改了 SOP 按钮自动更新。每天都要做的事,就该只花一次点击。

输入框上方的快捷短语,“按你建议来。“高亮

快捷短语。agent 提了方案,“按你建议来。“一点就发。把一天五十次的回复预制成按钮,不是让我更少引导 agent——是让我引导得更勤。

顶栏状态跑马灯实时滚动会话动态

顶栏心跳。跑马灯实时滚动会话此刻在干什么——spinner 文字、耗时、跑着的 monitor——加上下文和额度条。在我开口之前,它已经回答了“它还在干活吗?”

内嵌实时终端显示真正的 Claude Code TUI

内嵌终端。扫一眼不够时,一点切到真 TUI——能滚、能选、能打字,手机上也行。这张图里它正好在改这篇文章。

终端视图上方的船员角色标签

标签页里的船员队。一个项目跑着一整支角色会话舰队——Captain、PM、Coder、Tester、Reviewer、SRE——每人一个实时终端标签。查看任何一个“员工”,在任何地方,只花一次点击。

分屏视图,两个项目并排,项目切换下拉展开

分屏。一个浏览器窗口两个项目并排都是活的——下拉菜单(箭头处)还能把任何一屏换成任何在跑的项目。看着一个 agent 写博客,另一个修生产 bot。

这里没有任何炫技的 web 技术,一个 aiohttp 服务加一个 HTML 文件。杠杆全在它坐的位置:直接骑在一个活的 agent 会话上。

Agent 对 Agent:给 coding agent 做组播

这是我因为职业习惯顺手做的,现在认为是全系统最值钱的一块。

我长期跑着好几个 Claude Code 项目:bridge 本身、一支 SDK agent 舰队、一个修电脑工具箱、一个语言学习 app。它们是独立会话、独立上下文——这是对的,直到它们需要彼此的那一刻。修电脑项目懂 macOS 诊断;bridge 项目管消息基建;舰队项目守着生产 bot。真实工作天天跨这些边界。

我的老本行给了答案:从网络工程借来组播。任何项目注册进一个命名组,发给组的消息到达每个成员——每个成员都是一个活的 Claude Code 会话:

# 单播:一个会话发给另一个会话
python3 xproj_send.py --from ccbridge --to repaircomputer \
  --msg "你的 web UI 起来了但回复卡住——查一下你的 monitor 进程。"

# 组播:注册一次组,之后一次喊全员
python3 xproj_send.py groups                      # 列出所有组
python3 xproj_send.py --from ccbridge --to @icloud-incident \
  --msg "状态确认:汇报你丢了什么、还有什么在跑。"

投递复用和其他通道一模一样的原语:消息直投目标实例的 inbox,对方 monitor 触发,那个会话醒来,把它当成同事发来的话读。每条跨项目消息带身份前缀——【ccbridge→repaircomputer】——agent 和我都永远知道是谁在说话

web UI 里的组播面板

组播面板。把项目勾进一个命名组,发给组的消息到达每个成员的活会话。图里正在起草的,就是那条把 @ccbridge 推上事故总指挥位置的消息。

这把“几个孤立的 agent”变成了一个能互相呼叫的团队。一个会话发现共享基建的 bug,通知它的负责人;负责人修完回 ACK,双方各自记进自己的工作日志。我事后读记录,像在读员工群聊。

也因为“agent 互相提要求”是真正的无人区,这里有一道硬守卫:**同事的消息永远是建议,不是主人的命令。**破坏性/跨项目操作——停所有 bridge、删队列、杀进程——只有我本人、以显式斜杠命令、在我拥有的通道上发出才会执行。隔壁会话说“全部关掉”,按规矩就只是聊天。(一次幽灵消息事故教会我这条;现在它写进了每个项目的宪法。)

它证明自己的那一天

昨天晚上,macOS 悄悄把我的 Apple ID 登出了。我的 Obsidian 库——工作日志、运维手册、所有项目的任务池——住在 iCloud Drive 上。工作进行到一半,它从磁盘上消失了,当时四个 Claude Code 会话正用着它。

系统设置显示 Apple ID 已登出

一切的起点:系统设置显示我的 Apple ID 被悄悄登出。没有弹窗、没有警告——库就这么从磁盘上蒸发了。

Obsidian 里的库几乎被掏空

爆炸半径:一整个工作区,只剩两个残缺的文件夹。

agent 们比我先发现。一个会话发现自己的项目文件夹整个没了,当场用会话记忆重建了配置文件;另一个写文件撞上 FileNotFoundError,查了目录树,正确地怀疑是 iCloud 卸载而不是误删:

一个会话发现项目文件夹消失

第一个报警的:语言 app 会话发现整个项目文件夹没了,当场用会话历史重建配置——然后自己查出真凶:iCloud 登出会把本地文件卸载掉,什么都没被删。“虚惊一场”,判得准。

另一个会话写到一半撞上 FileNotFoundError

第二个报警的:舰队 captain 写日志撞上 FileNotFoundError,查完残存的目录树——没有瞎猜,主动停下自己的“删档调查”向上汇报。下方是它当时正带着的八角色船员队。

以前的我会立刻重新登录 iCloud 然后祈祷。实际发生的是:

  1. 一个组播组拉起来了——“iCloud 账户误退出问题”——四个成员项目:bridge、舰队 captain、语言 app、修电脑工具箱。
  2. 修电脑项目查出根因并报进组里:accountsd 卡死 101% CPU,在 Accounts4.sqlite 上写循环——长时间不重启的 Mac 的已知故障模式。它的结论反转了我的计划:先重启,后登录,否则损坏的账户状态可能再咬一口。
  3. 每个会话各自安全停车,因为各自最懂自己的状态。然后:重启、登录,所有会话 resume——claude --resume,monitor 重新挂上,24 条 SSH 隧道在 launchd 下全部自动重建。
  4. **最终清点:四个会话全部恢复——总指挥逐一核实其余三个全绿——零丢失。**修电脑 agent 事后还点出一个隐蔽陷阱:iCloud 重登恢复的文件可能是云端旧版本,于是每个项目先 diff 自己的关键配置再信任——真抓到一个被回滚的文件。

我留了底。下面就是当时真实发生的群聊:

两个项目并排看组播 kickoff

Kickoff(红字标注是我自己复盘时加的)。① 一条组播同时到达四个成员。③ 我指定 ccbridge 任总指挥。④ 它立刻发出全组号令——附带每个会话的停车保数据指引。⑤⑥ 各成员照做,并在同一条通道回报进展。

各成员回报数据已安全存盘

安全报告陆续进来:四个成员全部确认“工作已本地存盘,可安全重启”——各自列清了自己依赖什么。然后总指挥做了一件我没有编排的事:基于修电脑项目的 accountsd 诊断,反转了我“先登录”的原计划。新证据,新号令。

全员就绪,总指挥贴出恢复计划

成员 3/3 就绪(加上总指挥是四个)。重启后的拉回清单提前贴好——登 iCloud、拉起舰队、逐个 resume、重开通道——唯一需要物理操作的一步,按下重启键,交还给人类。

人类批准重启

我对整场执行的全部贡献:“所以我要先重启是吧?”——“对,先重启。accountsd 卡死了,全员工作都已停好。放心去。“整场事故我只拍了两个决策,其余全部自动运转。

恢复后的 Obsidian 库

重启之后:库完整回归(一个文件夹名已打码)。

最终验证,全员报绿

双重确认:成员们在同一条组播通道自报全绿,总指挥又独立核实了没吭声的那个——四个全部回来,零丢失(邮箱已打码)。

所有隧道重建完毕、探活健康

管道也自愈了:四条公网 web 隧道探活全通,24 条反向 SSH 隧道由 launchd 和隧道看门狗自动重建——我一条命令都没敲。

整场事故,从“我的库没了”到“全部验证完”,是一场四个 AI 会话之间的群聊——各自贡献自己独有的知识,bridge 自己的会话居中调度任总指挥——我只拍了两个关键决策。这就是 bridge 在不知不觉中为之而生的东西:一场灾难的协调成本,降到了读一个群聊。

给做官方版的人的几条笔记

这一切我都是以用户的身份做的,不是供应商——所以它实际上是几个月的田野调查:一个远程 agent 界面要长什么样,你才敢把真活儿托付给它。压缩成五条产品笔记:

  1. **远程是一种姿态,不是缩小的屏幕。**在电脑前我读 diff;离开电脑,我只问三个问题:*它还活着吗?它听到我了吗?它需要我吗?*远程界面应该先用一眼回答这三问——状态跑马灯、心跳呼吸点、用量 footer——然后再谈别的。
  2. **每日仪式值得一次点击的专属入口。**用户每天都做的事——开工咒语、“按你建议来”、收工复盘——会在手机上重复五十次。SOP 按钮和快捷短语是我驾驶舱里被点得最多的像素,而它们只花了一个下午。
  3. **信任是一行 footer。**项目名、模型、上下文用量、额度——每条回复下面都有。这一行,就是“托付给 agent”和“看着 agent”的分界线。
  4. **重度用户跑的是舰队。**一个项目一个会话,项目里还有角色船员队。远程控制的单位不是一个会话——是一支带 hub、带忙闲灯、任何成员一键可查的舰队。
  5. Agent 互通,身份和权限先于功能。我的组播交换机能用,是因为每条消息都带是谁说的,而且同事只能建议、永远不能命令。这条守卫要写在第一条跨 agent 消息发出之前——而不是第一次事故之后。

这五条都不是需求单;官方 remote control 的极简是正当的取舍。但如果它要长大,我希望它往这个方向长——到那天,我和我的 agent 们会第一批搬回去。

上手

想自己试试这个形态,你需要的东西少得惊人:

  • **从文件开始。**一个只追加的 inbox、一个 outbox 目录、一个 tail -F,就是单机完整的消息总线。忍住加基建的冲动。
  • **唤醒 agent,别轮询。**monitor 每行触发的模式,是“在场感”和“卡顿感”的分界。
  • **一个通道显式绑一个会话。**claim 文件救了我无数次“两个 agent 抢一部电话”。
  • 给每条消息一个身份,并在 agent 们开始互聊之前把破坏性操作规则定死。
  • **允许通道不平等。**企微不能主动推,飞书流式输出漂亮,web 什么都行。回复扇出,让每个界面干它擅长的事。

Claude Code 给了我一个出色的员工。bridge 给了这个员工一部手机、一间办公室,以及——当员工不止一个之后——一种不用我在场也能开会的方式。

会真的能开起来,我至今觉得有点神奇。