docs(runbook): browser provider setup, takeover rules and real-instance checklist
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8574d34144
commit
f9b30b7e72
1 file changed
+122
@@ -0,0 +1,122 @@
|
||||
# 浏览器(camofox)与原站视图运行手册
|
||||
|
||||
对应 REQ v0.7 §4.4、§4.7.1–4.7.4;合同与 camofox OpenAPI 对照见 `docs/design/browser-provider.md`。
|
||||
代码:`packages/adapters/src/browser/**`(适配器)、`apps/server/src/modules/browser/**`(会话/接管/能力)、
|
||||
`apps/web/app/components/browser/**`(原站视图)。
|
||||
|
||||
> 当前状态:全部能力只对 testkit 的 mock camofox 验证过,**没有连接过真实实例**。界面上"画面与输入未对真实实例核验"
|
||||
> 这个标记会一直显示,直到按第 6 节完成核验并写入 `config.verified`。
|
||||
|
||||
## 1. 登记浏览器服务
|
||||
|
||||
设置页还没有浏览器服务的表单(留给 C1 之后)。当前用 API + SQL 登记:
|
||||
|
||||
```sh
|
||||
KEY="Authorization: Bearer <admin API Key 或 DEV_ADMIN_KEY>"
|
||||
# 1) 访问凭据(camofox 的 CAMOFOX_ACCESS_KEY)存进凭证库,拿到 secret id
|
||||
curl -s -XPOST localhost:4380/api/v1/secrets -H "$KEY" -H 'content-type: application/json' \
|
||||
-d '{"purpose":"other","name":"camofox-access-key","plaintext":"<CAMOFOX_ACCESS_KEY>"}'
|
||||
# 2) 登记服务(secret_ref 用上一步的 id;没设访问凭据就留 null)
|
||||
psql "$DATABASE_URL" -c "insert into browser_provider (name, kind, base_url, secret_ref, enabled)
|
||||
values ('camofox', 'camofox', 'http://camofox:9377', '<secret id>', true)"
|
||||
# 3) 探测:读 /health 与 /openapi.json,写回 capabilities / health_status
|
||||
curl -s "localhost:4380/api/v1/browser/providers?probe=true" -H "$KEY"
|
||||
```
|
||||
|
||||
设置页将来要做的字段:`base_url`、访问凭据(写 `secret_ref`)、`enabled`、只读的能力表(supported / verified / 上游版本)、
|
||||
下面的 `config` 可调项。
|
||||
|
||||
`browser_provider.config` 可调项(都可省略):
|
||||
|
||||
| 键 | 缺省 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| `verified` | `{}` | 人工核验通过的能力,如 `{"snapshot": true, "screenshot": true}`;探测永远不会把它置 true |
|
||||
| `navigationTimeoutMs` | 30000 | 单次导航超时 |
|
||||
| `maxRetries` | 2 | 导航只重试超时 / 网络 / 5xx / 503;401、白名单、控制权冲突不重试 |
|
||||
| `snapshotMaxBytes` | 524288 | 快照文本上限(超出截断并标 truncated) |
|
||||
| `requestTimeoutMs` | 30000 | 其它请求超时 |
|
||||
|
||||
站点域名白名单 = 站点 `base_url` 的主机(含子域)+ `site.config.browserDomains`(字符串数组,给跳转到独立下载域的站点用)。
|
||||
站点限流与快照 / 实时搜索共用同一个限流器(导航、开标签页、历史导航取令牌)。
|
||||
|
||||
## 2. 会话与控制权
|
||||
|
||||
- 会话按站点 / 账号隔离:`profileKey = umt-site-<siteId>-acct-<accountId|anon>`,同时作为 camofox `userId`
|
||||
(上游的登录状态隔离单元);`browser_session.id` 作 camofox `sessionKey`。同一 provider + profile 至多一个未关闭会话。
|
||||
- 数据库为准:服务重启后按 `browser_session` / `browser_tab` 恢复适配器内存,`needsSnapshot` 置 true,旧元素引用全部作废。
|
||||
上游回收的标签页(404)对账标 `lost`,返回 409"标签页已被浏览器服务回收"。
|
||||
- 控制者:`automation`(Agent/任务)→ `human`(人工接管)→ `automation` 或 `waiting`;`closed` 终态。
|
||||
|
||||
| 事件 | 结果 |
|
||||
| --- | --- |
|
||||
| 接管 `POST /browser/sessions/{id}/control/acquire` | 先把适配器控制者切到 human(新的自动化输入立即被拒),等该 profile 上进行中的请求收尾,再写 `browser_control_lease`(缺省 30 分钟) |
|
||||
| 他人已接管 | 409 |
|
||||
| 交还 `.../control/release` | 租约 released;会话回 automation(或 `to: "waiting"`);`needsSnapshot = true`;待处理事项关闭 |
|
||||
| 租约到期未交还 | 租约 expired,会话转 `waiting`(`lease_expired`),不自动从未知页面继续 |
|
||||
| Agent 快照看到验证码 / 二次认证 / Cloudflare 挑战 | 会话转 `waiting`(`captcha` / `challenge`),生成待处理事项(kind `other`,subject `browser_session`) |
|
||||
| Agent `browser.interact action=request_human` | 同上,原因 `agent_request` |
|
||||
| 站点停用 / 浏览器服务停用 | 下一次访问时会话关闭(`site_disabled` / `provider_disabled`) |
|
||||
|
||||
HTTP 输入类端点(navigate / click / type / scroll / select / 开关标签页 / 带 target 的 download)只接受持有自己人工租约的调用者;
|
||||
快照、截图、会话详情随时可读。截图只给原站视图,`modelSafe=false` 的不进模型。
|
||||
|
||||
## 3. Agent 能力
|
||||
|
||||
| 能力 | 类别 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `browser.snapshot` | read | 结构化快照(已脱敏);`sessionId` 或 `siteId`;清掉 `needsSnapshot` |
|
||||
| `browser.navigate` | execute(admin) | 站点域名内;没有会话时按站点打开 |
|
||||
| `browser.interact` | execute(admin) | click / type / select / scroll / press / request_human;登录字段用 `credential` 由服务端注入 |
|
||||
| `browser.download` | execute(organize.execute) | 捕获下载 → 暂存 → 解析种子、匹配本地资源;**不提交**,提交用 `download.plan` / `download.submit` |
|
||||
|
||||
execute 类需要持续授权(`automation_grant`)或审批。元素引用只来自最近一次快照:导航、点击、按键提交、控制权交接、
|
||||
服务重启之后,旧的 `eN` 报 409 `元素引用已失效…先重新读取页面`。
|
||||
|
||||
## 4. 下载产物
|
||||
|
||||
- 暂存:`DATA_DIR/tmp/browser-downloads/<artifactId>.bin`(目录 0700、文件 0600),超过 24 小时的在下一次捕获时清理。
|
||||
合同里只出现引用(`bytes.ref`),来源地址去掉 passkey。
|
||||
- 核验:bencode 解析、infohash v1/v2、私有标记;按 `source_release.infohash_v1/v2`,再按下载地址 `id` 参数匹配本站资源。
|
||||
- 提交:HTTP `POST /browser/sessions/{id}/download` 带 `submit` 时转调 `POST /acquisition-plans`(同一鉴权、计划、去重、
|
||||
缓存阈值、授权检查);被拒时产物照常返回,`note` 写拒绝原因。同一会话同一 sha256 第二次捕获标 `duplicate`,不再提交。
|
||||
- 浏览器没有媒体库写权限;提交链路下载的是站点适配器取到的种子,暂存文件只作核验证据。
|
||||
|
||||
## 5. 排障
|
||||
|
||||
| 现象 | 查 |
|
||||
| --- | --- |
|
||||
| 原站视图"没有启用的浏览器服务" | `browser_provider` 是否有 `enabled = true` 且 `kind = 'camofox'` 的行 |
|
||||
| 412"浏览器服务认证失败" / camofox 401 | 访问凭据与 `CAMOFOX_ACCESS_KEY` 是否一致;`GET /browser/providers?probe=true` |
|
||||
| 502 浏览器服务不可用 | camofox `/health`(503 = 恢复中);容器日志 |
|
||||
| 409 先接管会话再操作 | 界面点"接管";或租约已被他人持有 / 已过期 |
|
||||
| "连接中断" | 截图请求失败;看 API 日志 `component: browser` 的方法 / 路径 / 状态(日志不含正文与凭据) |
|
||||
|
||||
## 6. 真实实例核验清单(REQ §4.7.4)
|
||||
|
||||
先固定用户现有 camofox-browser 的**仓库、版本(`/openapi.json` 的 `info.version`)、启动方式与认证配置**,记在本节末尾。
|
||||
每项通过后把对应能力写进 `browser_provider.config.verified` 并更新 `verified_at`;不通过的保持 false,界面继续标"未核验"。
|
||||
不得用另一个浏览器窗口代替后宣称通过。
|
||||
|
||||
| # | 项目 | 做法 | 通过标准 | 对应 verified 键 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | 登录保持 | 观众 / 馒头 / HHClub / CHD 各一账号:原站视图接管登录;闲置超过 `SESSION_TIMEOUT_MS` 后再 `browser.snapshot` | 不需重新登录;过期时落到登录页、`loginRequired = true`,而不是空列表 | `sessions`、`persistentProfile` |
|
||||
| 2 | 截图 / 结构化快照 | 列表页、详情页、登录页各取 `screenshot` 与 `snapshot` | 截图格式(JSON base64 还是 image/*)、尺寸与坐标一致;快照 `eN` 引用可点;密码框值、Cookie、passkey 不出现;大页面 `offset` 分页正确 | `screenshot`、`snapshot` |
|
||||
| 3 | 列表 / 详情提取 | 快照 + `POST /extract`(属性带 `x-ref`)与现有 HTTP 适配器结果对比 | 标题、分类、副标题、促销、做种一致,原始字段不丢 | `extract` |
|
||||
| 4 | 文件下载捕获 | 详情页点"下载"后 `POST /browser/sessions/{id}/download` | `GET /tabs/{id}/downloads` 的字段形状(filename / data / size / status)与适配器兼容;sha256 与直接 HTTP 下载一致;进暂存核验,不直写媒体库 | `downloadCapture` |
|
||||
| 5 | 重启后会话 | 分别触发 `BROWSER_IDLE_TIMEOUT_MS` 回收与强制重启 camofox 容器 | 持久化 profile 恢复登录;旧 tabId 识别为 `lost`,不重放旧引用 | `persistentProfile`、`tabs` |
|
||||
| 6 | 人与 Agent 同标签页连续操作 | Agent 打开详情页 → 原站视图接管,在**同一标签页**点击、输入 → 交还 → Agent 重新 `snapshot` | 人看到的是同一会话同一页面;交还后 Agent 读到人工操作后的状态;下载不重复提交 | `takeover`、`interact` |
|
||||
|
||||
本适配器依赖、但 1.17.0 OpenAPI 未写明的行为,核验时一并确认(不符就改适配器,不改合同):
|
||||
|
||||
- `POST /tabs/{id}/click` 接受 `{x, y}` 视口坐标(原站视图点击);不支持时需要改走 VNC 插件或上游新增端点。
|
||||
- `POST /tabs/{id}/type` 不带 `ref`/`selector` 时输入到当前焦点;`submit` 与 README 的 `pressEnter` 哪个生效。
|
||||
- `POST /tabs/{id}/press`、`/scroll`、`/back`、`/forward`、`/refresh` 是否存在及请求体字段。
|
||||
- `DELETE /tabs/{id}` 的 `userId` 放 query 还是 body;`DELETE /sessions/{userId}` 是否关闭该 profile 全部标签。
|
||||
- `userId` 允许的字符与长度(`umt-site-<uuid>-acct-<uuid>`,约 90 字符)。
|
||||
- 性能:四站并发 1 / 2 / 4 会话的启动延迟、截图往返(原站视图 1.5 秒刷新是否跟得上)、内存与失败恢复时间。
|
||||
|
||||
核验记录(填写):
|
||||
|
||||
| 日期 | 仓库 / fork | 版本 | 启动方式 / 认证 | 通过项 | 备注 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| | | | | | |
|
||||
Reference in new issue
Block a user