Files
UHD_Media_Tool/docs/runbook/browser.md
T

123 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 浏览器(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 | 版本 | 启动方式 / 认证 | 通过项 | 备注 |
| --- | --- | --- | --- | --- | --- |
| | | | | | |