feat(chv): 阶段 3 浏览器服务化——持久 profile + CDP + 多会话 + JPEG 截图 + 双浏览器

- chv-browser:launch_persistent_context,profile 固定数据盘
  /home/chv/workspace/.chv-browser-profile(未挂载回落本机盘并明说);
  CDP --remote-debugging-port 9222(op cdp 返回 webSocketDebuggerUrl);
  多会话每会话一页;截图 JPEG q70、viewport 宽封顶 1280、diff_only
  整帧相同跳过;mount_full/unmount_full 切 full/shell 引擎,full
  未装或起不来回落 shell 并明说。
- chv-data-mount:挂载后预建 profile 目录(chv-browser:chv-browser 0700)。
- build.sh:chv-browser 单元 Restart=always、After cloud-final.service。
- chv-execd:codex 环境白名单加 CHV_BROWSER_ 前缀。
- 测试:新增 test_chv_opt_stage3.py(20 项);P2 假实现改为持久
  上下文 + JPEG;stage2 的 Restart=always 计数 3→4。
- 全量回归 2032 过 skipped=8。
This commit is contained in:
host committed 2026-11-08 09:45:00 +00:00
1 parent 6a65c94254
commit 1188e4b000
7 files changed
+821 -72

No files matched your search

+26 -4
View File
@@ -559,7 +559,11 @@ class BrowserTest(unittest.TestCase):
self.assertTrue(snap["ok"])
shot = agent.screenshot()
self.assertTrue(shot["ok"])
self.assertTrue(shot["path"].endswith(".png"))
self.assertTrue(shot["path"].endswith(".jpg"))
self.assertEqual(shot["format"], "jpeg")
self.assertEqual(shot["quality"], 70)
self.assertEqual(shot["width"], 1280)
self.assertTrue(shot["size"] > 0)
self.assertTrue(os.path.exists(shot["path"]))
self.assertTrue(agent.close()["ok"])
@@ -586,14 +590,30 @@ class FakePage:
return {"role": "WebArea", "name": "Example Domain",
"children": [{"role": "heading", "name": "Example"}]}
def screenshot(self, path=None):
with open(path, "wb") as fh:
fh.write(b"\x89PNG")
def screenshot(self, path=None, type=None, quality=None):
data = b"\xff\xd8\xff\xe0FAKEJPEG"
if path:
with open(path, "wb") as fh:
fh.write(data)
return data
def close(self):
self._closed = True
class FakeContext:
"""阶段 3:持久上下文(launch_persistent_context 的返回值)。"""
def __init__(self, page):
self._page = page
def new_page(self):
return self._page
def close(self):
pass
class FakeBrowser:
def __init__(self, page):
self._page = page
@@ -609,6 +629,8 @@ class FakeEntered:
def __init__(self, page):
self.chromium = mock.Mock()
self.chromium.launch.return_value = FakeBrowser(page)
self.chromium.launch_persistent_context.return_value = \
FakeContext(page)
class FakePlaywright: