baa-conductor

git clone 

baa-conductor / docs / firefox
codex@macbookpro  ·  2026-04-01

README.md

  1# Firefox Control Protocol
  2
  3当前 Firefox 插件代码在:
  4
  5- [`../../plugins/baa-firefox/`](../../plugins/baa-firefox/)
  6
  7这份文档只描述当前 `baa-conductor` 里正式支持的 Firefox 插件能力。
  8
  9## 正式模型
 10
 11当前主线已经收口到四件事:
 12
 131. 每个平台维持一个空壳 shell tab,作为登录环境和同源请求来源环境
 142. 插件通过本地 `/ws/firefox` 上报登录态元数据、凭证指纹和端点元数据
 153. 真正的上游请求仍由浏览器本地代发,`conductor` 只负责调度、持久化元数据和暴露读面
 164. ChatGPT / Gemini 的最终 assistant message 会以 `browser.final_message` 形式做 raw relay
 17
 18除此之外,live delivery 仍保留一条受限的插件侧 DOM adapter:
 19
 205. `Claude` / `ChatGPT` / `Gemini` 的 text-only `inject_message` / `send_message` 会由插件 content script 内的 delivery adapter 执行
 21
 22页面对话 UI、消息回读、DOM 自动化不是浏览器桥接的正式主能力。`browser.final_message` 只负责最终文本原样转发,不在插件里做 BAA parser。当前保留的 `/v1/browser/{claude,chatgpt,gemini}/*` 只覆盖 helper / legacy 包装与辅助回读;delivery adapter 也只是 thin-plugin 交付面的受限执行层,不是通用页面自动化框架。
 23
 24## 固定入口
 25
 26- Local WS bridge: `ws://100.71.210.78:4317/ws/firefox`
 27- Local HTTP host: `http://100.71.210.78:4317`
 28
 29当前插件默认同时使用两条链路:
 30
 31- 本地 WS:负责 Firefox bridge 握手、浏览器元数据同步、服务端 `api_request` / `request_cancel``stream_*`
 32- 本地 HTTP:负责控制面状态同步,以及 `pause` / `resume` / `drain`
 33
 34插件管理页不再允许手工编辑地址。
 35
 36## 空壳页模型
 37
 38每个平台最多保留一个专用 shell tab:
 39
 40- Claude:`https://claude.ai/#baa-shell`
 41- ChatGPT:`https://chatgpt.com/#baa-shell`
 42- Gemini:`https://gemini.google.com/#baa-shell`
 43
 44这些标签页只承载:
 45
 46- 登录态
 47- 同源请求上下文
 48- endpoint 发现
 49
 50它们不再承载持久化会话语义,也不是服务端侧的正式业务主键。
 51
 52## `conductor` 侧会保存什么
 53
 54通过插件上报并由 `conductor` 持久化的字段:
 55
 56- `platform`
 57- `browser`
 58- `client_id`
 59- `host`
 60- `account`
 61- `credential_fingerprint`
 62- `captured_at`
 63- `last_seen_at`
 64- `freshness`
 65- `endpoints`
 66- `endpoint_metadata`
 67
 68明确不会持久化的内容:
 69
 70- 原始 `cookie`
 71- 原始 `token`
 72- 原始 header 值
 73- 页面会话内容
 74- 页面 UI 状态
 75
 76补充说明:
 77
 78- `browser.final_message.raw_text` 属于 live WS relay,不进入当前持久化表
 79- `browser.final_message` 现在会直接进入 conductor 侧 `BaaInstructionCenter`;普通消息若不含 ` ```baa ` 会被安全忽略
 80- 当前服务端会同时保留:
 81  - 活跃 bridge client 的最近最终消息快照
 82  - 持久化的 `instruction_ingest` / `execute` 最近历史(见 `GET /v1/browser``last_*` / `recent_*` 83- 当前 delivery 已改成 text-only:conductor 侧会直接渲染文本并负责超长截断;插件侧只承担受限的 `inject` / `send` adapter 执行,不负责 parser 或复杂编排
 84
 85`GET /v1/browser` 会把活跃 WS 连接和持久化记录合并成统一读面,并暴露 `fresh` / `stale` / `lost` 86
 87## 给 AI / CLI 的最短入口
 88
 89如果目标是浏览器桥接能力,推荐固定按这个顺序:
 90
 911. 先读 [`../api/business-interfaces.md`](../api/business-interfaces.md)
 922. 再调 `GET /describe/business`
 933. 再调 `GET /v1/browser`,确认需要的平台记录、`status` 和 `view`
 944. 如果目标是浏览器代发,先走 `POST /v1/browser/actions`、`POST /v1/browser/request`、`POST /v1/browser/request/cancel`
 955. 只有在需要 helper / legacy 包装或辅助回读时,才调 `/v1/browser/{claude,chatgpt,gemini}/*`
 96
 97不要把这条链路当成通用公网浏览器自动化服务;正式链路依赖 `mini` 本地 Firefox 插件和本地 `/ws/firefox` 98
 99## WS 上报模型
100
101插件启动后会先发送:
102
103```json
104{
105  "type": "hello",
106  "clientId": "firefox-ab12cd",
107  "nodeType": "browser",
108  "nodeCategory": "proxy",
109  "nodePlatform": "firefox"
110}
111```
112
113登录态元数据上报:
114
115```json
116{
117  "type": "credentials",
118  "platform": "claude",
119  "account": "user@example.com",
120  "credential_fingerprint": "fp-claude-demo",
121  "freshness": "fresh",
122  "captured_at": 1760000000000,
123  "last_seen_at": 1760000005000,
124  "headers": {
125    "cookie": "<redacted>",
126    "x-csrf-token": "<redacted>"
127  }
128}
129```
130
131端点元数据上报:
132
133```json
134{
135  "type": "api_endpoints",
136  "platform": "claude",
137  "account": "user@example.com",
138  "credential_fingerprint": "fp-claude-demo",
139  "updated_at": 1760000008000,
140  "endpoints": [
141    "GET /api/organizations",
142    "POST /api/organizations/{id}/chat_conversations/{id}/completion"
143  ],
144  "endpoint_metadata": [
145    {
146      "method": "GET",
147      "path": "/api/organizations",
148      "first_seen_at": 1760000001000,
149      "last_seen_at": 1760000008000
150    }
151  ]
152}
153```
154
155最终消息 raw relay:
156
157```json
158{
159  "type": "browser.final_message",
160  "platform": "chatgpt",
161  "conversation_id": "conv_demo",
162  "assistant_message_id": "msg_demo",
163  "raw_text": "@conductor::describe",
164  "observed_at": 1760000012000
165}
166```
167
168说明:
169
170- `headers` 只保留脱敏占位符,用于让服务端知道 header 名称和数量
171- 原始凭证值仍只停留在浏览器本地,用于后续同源代发
172- client 断开或流量长时间老化后,持久化记录仍可读,但会从 `fresh` 变成 `stale` / `lost`
173- `browser.final_message` 只在最终完成态上报完整文本,不会在 streaming 半截上报
174- 若平台暂时拿不到原生稳定 message id,插件会退化到等价稳定字段后再放进 `assistant_message_id`
175-`raw_text`` ```baa `,当前只会按 Phase 1 边界执行精确 target:
176  - `conductor`
177  - `system`
178- `conversation_id` 允许为空;当前 replay 去重至少覆盖 `platform + assistant_message_id + raw_text`
179- 当前 live 路径已经接到 text-only `inject / send` 闭环
180- 但插件侧 `inject / send` 仍是 DOM adapter,当前已对 `Claude` / `ChatGPT` / `Gemini` 做了选择器收口、page readiness 探测和有限重试;Gemini 兼容 `rich-text-field` / `rich-textarea` 的 Quill composer 变体
181- 如果页面未 ready、关键 selector 缺失或 send 点击后没有出现确认状态,adapter 会以 `delivery.<code>` 形式明确 fail-closed,不会把“实际没发出”的场景误标成成功
182- 超长文本当前默认只保留前 `200` 行,并在末尾追加 `超长截断`
183- 当前交付仍按任务边界停留在单客户端、单轮 delivery 首版
184
185## 浏览器本地代发
186
187当前正式对外 HTTP relay 已经收口到:
188
1891. `GET /v1/browser`
1902. `POST /v1/browser/actions`
1913. `POST /v1/browser/request`
1924. `POST /v1/browser/request/cancel`
1935. `POST` / `GET /v1/browser/{claude,chatgpt,gemini}/*`(helper / legacy 包装与辅助读)
194
195这条链路的关键边界:
196
197- 当前正式 relay 平台已支持 Claude、ChatGPT 和 Gemini;其中 Claude 保留 prompt shortcut,ChatGPT / Gemini 当前只支持显式 path 的 raw relay
198- `request` / `current` 通过插件已有的页面内 HTTP 代理完成,不是 DOM 自动化
199- `conductor` 不直接持有原始平台凭证
200- `responseMode=sse` 时,浏览器会通过 `stream_open` / `stream_event` / `stream_end` / `stream_error` 回传
201- `POST /v1/browser/request/cancel` 会向插件下发正式 `request_cancel`
202- 如果没有活跃 Firefox bridge client,会返回 `503`
203- 如果 client 还没有 Claude 凭证和 endpoint,Claude prompt helper 会返回 `409`
204- ChatGPT / Gemini raw relay 同样依赖真实浏览器里已捕获到的有效 header / 登录态;Gemini 现在会优先匹配持久化的会话级发送模板
205
206## 启动和管理页
207
208- Firefox 启动时,`background.js` 会确保 `controller.html` 存在
209- `controller.html` 启动后立刻连接 `ws://100.71.210.78:4317/ws/firefox`
210- `controller.html` 启动、刷新或扩展重载后,会自动恢复之前明确启用过、但当前缺失的 shell tab
211- 如果用户手工打开 Claude `new`、ChatGPT 根页或 Gemini `app` 这类平台根页,插件也会把它们纳入受管理 shell 集合
212- WS 断开后按固定间隔自动重连
213- `controller.html` 启动后也会立刻请求 `GET /v1/system/state`
214- HTTP 成功后按 `15` 秒周期继续同步
215- HTTP 失败后按 `1` 秒、`3` 秒、`5` 秒快速重试,再进入 `30` 秒慢速重试
216
217管理页当前只保留:
218
219- 本地 WS 状态卡片
220- 本地 HTTP 状态卡片
221- `暂停` / `恢复` / `排空` 按钮
222- 空壳页 / 账号 / 指纹 / endpoint 元数据面板
223
224## 验证
225
226最小自动 smoke:
227
228- `./scripts/runtime/browser-control-e2e-smoke.sh`
229
230这条 smoke 现在覆盖:
231
232- `GET /v1/browser` 上的元数据上报
233- 持久化记录在断连和重启后的可读性
234- `fresh` / `stale` / `lost` 状态变化
235- 读接口不泄露原始凭证值
236- Claude / ChatGPT 通用 browser request / cancel、正式 SSE 和 Claude legacy wrapper 的最小浏览器本地代发闭环
237- ChatGPT / Gemini 的 `browser.final_message` raw relay 与最近快照可见性
238
239随后插件会继续上送:
240
241- `credentials`
242- `api_endpoints`
243- `browser.final_message`
244- `client_log`
245- `api_response`
246- `stream_open`
247- `stream_event`
248- `stream_end`
249- `stream_error`
250
251其中:
252
253- `credentials` / `api_endpoints` / `api_response` / `stream_*` 属于当前正式 bridge 合同
254- `network_log` / `sse_event` 仍是插件侧诊断数据,不属于当前服务端正式承诺的 runtime message
255
256同时也会消费服务端下发的:
257
258- `hello_ack`
259- `state_snapshot`
260- `open_tab`
261- `plugin_status`
262- `ws_reconnect`
263- `controller_reload`
264- `tab_restore`
265- `api_request`
266- `request_cancel`
267- `request_credentials`
268- `reload`
269- `action_result`
270- `error`
271
272其中 `api_request` 仍然走统一 proxy 通道;Claude legacy message 只是兼容包装,正式主模型已经回到通用 browser request / cancel / SSE。
273
274## 验收建议
275
276### 0. 合同 smoke
277
278先跑仓库内最小闭环:
279
280```bash
281./scripts/runtime/browser-control-e2e-smoke.sh
282```
283
284它会在临时 runtime 上覆盖:
285
286- `GET /v1/browser` 的元数据上报与合并读面
287- 持久化记录在断连和重启后的可读性
288- `fresh` / `stale` / `lost` 状态变化
289- 读接口不泄露原始凭证值
290- 通用 browser actions / request / cancel、正式 SSE 和 Claude legacy wrapper 的最小浏览器本地代发闭环
291
292### 1. 元数据与持久化
293
2941. 安装插件并打开 `https://claude.ai/`
2952. 手工登录 Claude
2963. 手工发一条真实消息
2974. 在管理页确认:
298   - Claude 有有效凭证
299   - 已发现 `/chat_conversations``/completion` endpoint
3005. 请求 `GET /v1/browser?platform=claude`
3016. 确认返回:
302   - `records[0].view``active_and_persisted`
303   - 返回里只有 `account`、凭证指纹、端点元数据和状态
304   - 没有原始 `cookie`、`token` 或 header 值
305
306### 2. 断连与状态变化
307
3081. 断开插件 WS,或停止管理页连接
3092. 轮询 `GET /v1/browser?platform=claude`
3103. 确认:
311   - 记录仍然可读
312   - `view` 变成 `persisted_only` 或等价离线视图
313   - `status` 会从 `fresh` 变成 `stale`
3144. 继续等待老化窗口
3155. 确认同一条记录进一步进入 `lost`
316
317### 3. Browser relay
318
3191.`POST /v1/browser/actions` 打开或聚焦 Claude shell tab
3202.`POST /v1/browser/request`
3213. 确认请求经 `/ws/firefox` 转给浏览器本地代理,并收到 Claude API 回包
3224. 再调 `POST /v1/browser/request` + `responseMode=sse`
3235. 确认浏览器回传 `stream_open` / `stream_event` / `stream_end`
3246. 如需终止 in-flight request 或流,调 `POST /v1/browser/request/cancel`
3257. 如需 helper 辅助回读或旧调用方兼容,再调 `/v1/browser/{claude,chatgpt,gemini}/*`
326
327### 4. 控制面
328
3291. 点击 `暂停`,确认 HTTP 状态里的 `mode` 变成 `paused`
3302. 点击 `恢复`,确认 `mode` 变成 `running`
3313. 点击 `排空`,确认 `mode` 变成 `draining`
332
333## 已知限制
334
335- Gemini 当前只保留空壳页和元数据上报,不在正式 `/v1/browser/*` relay 合同里
336- 必须先在真实 Claude 页面里产生过请求,插件才能学到可用凭证和 `org-id`
337- ChatGPT 当前没有 Claude 那种 prompt shortcut / conversation helper;正式支持面只覆盖显式 `path` 的 raw request / SSE / cancel
338- `browser/request`、`claude_send` / `claude_current` 走浏览器本地 HTTP 代理,不会驱动 Claude 页面 DOM,也不会把页面会话历史持久化到 `conductor`
339- 当前 `/v1/browser/claude/current` 只是辅助回读最近一段 Claude 状态,不提供长期历史合同
340
341## 相关文件
342
343- [`../../plugins/baa-firefox/controller.js`](../../plugins/baa-firefox/controller.js)
344- [`../../plugins/baa-firefox/background.js`](../../plugins/baa-firefox/background.js)
345- [`../../plugins/baa-firefox/content-script.js`](../../plugins/baa-firefox/content-script.js)
346- [`../../plugins/baa-firefox/page-interceptor.js`](../../plugins/baa-firefox/page-interceptor.js)