跳转到内容

架构:本地原生主机(后端)

Browy 的后端是一个小型 Node.js 进程,以原生消息主机的形式注册到 Chrome / Edge / Brave 上,应用 id 是 com.browy.host。它运行在你的 机器上。

┌───────────────────┐ 原生消息 ┌───────────────────┐ 子进程 ┌───────────────────┐
│ 浏览器扩展 │ ◄───────────► │ Browy 主机 │ ◄───────────► │ GitHub Copilot │
│ (侧边栏, │ port │ (Node,本文 │ │ CLI / SDK │
│ devtools 面板) │ │ 介绍) │ │ (LLM 桥) │
└───────────────────┘ └───────────────────┘ └───────────────────┘
┌───────────────────┐
│ CDP 走 │
│ chrome.debugger │
│ (回到扩展里执行) │
└───────────────────┘

扩展和主机之间通过一条双向、长度前缀的 JSON 通道通信(Chrome 的 原生消息协议)。智能体决定调用的每一个工具都会作为 tool_call 消息 返回给扩展;扩展执行它(通常通过 chrome.debugger + CDP),然后 回发一个 tool_result

`src/native-host.ts`

进程入口。负责 stdio 分帧、日志文件、每个 port 一个的 Runner 实例,以及登出 / 登录辅助逻辑。

`src/agent/loop.ts`

智能体循环。调用 Copilot SDK(createSession / resumeSession), 把 prompt 和工具清单喂给它,把助手文字和 tool_call 事件流式 回传到扩展,把工具结果路由回 SDK。

`src/agent/tools/browser.ts`

浏览器工具登记表:大模型可以调用的每一个工具(snapshotclicktypenetwork_log 等)。每个工具直接和它的 JSON schema 一起声明,加上一个薄的 handler,把调用通过 port 转发给扩展。

`src/runner.ts`

按 port 的生命周期。每个连上的 port 一个 Runner(每个侧边栏 / DevTools 面板一个)。拥有自己的 session id、模型、未完成的工具调用。

`src/transports/`

Stdio 分帧(stdio.ts)和测试桥,让同一个主机能在不需要 Chrome 的情况下跑单元测试。

`src/cli-bin.ts`

独立入口:browy 二进制。当前是安装 / 注册 / 反注册的 CLI。

  1. 用户在侧边栏输入并按发送。
  2. 扩展通过 port 发出 {type: 'user', text, sessionId}
  3. 主机的 Runner 调用 sdk.resumeSession(sessionId, ...)(首次 调用是 createSession),带上消息和配置好的工具列表。
  4. SDK 流式返回助手文字和工具调用。主机把文字块({type: 'delta'}) 和工具调用({type: 'tool_call'})实时转发给扩展。
  5. 扩展执行每个工具(snapshotchrome.debugger → CDP Accessibility.getFullAXTree 等),然后发出 {type: 'tool_result', id, result}
  6. 主机把结果交还给 SDK;循环继续,直到模型说自己做完了。
  7. 主机发 {type: 'turn_done'}

整个交换在模型那一侧是同步的;在用户这一侧是实时流式的。

Browy 把会话存储委托给 GitHub Copilot CLI 自己的会话机制(在 ~/.copilot/)。主机在扩展端(不在主机端)的 chrome.storage.local 里维护一份 extensionChatId → copilotSessionId 的映射,这样恢复 某一段对话时能命中正确的 Copilot 会话。

智能体自己也有几个临时面:

  • ~/.browy/data/files/:沙箱化的文件临时空间,save_file / read_file 智能体工具操作的就是这个目录
  • ~/.browy/data/notes.json:跨对话的持久键值记忆
  • ~/.browy/host/host.log:单文件循环 5 MB 日志

工具定义在 src/agent/tools/browser.ts。每一个工具声明:

  • namedescription、JSON-schema parameters(SDK 会消费)
  • handler(input, ctx):在主机里运行,通常把调用通过 ctx.port.send({type: 'tool_call', …}) 转发给扩展,等待匹配的 tool_result 回复

也就是说,每个浏览器工具的执行发生在扩展里(页面所在的地方), 不在主机里。主机是调度者,是连接大模型的桥。

src/agent/loop.ts 在每次连接时构造交给 SDK 的工具清单。今天它是 完整的浏览器工具列表;v0.1.2 起会按用户的 设置 → 工具开关过滤。

src/native-host.tsconsole.* 重定向到单文件 ~/.browy/host/host.log。文件大小超过约 5 MB 时轮转(重命名为 host.log.1,重开一个)。一次只保留一份轮转;下一次轮转会覆盖旧的。

会话出问题时这是第一个要看的地方。它装的是主机的诊断输出,不是 对话记录。