用户令逐字:「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/`)。
7.2 KiB
浏览器验证栈详解 · 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"](classuV2eYG_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 端口对应进程。