Files
workbuddy_skills/dsh-workflow/references/dsh-change-workflow/08-浏览器验证栈详解.md
T
admin e03465c398 按用户令提交:把此前未纳管的 9 个技能目录一并入库
用户令逐字:「E:/ProgramData/.workbuddy/skills  提交仓库是指的这里」——
即本目录就是仓库(2026-10-07 已在本目录建仓,见当日日志 §22),本轮把余下未纳管的 9 个技能一并提交。

本次入库(9 个技能,46 个文件):
1、`AI HOT`
2、`draw-ui`
3、`dsh-diagnose`
4、`dsh-knowledge`
5、`dsh-local-env`
6、`dsh-opensource-release`
7、`dsh-workflow`
8、`oil-motion`
9、`skills-security-check`

提交前核对:
· **凭据类扫描**(`*.env` / `*token*` / `*.key` / `*secret*` / `*.pem`)⇒ **零命中** ✓;
· 体积合计约 20 MB(`draw-ui` 12M + `oil-motion` 6.5M 是大头,形态为配图/素材 ——
  仓库 `.gitignore` 里明写「`assets/*.png` 是内容不是产物」⇒ 属刻意入库);
· 运行产物仍按既定规则排除(`__pycache__` / `logs/` / `tmp/` / `.venv/` / `*.egg-info` / `uv.lock` / `.workbuddy/`)。
2026-10-08 22:29:08 +08:00

7.2 KiB
Raw Blame History

浏览器验证栈详解 · browser-harness 调用 / Lexical 输入难点 / 独立 headless 定型做法 / 必踩坑

归属:技能 dsh-change-workflow 的详情档(按需读,不是每次都要读)。 本档覆盖:正确调用(Windows,2026-09-11 实测可用) · ⛔ dsh composer(Lexical)输入难点 · 三个必踩坑(原行 L231–L293)。 主文件 / 判据与流程主干 = ../SKILL.md(§1 六阶段流程 · §2 红线 R1–R8 原文速查 · §3 本机 Git Bash 环境坑 · §4 并行调度结论)。 来源:2026-09-22「技能重组线」把 ../SKILL.md 的 L231–L293 段逐行原样下沉到本文件,未改一字。 跨档引用:正文里的「§N / 见 §8 坑 N / 见下表」等编号,用 ../SKILL.md 末节「详情索引」的原章节列定位。 维护:本文件与 ../SKILL.md 的指针行成对;改内容时同时核对主文件的指针描述是否仍准确。


正确调用(Windows,2026-09-11 实测可用)

export PATH="/c/Users/Administrator/.local/bin:$PATH"      # 全局 console script 在这个目录
export BU_CDP_URL="http://127.0.0.1:9223"                 # 指向独立 headless 实例
env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy NO_PROXY="127.0.0.1,localhost" \
  browser-harness <<'PY'                                  # helper 已预导入;⛔ 别裸调 python -m
print(page_info())
PY
#  本 checkout(E:/ProgramData/.workbuddy/skills/browser-harness/)另有 dev 启动器:./browser-harness
#  🔴 2026-09-25 实测更正:旧写法 R="C:/Users/Administrator/.workbuddy/skills/browser-harness" +
#     "$R/.venv/Scripts/python.exe -m browser_harness.run" **已作废** —— C: 侧该目录不存在、本机 checkout 里也没有 .venv。

helper:js / click_at_xy / type_text / fill_input / press_key / switch_tab / close_tab / capture_screenshot / list_tabs / wait。

⛔ dsh composer(Lexical)输入难点(2026-09-11 未攻克)

  • composer 真身:div[contenteditable="true"][data-lexical-editor="true"][data-composer-input="true"](class uV2eYG_input)。
  • type_text() = Input.insertText → 绕过框架监听,Lexical 模型不更新 = 没输入。
  • fill_input()(逐字符 press_key + input/change)实测也未生效。
  • 真因:目标标签不是前台标签 → el.focus() 后 document.activeElement 仍是 BODY,CDP 鼠标/键盘到不了该标签; switch_tab + ensure_real_tab + Page.bringToFront 都没解决。
  • 对照:Playwright 的 page.click() + page.keyboard.type() 成功过(能真实发出消息并拿到回复)。 → 结论:要在独占浏览器里跑(不与用户标签争前台);传输层 bug 一律用 curl 验证,浏览器只用于视觉/交互确认。

⚠️ 工具选择口径(🔴 2026-09-25 用户明令 —— 这条优先于下面所有旧实测): 只允许一件工具 —— browser-harness(路径与调用见上)。此前「两件(含 agent-browser)」的口径作废。 ⛔ Playwright / playwright-core 全面禁止(用户 2026-09-13 明令)。 本机现状:agent-browser 的 daemon 起不来(2026-09-13 实测:open 挂住零输出,而 --version / node -e 正常;Chrome 153 已装) ⇒ 该件已禁用(见上条),此段只作背景。 browser-harness 必须用下面「独立 headless 实例」那套(至少先 list_tabs() 确认),绝不附着用户日常 Chrome。 拿不到浏览器时,静态页与 API 取数一律用 curl/WebFetch,UI 结构用「无浏览器 harness」验收(见阶段 4 第 8 条三层验收)。

⛔ agent-browser 2026-09-11 实测记录(该件已禁用**;此段仅作历史背景,❌ 不得据此恢复使用)**: ① 自带 Chromium 要从 storage.googleapis.com 下 196MB → 必然超时失败; ② 改用本机 Chrome + --cdp 时,环境里有 HTTP_PROXY=http://127.0.0.1:2349(WorkBuddy 服务代理), CLI 连 127.0.0.1 的 CDP 也走代理 → Timeout connecting to CDP (要先 env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy NO_PROXY="127.0.0.1,localhost" 才通); ③ 即使通了,标签/会话状态不一致:tab list 显示 [t1] about:blank,而 get url 报 login.html; open <实例子域> 不生效。→ 要浏览器就用下面这套 browser-harness。

核心教训:不要用用户日常 Chrome。 直接 new_tab 到用户正在用的浏览器会反复弹「允许远程调试?」——用户会烦。根因:绕过 wrapper / 启动器(直接用 python -m browser_harness.run)就不会设 BH_RUNTIME_DIR_SHARED=1,daemon 无法常驻复用 → 每次调用新建连接 → 每次弹一次授权。⇒ 2026-09-25 起统一走全局 console script browser-harness(或本 checkout 的 ./browser-harness),⛔ 不再裸调 python -m。

定型做法 = 起独立 headless 实例(零弹窗、不碰用户浏览器、cookie 隔离):

# ① 一次性启动(后台常驻;PATH 必须先修好,否则 rm/node 会失败导致 Chrome 根本没起)
export PATH="/e/ProgramData/.workbuddy/binaries/PortableGit/versions/1.2.0/usr/bin:/e/ProgramData/.workbuddy/binaries/PortableGit/versions/1.2.0/mingw64/bin:/c/Windows/System32:/c/Windows:$PATH"
"/c/Program Files/Google/Chrome/Application/chrome.exe" \
  --headless=new --remote-debugging-port=9223 --remote-allow-origins='*' \
  --user-data-dir='C:\Users\Administrator\AppData\Local\Temp\chrome-bh-dsh' \
  --no-first-run --no-default-browser-check --no-proxy-server --disable-gpu about:blank
# ② 探活:curl -s --noproxy '*' http://127.0.0.1:9223/json/version
# ③ 每次验证(把 python 脚本写成文件后重定向进 stdin,避免 heredoc 引号地狱)
export PATH="/c/Users/Administrator/.local/bin:$PATH"
export BU_CDP_URL="http://127.0.0.1:9223"
browser-harness < "E:/<你的工作区>/tmp/shot.py"      # 或 heredoc;脚本走 stdin

登录态:DB 直插临时 session(user_agent=poc-ui,用后即删),取 token 后 cdp("Network.setCookie", name="sid", value=TOKEN, domain=".ai1net.com", path="/", secure=True) → Page.reload。 用户侧真实浏览器完全不受影响(不覆盖其 sid)。

三个必踩坑:

  • Emulation.setDeviceMetricsOverride 按 target 生效 → 必须先 new_tab 再设覆盖;设在旧标签上再开新标签 = 覆盖丢失(截图只有 758×482)。
  • hash 残留会骗人:页面停在上次的 #/plugins/manual,重载后目标元素不存在 → js() 返回 null / 元素测量得 0/0。验证脚本必须显式导航到目标 tab。
  • Chrome 必须先 --headless=new 前台跑通再加后台;后台启动失败时先看 task 输出(常见是 PATH 未设导致前置 rm 失败,&& 短路,Chrome 从未执行)。
  • 收尾:临时 session 删除;独立 Chrome 可留着复用(内存小),要停就 kill 9223 端口对应进程。