fix(chv-muse-r11fix): browser 快照改 Page.aria_snapshot()(复测新发现 2)
Playwright 1.64.1 的 Page 根本没有 accessibility 属性(那是 Puppeteer 的 API),open/snapshot 在真实库上必报 'Page' object has no attribute 'accessibility'。原生快照是 Page.aria_snapshot()(返回 ARIA 快照字符串),a11y 字段仍叫 a11y,值改为字符串;截断改字符串版 truncate_aria(200 节点 行/64KB)。FakePage 同步改真形状(旧 fake 自带 .accessibility 与真实库分叉,是与 sync_playwright bug 同款模式)。 wheel 1.64.1 实测:sync_api/_generated.py 无 def accessibility, Page 与 Locator 均有 aria_snapshot() -> str。
This commit is contained in:
1 parent
581e850eb7
commit
179a86d655
2 files changed
+34
-42
No files matched your search
+33
-41
@@ -2,9 +2,9 @@
|
|||||||
"""chv-browser:浏览器子代理(客机侧、单元外独立进程)。
|
"""chv-browser:浏览器子代理(客机侧、单元外独立进程)。
|
||||||
|
|
||||||
对齐 Muse P2(见 docs/chv-muse-p2-gap.md):浏览器任务由专门子代理
|
对齐 Muse P2(见 docs/chv-muse-p2-gap.md):浏览器任务由专门子代理
|
||||||
完成,与 codex 单元分离。子代理只向单元返回页面无障碍树快照
|
完成,与 codex 单元分离。子代理只向单元返回页面无障碍快照
|
||||||
(Playwright accessibility.snapshot),不见原始 DOM,不能在页面
|
(Playwright Page.aria_snapshot() 的 ARIA 快照字符串),不见原始
|
||||||
上下文跑 JS,不暴露 evaluate/content,不开 DevTools。
|
DOM,不能在页面上下文跑 JS,不暴露 evaluate/content,不开 DevTools。
|
||||||
|
|
||||||
操作(Unix socket /run/chv/browser.sock,JSON 行):
|
操作(Unix socket /run/chv/browser.sock,JSON 行):
|
||||||
open {url} -> 导航并返回 {title, url, a11y, truncated}
|
open {url} -> 导航并返回 {title, url, a11y, truncated}
|
||||||
@@ -64,43 +64,31 @@ def validate_url(url):
|
|||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
|
|
||||||
def truncate_a11y(node, max_nodes=MAX_A11Y_NODES,
|
def truncate_aria(text, max_nodes=MAX_A11Y_NODES,
|
||||||
max_chars=MAX_A11Y_CHARS):
|
max_chars=MAX_A11Y_CHARS):
|
||||||
"""截断无障碍树:节点数超上限时剪掉多余子树。返回 (tree, truncated)。
|
"""截断 ARIA 快照字符串:节点行(行首 "- ")超上限时剪掉多余
|
||||||
纯函数,可单测。节点计数按 dict 节点数(含 children 展开)。"""
|
行,字符超上限时截断。返回 (text, truncated)。纯函数,可单测。
|
||||||
state = {"count": 0, "truncated": False}
|
round-11 复测纠正(第二轮):Playwright 根本没有
|
||||||
|
page.accessibility(那是 Puppeteer 的 API),原生快照是
|
||||||
def walk(n):
|
Page.aria_snapshot() 返回的字符串;旧的 dict 版 truncate_a11y
|
||||||
if not isinstance(n, dict):
|
随之废止。a11y 字段仍叫 a11y,值改为 ARIA 快照字符串。"""
|
||||||
return n
|
text = text or ""
|
||||||
state["count"] += 1
|
lines = text.splitlines()
|
||||||
if state["count"] > max_nodes:
|
kept = []
|
||||||
state["truncated"] = True
|
nodes = 0
|
||||||
return None
|
truncated = False
|
||||||
out = {}
|
for line in lines:
|
||||||
for key, value in n.items():
|
if line.lstrip().startswith("- "):
|
||||||
if key == "children" and isinstance(value, list):
|
nodes += 1
|
||||||
children = []
|
if nodes > max_nodes:
|
||||||
for child in value:
|
truncated = True
|
||||||
walked = walk(child)
|
|
||||||
if walked is None:
|
|
||||||
state["truncated"] = True
|
|
||||||
break
|
break
|
||||||
children.append(walked)
|
kept.append(line)
|
||||||
out[key] = children
|
out = "\n".join(kept)
|
||||||
else:
|
if len(out) > max_chars:
|
||||||
out[key] = value
|
out = out[:max_chars]
|
||||||
return out
|
truncated = True
|
||||||
|
return out, truncated
|
||||||
tree = walk(node)
|
|
||||||
text = json.dumps(tree, ensure_ascii=False)
|
|
||||||
if len(text) > max_chars:
|
|
||||||
state["truncated"] = True
|
|
||||||
# 字符超限时只留顶层角色/名字/URL 信息,不灌整棵树。
|
|
||||||
tree = {"role": (node or {}).get("role"),
|
|
||||||
"name": (node or {}).get("name"),
|
|
||||||
"truncated_chars": True}
|
|
||||||
return tree, state["truncated"]
|
|
||||||
|
|
||||||
|
|
||||||
def _load_playwright():
|
def _load_playwright():
|
||||||
@@ -166,12 +154,16 @@ class BrowserAgent:
|
|||||||
try:
|
try:
|
||||||
title = self._page.title()
|
title = self._page.title()
|
||||||
url = self._page.url
|
url = self._page.url
|
||||||
tree = self._page.accessibility.snapshot()
|
# round-11 复测纠正(第二轮):Playwright 没有
|
||||||
|
# page.accessibility(Puppeteer 的 API),原生快照是
|
||||||
|
# Page.aria_snapshot()(字符串);a11y 字段仍叫 a11y,
|
||||||
|
# 值改为 ARIA 快照字符串(截断到 200 节点行/64KB)。
|
||||||
|
aria = self._page.aria_snapshot()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"ok": False, "error": "取快照失败:%s" % e}
|
return {"ok": False, "error": "取快照失败:%s" % e}
|
||||||
tree, truncated = truncate_a11y(tree or {})
|
aria, truncated = truncate_aria(aria or "")
|
||||||
return {"ok": True, "title": title, "url": url,
|
return {"ok": True, "title": title, "url": url,
|
||||||
"a11y": tree, "truncated": truncated}
|
"a11y": aria, "truncated": truncated}
|
||||||
|
|
||||||
def screenshot(self):
|
def screenshot(self):
|
||||||
if self._page is None:
|
if self._page is None:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ CDP 由单元外 broker 管理。
|
|||||||
| 单元内 codex 沙箱 | `host.py` 默认 `sandbox="workspace-write"`(bwrap) | 单元本身就是边界 | 单元内 codex 用 `danger-full-access`:bwrap 的 `--proc /proc` 挂载在嵌套命名空间里是已知走不通的形状(见 AGENTS.md 内层 proc 定位),单元内再套 bwrap 只会假隔离。执行器侧改 `--sandbox` 透传是 P1 的文件,本轨只定义接口(见 §4),不改 `executor.py` |
|
| 单元内 codex 沙箱 | `host.py` 默认 `sandbox="workspace-write"`(bwrap) | 单元本身就是边界 | 单元内 codex 用 `danger-full-access`:bwrap 的 `--proc /proc` 挂载在嵌套命名空间里是已知走不通的形状(见 AGENTS.md 内层 proc 定位),单元内再套 bwrap 只会假隔离。执行器侧改 `--sandbox` 透传是 P1 的文件,本轨只定义接口(见 §4),不改 `executor.py` |
|
||||||
| 单元网络 | 无(与客机共享网络命名空间) | 单元自有虚拟网络接口 | `--network-veth`:宿主侧 `ve-chv-cell`(10.200.0.1/24)、单元侧 `host0`(10.200.0.2/24,默认路由经 10.200.0.1),客机侧 NAT 出站(MASQUERADE 走客机默认路由的上行口,动态发现,不写死口名)。单元内配网经 nsenter 进单元 init 的 netns 做(`chv-cell.py` 内实现)。veth 建不起来即大声失败,不静默回落共享网络;调试逃生口 `CHV_CELL_NETWORK=shared` 须显式设置、打日志,绝不默认 |
|
| 单元网络 | 无(与客机共享网络命名空间) | 单元自有虚拟网络接口 | `--network-veth`:宿主侧 `ve-chv-cell`(10.200.0.1/24)、单元侧 `host0`(10.200.0.2/24,默认路由经 10.200.0.1),客机侧 NAT 出站(MASQUERADE 走客机默认路由的上行口,动态发现,不写死口名)。单元内配网经 nsenter 进单元 init 的 netns 做(`chv-cell.py` 内实现)。veth 建不起来即大声失败,不静默回落共享网络;调试逃生口 `CHV_CELL_NETWORK=shared` 须显式设置、打日志,绝不默认 |
|
||||||
| 单元内外 IPC | 无(SSH 直达,host key 钉死) | Unix socket + SO_PEERCRED + 对端 ACL 的最小权限 IPC | `chv-broker`(客机侧、单元外):Unix socket `/run/chv/broker.sock`(bind 进单元)。SO_PEERCRED 校验:对端 uid 须在单元映射范围 1000–66535,且对端 pid 的 pid ns 须与 `/run/chv/cell.pid` 记的单元 init 同 ns(cell.pid 不存在时只做 uid 检查,并打日志)。操作白名单:`ping`、`sync pull|push|status`(仓目录只允许 `~/workspace` 下,`chv-sync` 以 `chv` 身份跑,不进 shell)、`browser …`(转发给浏览器子代理)。其余一律拒绝 |
|
| 单元内外 IPC | 无(SSH 直达,host key 钉死) | Unix socket + SO_PEERCRED + 对端 ACL 的最小权限 IPC | `chv-broker`(客机侧、单元外):Unix socket `/run/chv/broker.sock`(bind 进单元)。SO_PEERCRED 校验:对端 uid 须在单元映射范围 1000–66535,且对端 pid 的 pid ns 须与 `/run/chv/cell.pid` 记的单元 init 同 ns(cell.pid 不存在时只做 uid 检查,并打日志)。操作白名单:`ping`、`sync pull|push|status`(仓目录只允许 `~/workspace` 下,`chv-sync` 以 `chv` 身份跑,不进 shell)、`browser …`(转发给浏览器子代理)。其余一律拒绝 |
|
||||||
| 浏览器 | Playwright headless-shell 由 agent 在客机内直接跑,能读原始 DOM、能跑 JS | 浏览器任务由专门浏览器子代理完成,只见无障碍树快照,不见原始 DOM,不能在页面上下文跑 JS,CDP 由单元外 broker 管理 | `chv-browser`(客机侧、单元外独立进程,用户 `chv-browser`):Unix socket `/run/chv/browser.sock`,操作只有 `open|snapshot|screenshot|close`。`open`/`snapshot` 返回标题、URL 与 Playwright `accessibility.snapshot()` 的无障碍树(截断到 200 节点/64KB);`screenshot` 落盘到 `/run/chv/shots/`(bind 进单元,单元只读路径取图)。**不暴露** `evaluate`、`content`(原始 DOM)、DevTools。单元内 playwright 包与浏览器二进制用 `--inaccessible` 遮掉,单元内跑不起自己的浏览器,只能走 broker |
|
| 浏览器 | Playwright headless-shell 由 agent 在客机内直接跑,能读原始 DOM、能跑 JS | 浏览器任务由专门浏览器子代理完成,只见无障碍树快照,不见原始 DOM,不能在页面上下文跑 JS,CDP 由单元外 broker 管理 | `chv-browser`(客机侧、单元外独立进程,用户 `chv-browser`):Unix socket `/run/chv-browser/browser.sock`,操作只有 `open|snapshot|screenshot|close`。`open`/`snapshot` 返回标题、URL 与 Playwright `Page.aria_snapshot()` 的 ARIA 快照字符串(截断到 200 节点行/64KB);`screenshot` 落盘到 `/run/chv/shots/`(bind 进单元,单元只读路径取图)。**不暴露** `evaluate`、`content`(原始 DOM)、DevTools。单元内 playwright 包与浏览器二进制用 `--inaccessible` 遮掉,单元内跑不起自己的浏览器,只能走 broker |
|
||||||
| 凭证 | `/etc/chv/guest-env`(0600 root)+ `~/.git-credentials`(0600 chv),codex 在客机内直接可读后者 | 凭证由 authd 保管,代理只见替身令牌 | 本轨只做“单元看不见”:`/etc/chv` 与 `~/.git-credentials` 都 `--inaccessible`,同步走 broker 的 `sync` 操作(broker 在单元外跑 `chv-sync`,token 不进单元)。替身化本身是 P3,不在本轨冒充已做 |
|
| 凭证 | `/etc/chv/guest-env`(0600 root)+ `~/.git-credentials`(0600 chv),codex 在客机内直接可读后者 | 凭证由 authd 保管,代理只见替身令牌 | 本轨只做“单元看不见”:`/etc/chv` 与 `~/.git-credentials` 都 `--inaccessible`,同步走 broker 的 `sync` 操作(broker 在单元外跑 `chv-sync`,token 不进单元)。替身化本身是 P3,不在本轨冒充已做 |
|
||||||
| 客机启动同步 | `chv-sync boot` 由 cloud-init runcmd 以 root 跑,clone 到 `~/workspace` | — | 不动:boot clone 仍在单元外做;单元内只经 broker 做 pull/push/status |
|
| 客机启动同步 | `chv-sync boot` 由 cloud-init runcmd 以 root 跑,clone 到 `~/workspace` | — | 不动:boot clone 仍在单元外做;单元内只经 broker 做 pull/push/status |
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user