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:
host committed 2026-11-07 07:02:37 +00:00
1 parent 581e850eb7
commit 179a86d655
2 files changed
+35 -43

No files matched your search

+34 -42
View File
@@ -2,9 +2,9 @@
"""chv-browser:浏览器子代理(客机侧、单元外独立进程)。
对齐 Muse P2(见 docs/chv-muse-p2-gap.md):浏览器任务由专门子代理
完成,与 codex 单元分离。子代理只向单元返回页面无障碍树快照
(Playwright accessibility.snapshot),不见原始 DOM,不能在页面
上下文跑 JS,不暴露 evaluate/content,不开 DevTools。
完成,与 codex 单元分离。子代理只向单元返回页面无障碍快照
(Playwright Page.aria_snapshot() 的 ARIA 快照字符串),不见原始
DOM,不能在页面上下文跑 JS,不暴露 evaluate/content,不开 DevTools。
操作(Unix socket /run/chv/browser.sock,JSON 行):
open {url} -> 导航并返回 {title, url, a11y, truncated}
@@ -64,43 +64,31 @@ def validate_url(url):
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):
"""截断无障碍树:节点数超上限时剪掉多余子树。返回 (tree, truncated)。
纯函数,可单测。节点计数按 dict 节点数(含 children 展开)。"""
state = {"count": 0, "truncated": False}
def walk(n):
if not isinstance(n, dict):
return n
state["count"] += 1
if state["count"] > max_nodes:
state["truncated"] = True
return None
out = {}
for key, value in n.items():
if key == "children" and isinstance(value, list):
children = []
for child in value:
walked = walk(child)
if walked is None:
state["truncated"] = True
break
children.append(walked)
out[key] = children
else:
out[key] = value
return out
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"]
"""截断 ARIA 快照字符串:节点行(行首 "- ")超上限时剪掉多余
行,字符超上限时截断。返回 (text, truncated)。纯函数,可单测。
round-11 复测纠正(第二轮):Playwright 根本没有
page.accessibility(那是 Puppeteer 的 API),原生快照是
Page.aria_snapshot() 返回的字符串;旧的 dict 版 truncate_a11y
随之废止。a11y 字段仍叫 a11y,值改为 ARIA 快照字符串。"""
text = text or ""
lines = text.splitlines()
kept = []
nodes = 0
truncated = False
for line in lines:
if line.lstrip().startswith("- "):
nodes += 1
if nodes > max_nodes:
truncated = True
break
kept.append(line)
out = "\n".join(kept)
if len(out) > max_chars:
out = out[:max_chars]
truncated = True
return out, truncated
def _load_playwright():
@@ -166,12 +154,16 @@ class BrowserAgent:
try:
title = self._page.title()
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:
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,
"a11y": tree, "truncated": truncated}
"a11y": aria, "truncated": truncated}
def screenshot(self):
if self._page is None: