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
+35
-43
No files matched your search
+34
-42
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user