Skip to content

Releases: miqian-nomad/dsh-browser-playwright-codex

v0.7.4 — 缓存面板 + 有头/无头切换 + 无头提示修复

Choose a tag to compare

@miqian-nomad miqian-nomad released this 24 Oct 16:03

0.7.4 — 2026-10-23

一句话摘要:设置卡可以切换有头 / 无头 —— 运行时开关,不用改 YAML、不用重启 DSH;切换在下一次操作时生效(先导出登录态,再按新模式重开窗口)。默认仍是有头。

Added

  • 「浏览器窗口」开关:设置卡新增单选「显示浏览器窗口(有头)」/「后台运行(无头)」,选择存进 ~/.dsh/dsh-browser-playwright.state.json(与页面识别方式同一套 runtime-state),重启后仍记住。改完在下一次浏览器操作时重开窗口生效——不会打断正在进行的调用;代价是标签页和未提交的表单会丢,登录状态会先导出、不会丢。
  • POST /api/browser-playwright/window { headless }:校验布尔值,返回生效值与 changed;GET /state 同时返回 headless / headlessUserChosen / headlessConfigured,卡片据此显示「当前 / 你选的」。
  • 界面明确写清无头的代价:无头模式下「看得见、能随时接手」的前提不成立——验证码/二次验证只剩提示,没人能在窗口里替你点;只在无人值守且接受这个代价时用。

Changed

  • provider 按「期望模式 vs 实际启动模式」懒重开:desiredHeadless() 读运行时选择(无选择时回退 launch.headless);每次 acquire 前比较 launchedHeadless,不一致就先 dispose()(导出登录态、关窗)再按新模式启动。已经在用的 owner 切换模式也会被重建,而不是「下次重启才生效」。
  • launch.headless 仍是配置默认:配置里写什么就是没在卡片上选过时的默认值;两处启动路径(持久 context / legacy browser)都改为读解析后的值。

Fixed

  • 挑战页提示语随窗口模式变化:无头模式下原提示仍说「浏览器窗口就在你桌面上,等人去点」——那个窗口不存在。现在无头时改为明确说「HEADLESS、没有窗口可完成验证;要么把窗口模式切回『显示浏览器窗口』让人处理,要么换一种方式」,并保留「不要盲目重试」的纪律。有头模式的措辞不变。

Tests

  • 运行时状态:窗口模式持久化、configured 默认只在没有用户选择时生效(2 条)。
  • provider:desiredHeadless() 用户选择优先于 launch.headless(1 条,无浏览器启动)。
  • 真机验收:有头启动 → 切无头(重开、会话句柄替换)→ 切回有头,状态文件落盘。

完整迁移/行为说明见仓库 README 的 Window mode 与 Cache and disk 两节。

v0.7.0 — 一个插件:设置卡并入 + 成本/快照瘦身 + 凭据字段机制级拒绝

Choose a tag to compare

@miqian-nomad miqian-nomad released this 19 Oct 07:36

0.7.0 — 2026-10-19

一句话摘要:两个插件合成一个 —— 设置卡片并入本包;加载行从三行收成一行(这是 dsh-client-modules 允许本包带 client 模块的前提),独立的 dsh-browser-toggle 退役。

Breaking

  • 一个包一行 loader:cordis.patch.yml 现在只插 - id: browser / name: 'dsh-browser-playwright-codex',三块配置收进嵌套的 service / playwright / tool。旧的 browser-playwright、browser-tool 行不再被读取;loader 整体替换 config,迁移时三节都要写全(设置卡会自动重写 home 补丁层里的那一块)。
  • 版本跳到 0.7.0,因为配置形状变了。

Added

  • 设置卡随本包发布:dsh.client + ./client 导出,卡片仍在 /api/browser-playwright/* 上提供总开关、页面识别方式、两个强力工具闸门与已登录站点名单。
  • 单 loader 行守卫(tests/plugin-entry.test.ts):cordis.patch.yml 不得再出现本包的子路径行,否则卡片会重新变成非法。

Changed

  • 两个诊断工具改为门控:browser_console_messages / browser_network_requests 不再常驻 —— 新增 tool.diagnostics: false(默认关),开启后才注册。常驻成本 18 工具 12,755 字符 ≈ 3,543 token/轮 → 16 工具 11,005 字符 ≈ 3,057 token/轮(−486,−13.7%);全量注册仍是 21 工具 ≈ 4,384。
  • 快照瘦身(运行时大头):无名无状态的 generic 包装层不再逐层输出(真实页面常把一个链接包在 7~8 层 div 里),显示用 href 去掉追踪参数(utm_* / gclid / rsv_* / sa 等)并截到 160 字符。百度结果页形状的样本:6,169 → 2,557 字符(−58.6%),行数 108 → 24。refs 与点击行为不变 —— 省的是模型读到的东西。
  • 闸门写入块改为整行 config(新标记 # >>> dsh-browser:),并兼容旧的 dsh-browser-toggle 标记就地替换 —— 避免同一个 id 被两段 patch 争抢。
  • scripts/shipped-gates.mjs 按新行名读取出厂闸门;scripts/build-client.mjs 把 client/client.js 复制进 lib/。

完整迁移说明见 RELEASE-0.7.0.md。

v0.6.6 — 审查驱动的安全与一致性修复

Choose a tag to compare

@miqian-nomad miqian-nomad released this 17 Oct 15:09

0.6.6 — 2026-10-17

一句话摘要:四份独立审查(文档漂移 / 代码对抗 / 发行打包 / 中性冷读)查出的问题集中修掉——两条安全缺陷、几处"假成功/假失败"、随包自检的假承诺、发布门禁漏洞与一批文档漂移。

Security

  • 点击白名单可被祖先 <a> 绕过:browser_click 的 ref / text / precise 三条路径只校验被点元素自身的 href,外层 <a href="http://外站/"> 包住它时点击会导航到站外并返回成功。现在改为「自身 → 最近祖先 <a href>(穿 shadow host)」,与裸坐标路径同层,统一交给 URL 策略。两处独立审查各自用本地 fixture 复现过。
  • 密码/验证码/卡号曾进入 prompt:browser_extract 把 pageData() 拼进辅助模型的 prompt,而 pageData() 采集输入框当前值——登录页的明文密码会随请求发出去(实测 prompt 里出现 hunter2)。现在 type=password 与 autocomplete 为 current-password / new-password / one-time-code / cc-number / cc-csc / cc-exp 的字段一律置空值,字段本身保留(模型仍知道"这里有个码要填")。

Fixed

  • 填表核对两头错:上一版把回读改成严格相等后,页面自动改写格式的输入框(全大写、千分位 1,000)被误判成"没写进去",而 browser_fill 的描述恰恰把 input-mask / currency 列为预期场景;更早的版本则相反,ABCD→ZZZZ、John→Jane 会被判成功。现在的归一化只放过"页面改写、不是我们写错":NFKC + 去大小写 + 去掉非字母数字;1000≡1,000、abc≡ABC 通过,ABCD↔ZZZZ、1234↔12345 仍然失败。contenteditable 也不再假失败(改读 textContent/innerText)。
  • 挑战页识别的两处回归(本轮自查引入的):给控件层加"页面得够空"的门控后,正常登录页里嵌的真 reCAPTCHA / hCaptcha / Turnstile 完全不再识别(登录页正是主要场景),已移除该门控;CAPTCHA_ROLES 曾删掉 iframe,导致名字就叫「验证码」的非厂商 iframe 不被识别,已恢复。
  • [nothing here] 提示三类误报:静默页扫描深度 4 → 8 层(正文藏得深的正常页曾被判"几乎没有文字",与同一结果树里的正文自相矛盾);无标题的裸文档(about:blank、直接打开图片)不再提示;有挂起弹窗时不出这条(否则与 [dialog] 指令冲突)。
  • 提示互斥:已判定为拦截页时不再叠 [http NNN];已识别为挑战时不再叠行为兜底提示。

Added

  • 测试 159 → 220(+61):挑战识别 50 条(含真实页面测得的门控数值与反例:讲 hCaptcha 的文章、验证码说明 帮助链接、获取验证码 按钮、百度搜索页标题)、真浏览器端到端 6 条(真实 cf-mitigated 头、无签名拦截面被行为兜底抓住、interactiveOnly 下 iframe 仍识别、单页应用改 URL 后旧响应头不复用、真实 404 显示状态码、真实访客门显示静默页提示)、安全回归 5 条(外链祖先点击被拒、密码/OTP 不进 prompt、contenteditable 成功、同长度不同值失败)、本机路径守卫。

Changed

  • 随包自检不再在 npm 安装形态崩溃:scripts/dsh-browser-verify.mjs 的「lib/ 与 src/ 同步」一组在没有 src/ 时跳过并打印说明,退出码 0 —— 安装形态显示「全部通过 (56 项,跳过 1 项)」,仓库形态仍是「(57 项)」。README / CHANGELOG 此前承诺的"装上即可跑"现在才真的成立。
  • 依赖不再全是 *:@deepseek-ai/schemastery 固定 ^3.18.4,peer cordis >=4.0.4、dsh-llm / dsh-tools >=0.2.0-rc.1。修前空项目安装会解析到 dsh-tools 0.0.1-rc.1、dsh-llm 0.0.1-rc.5(远旧于测试基线);修后解析到 0.2.0-rc.2。代价:不同版本元组的预发布版(如 0.2.1-rc.*)会收到 peer 警告(不是失败,pnpm 默认不严格)。
  • 发布门禁补齐:CI 增加 npm run typecheck 与 npm run preflight(与 prepublishOnly 对齐);CI 与 publish 都新增「npm run build + git diff --exit-code -- lib」守卫——以前先 build 再比 mtime,src 改完忘重建也能全绿,而 dsh plugin add github:… 的安装路径会拿到旧 lib/。
  • 文档漂移:独立审查列出 13 条硬错误 / 11 条过期 / 8 条矛盾 / 8 条缺失,全部修掉——随包文档死链由 12 条清零(改 GitHub 绝对链接)、测试计数与成本数字按实际命令重取、npm test:live 与 browser-plugin/dev.cordis.yml 这类跑不通/不存在的引用纠正、URL 白名单不再声称覆盖 hover(实际不覆盖)、补写 snapshot.engine 说明,并在 中文说明.md 新增 §4.14 挑战页识别(四层判定与两条结果提示,明确"产出提示不是阻断,认不出来就停下来问人")。许可表述收敛为:代码与说明文档 MIT,CC BY-NC-SA 仅限 FOCUS-STEALING.md / MODE-TRADE-OFFS.md 两篇分析长文。
  • .gitignore 补登录态:dsh-storage-state.json / browser-profiles/ / profile/。默认写在 ~/.dsh/browser-profiles/(仓库外),但只要有人把 launch.profileDir 指进 checkout,那个文件里就是明文 cookie。

v0.6.5 — 挑战页识别分层判定 + 行为兜底

Choose a tag to compare

@miqian-nomad miqian-nomad released this 16 Oct 13:43

0.6.5 — 2026-10-16

一句话摘要:挑战页识别不再靠一份关键词表 —— 判定改成「厂商自有的机器信号优先,认不出来就问一句『页面变了吗』」,所以新出现的验证方式也能被接住;顺带清掉漏网的 4 处本机路径,并把 npm 包里 12 个用不上的开发脚本收成 2 个。

Changed

  • 挑战页识别重做成分层判定(新增 src/challenge-signatures.ts,重写 src/challenge.ts)。原来只有一份中英混杂的词表,而且词表里根本没有「验证码」「安全验证」「拖动滑块」这些中文说法 —— 也就是说 README 承诺的「遇到验证码会停下来」,在中文站点上大半不成立:百度系的「安全验证」、滑块页的「拖动滑块完成拼图」一个都识别不到,插件会当作普通页面继续点。现在按稳定性排序,而不是按覆盖率:
    1. 主文档响应头 —— 厂商对外的公开 API:cf-mitigated: challenge(Cloudflare 官方文档就是教用户读这个头)、x-amzn-waf-action: challenge(AWS WAF)、x-iinfo(Imperva)、x-datadome*(DataDome)。为此在 src/page-diagnostics.ts 已有的 page.on('response') 里认出主文档导航响应,把 status 与头透传进快照(BrowserSnapshot.response)。只认产生当前文档的那次响应:单页应用改了 URL 却没有文档加载时,旧响应不再复用 —— 否则会在一个好好的页面上报出墙;退回低层是安全的失败方向。
    2. 厂商控件自己的无障碍名 —— reCAPTCHA / hCaptcha / Turnstile / Arkose / PerimeterX 的 iframe 标题就是他们自己的产品名;非 iframe 节点上的同名 token 必须另有「请你验证」字样才算数,而且那份旁证语汇排除了它自己(reCAPTCHA 里就含 captcha,不排除的话这条守卫是空的 —— 第一版就是这样,被测试当场抓出来)。
    3. 文案(最后手段) —— 标题里的 安全验证 / Just a moment...,或控件上的命令式文案(请输入验证码、拖动滑块完成拼图);按「动词族」组合匹配而不是逐句枚举。只有控件角色(输入框 / 按钮 / 图片 / 滑块…)能凭「验证码」二字触发,验证码说明 这种帮助页链接不触发;获取验证码 / 发送验证码 这类正常登录控件也排除(另一个被测试抓出来的误判)。
    4. 行为兜底(不认识任何厂商) —— 动作之后 URL、标题、元素集合完全没变、且页面上几乎没有别的可点,就在这次结果里附一句「没有可见变化」,并明说这是猜测。这一层覆盖所有没有签名的厂商:新出现的验证方式靠它接住,代价只是多问用户一句,而不是拿账号去撞墙。
  • interactiveOnly 不再把 iframe 剪掉(src/injected.ts 与 src/snapshot-aria.ts 两个引擎都改):iframe 的无障碍名就是内嵌控件自己的标题,是第 2 层唯一的证据。原来这个省 token 的开关一开,等于把验证码识别关掉 —— 一个静默失效。
  • 已经认出是拦截面时,不再叠那句兜底提示:「这是墙、原因是什么」才是可执行的信息,再叠一句更含糊的警告只是多花 prompt。
  • 清掉仓库里漏网的 4 处本机绝对路径(README.md、PLUGIN-README.md、SNAPSHOT-RULES.md、scripts/autocommit.ps1)。0.6.1 声称清干净了,实际漏了这四处;而它正是外部评审用来判断「这个项目离开作者的电脑就转不动」的证据。这次改成脚本守:tests/doc-reality.test.ts 新增守卫,扫全仓手写文本文件里「盘符 + 至少一段目录」的路径(已用探针验证:三条真实路径全部抓到,仓库里唯一命中是给读者的 D:\path\to\… 占位符,由白名单排除)。
  • npm 包从 85 个文件收到 75 个:files 里那一整个 scripts/ 收成逐个文件的白名单,只留 doctor.mjs 与 dsh-browser-verify.mjs(这两个只依赖 lib/ 与 harness,装到 npm 的用户能直接跑)。顺带修掉一处假承诺:README 原来说 measure-prompt-cost.mjs 也随包可跑,但它依赖 src/ 与 shipped-gates.mjs,在装好的包里从来跑不起来。scripts/preflight.mjs 的 SHOULD_SHIP 现在钉住这两个脚本,防止以后再被顺手丢掉。
  • 新增上手一节「第一个任务」(README.md / PLUGIN-README.md / 中文说明.md):三条可以直接复制的话 + 你会看到什么 + 第一天两个习惯(先看再点、不可逆的那步说停)。四家外部评审独立提到「装完不知道第一步干什么、文档过载」,而 examples/ 目录一直不存在。

Added

  • tests/challenge.test.ts:35 条用例,正例与必须不触发的反例各占一半 —— 讲 reCAPTCHA 的文章、验证码说明 帮助链接、获取验证码 按钮、只是提到「请输入验证码」的长段落、普通搜索页、只有「安全」二字的标题。另有一条守卫:旁证语汇不得被任何厂商自己的名字满足。
  • tests/scenarios.test.ts:4 条真浏览器端到端。①真实 cf-mitigated: challenge 响应头一路走到渲染结果(钉住「监听 → 快照 → 判定 → 模型读到的文字」这条链,单测盖不住);②一个完全没有任何签名的拦截面(标题「请稍等」、一个点不动的按钮)由第 4 层兜底抓住,测试先断言 detectChallenge 返回 null,证明它不是靠签名抓到的;③interactiveOnly 下 iframe 名字仍在;④单页应用改 URL 后旧响应头不复用。
  • tests/snapshot-aria.test.ts:interactiveOnly 保留 iframe 的用例。
  • tests/doc-reality.test.ts:本机路径守卫(见上)。

Fixed

  • PlaywrightSession.blockedSnapshot 从未声明返回类型,于是 navigate() 的推断返回类型变成一个联合类型,新加的字段在类型上根本不存在。类型检查抓出来的,不是运行期 —— 这正是把 typecheck 扩到 tests/ 的价值。
  • 兜底指纹原来只读两层:深层文字变化会被误判成「页面没变」,于是在一个正常页面上报出「没有可见变化」的假警告。现在读到 4 层。

v0.6.1 — 修正发布包里的两处说明

Choose a tag to compare

@miqian-nomad miqian-nomad released this 16 Oct 11:55

0.6.1 — 只改说明,不改行为

这个补丁版的起因很直接:7 家外部模型(国产)在不给任何提示的条件下评审这个仓库,其中两家当场读出了包里与事实不符的地方。

  • SECURITY.md 的扫描数字更新为 2026-10-16 对 0.6.0 的实测:1 Critical / 3 Warning / 25 Info(80 个文件)(此前记的是 v0.5.3 的 1 / 3 / 22、75 个文件)。
  • 清掉仓库里的本机绝对路径:DIALOG-POLICY.md 的 D:\dsh-plugins\… 与 codex-rules.md 的 C:\Users\…\.codex\… 换成通用说法。
  • ci.yml 里写死的验收计数("(52 checks)",实际 57 项)改成由脚本自己打印 —— 从此不会再漂移。

代码一行未改,无行为变化。

v0.6.0 — 工具面三合一(破坏性)

Choose a tag to compare

@miqian-nomad miqian-nomad released this 16 Oct 09:41

0.6.0 — 破坏性变更:工具面 20 → 18

点击家族三合一:browser_click / browser_hover / browser_click_at 合并成一个 browser_click。
能力一个没丢,用两个开关表达:

原来 现在
browser_hover { ref | text } browser_click { mode: "hover", ref | text }
browser_click_at { ref } browser_click { ref, precise: true }
browser_click_at { x, y } browser_click { x, y }

常驻 prompt 净减 16.5%:现在是 18 个工具 12,755 字符 ≈ 3,543 token/轮;全量注册(含三个闸门工具)21 个。
规则一条没丢 —— 守卫从"三个工具各自必须声明哪些规则"改成"合并后的工具必须保住原来三个的全部规则"。

其余:

  • browser_fill 接受 value 作为 text 的别名(模型按语义猜参数名不再第一次就撞墙)。
  • 设置页新增只读的「已登录的站点」:只列你在浏览器里真的访问过的域,判据用 OWASP 的会话特征;
    不显示 cookie 的名字和值、不写磁盘、模型看不到;同一家公司的域折叠成一行,每行写明"这个域名是什么"。
  • GET /state 补上 x-dsh-plugin 防伪头(四个路由里唯一没校验的);新增 GET /logins 同样强制该头。
  • 类型检查扩到 tests/(新增 npm run typecheck,接进发版路径);新增"跨文档一致性"守卫。

完整说明见 CHANGELOG.md。

v0.5.5 — NOTICE.md 补进 npm 包 + 发版审计(preflight)挂到 prepublishOnly

Choose a tag to compare

@miqian-nomad miqian-nomad released this 11 Oct 13:34

0.5.5 — NOTICE.md 补进 npm 包 + 发版审计(preflight)挂到 prepublishOnly

补丁版本,运行时行为零变化。发这版的原因很具体:NOTICE.md 从来没随包发布过 —— 而 npm 只会自动带上 LICENSE,不会带 NOTICE。

Fixed

  • NOTICE.md 进包(package.json#files 之前没有它)。那份文件说明"MIT 衍生部分保持 MIT,只有本项目的分析性散文才是 CC BY-NC-SA",是归属声明,必须送到用户手里。现在包内 79 个文件,含 NOTICE.md。
  • 上游那个插件的 README 放在仓库根目录容易与本包混淆(它写着旧包名与旧版本号)→ 顶部加了一行说明。
  • 0.5.4 的 CHANGELOG 条目描述"哪里写错了"时把失效路径与失效行号照抄进正文,审计(正确地)把它们当成真引用 → 改为描述而不照抄。

Added

  • scripts/preflight.mjs(
    pm run preflight)
    ,挂在 prepublishOnly(�uild && test && verify && preflight):审计不通过就发不出去。
    检查包里装了什么(必备文件清单、lib/ 必须在、src// ests//.bak 必须不在),以及文档"关于世界的断言"(本包版本号、写死的工具数与总 token 数、旧包名、反引号路径、「文件:行号」越界、markdown 链接失效)。基线取自命令本身(
    pm run cost、
    ode --test)。输出分 ✗ 必须修 / ? 待确认 / ✓ 已核对。

验证

pm test 131 / 128 pass / 0 fail / 3 skipped|
pm run verify 57 项|严格档 sc 0 处|格式干净|
pm run preflight 0 处硬错误|
pm publish 时前置门禁全部自动跑过|发布后拆包核对:NOTICE.md / LICENSE / README / SECURITY / CHANGELOG / locale / cordis.patch.yml 均在包内,且不含 src/、 ests/。

v0.5.4 — 修掉第三方评审指出的「文档 ≠ 事实」

Choose a tag to compare

@miqian-nomad miqian-nomad released this 11 Oct 13:17

0.5.4 — 修掉第三方评审指出的"文档 ≠ 事实"

补丁版本,运行时行为零变化。必须发版的原因:README / SECURITY / NOTICE 都随包发布 —— 不发版,从 npm 装的用户看到的仍是 README 里那句"这个包不在 npm 上"。

Fixed

  • README 的 "This package is not on npm"(与同文件 # from npm 自相矛盾)→ "From npm (published since v0.5.1)" + npm 安装命令。
  • NOTICE.md 的 7 个失效 docs/... 路径 → 改为根相对路径;同段里指向上游文档的 docs/browser-safety.md 保持原样(第一次误改已还原)。
  • README 写死的 "2 Critical" → 不含数字的说法;SECURITY.md §2 与表格按当前扫描重写(1 Critical / 3 Warning / 22 Info,v0.5.3、75 文件),v0.4.9 的 2/5/26 作为历史留在引用块并注明以他们的报告为准。
  • SECURITY.md 的 src/playwright.ts (~:2041)(拆分后该文件仅 55 行)→ 改为模块名:行号会漂,模块名不会。
  • 三份发帖草稿里的"2 个 Critical" → "那个 Critical";docs/articles/focus-stealing.md、patches/README.md、codex-rules.md 里标明那些文件属于上游仓库。
  • README 新增 Compatibility 段:测过的 DSH 版本 �.2.0-rc.1、peer 故意留 *、升级后跑
    pm run doctor。

Added

  • ests/doc-reality.test.ts(5 条守卫):不在 npm 上 / README 必须有 npm 安装路径 / 反引号路径必须真的在 / 「文件:行号」不得越界 / 带数字的 N Critical 只允许出现在 SECURITY.md。每条都做了"有牙复检"。

验证

pm test 131 / 128 pass / 0 fail / 3 skipped|
pm run verify 57 项|严格档 sc 0 处|格式干净|check-doc-links.py 38 文件 0 坏链|CI windows + ubuntu 全绿。

v0.5.3 — 源码按职责拆开 + 入口 Config 修复 + 守卫补强

Choose a tag to compare

@miqian-nomad miqian-nomad released this 06 Oct 12:52

0.5.3 — 源码按职责拆开 + 入口 Config 修复 + 守卫补强

补丁版本,对用户可见行为零变化:工具面、配置接口、出厂默认值全部不动。

Changed

  • 源码按职责拆开:playwright.ts 2,727 → 55 行,只剩注册入口 �pply() 与一张模块地图。新增/收窄:provider.ts、session.ts、page-actions.ts、page-snapshot.ts、page-diagnostics.ts、page-probes.ts、window-focus.ts、�rowser-lifecycle.ts、login-state.ts、config.ts、url-policy.ts、challenge.ts、extract.ts、 ool-{types,schemas,render}.ts。
  • 12 个模块的文件说明与内容对齐(此前 6 处内容变了没改、5 处还留着拆分当天的占位文案)。

Fixed

  • 入口子路径恢复 Config 导出(./playwright):拆分时 schema 搬进 config.ts 而入口漏了转出 → Cordis 不补默认值 → provider 读 config.snapshot.engine 会崩。该回归只存在于 main、从未随版本发布。 现已加 6 条入口契约测试(含"不含 snapshot 的 profile 配置"这一故障场景、lib/ 与 src/ 行为一致性防漂移)。
  • 探测守卫覆盖不到 hitTest:它原本是
    esolveClickPoint 里的嵌套命名函数(同样会被 keepNames 改写成 __name),已搬进 page-probes.ts;守卫改为枚举该模块所有函数导出。
  • 中文说明写明两个闸门出厂关闭(原文写"…�llowEvaluate/�llowCdp 打开",与出厂 alse/ alse 矛盾,会让人误判权限状态)。
  • 文档守卫补洞:原规则要求 : true|false 才检查,措辞检查写在其后 → "…�llowCdp 打开"永不触发;现为两条带邻近性的规则,三种坏写法全部被抓住且不误报 SECURITY.md 的合法表述。
  • 测试导航预算 2500ms → 15s:冷启动 windows runner 连本机 fixture 页都超时,一次挂掉 4 条与超时无关的用例。
  • 修掉拆分暴露的 3 处隐患(DOM Request 类型身份、provider⇄session 运行时循环、测试/脚本旧路径引用),残留未用 import 由 28 处清到 0。

验证

pm test 126 / 123 pass / 0 fail / 3 skipped|
pm run verify 57 项|严格档 sc 0 处|格式干净|CI windows + ubuntu 全绿。

v0.5.2 — 修掉点击几何的转译器陷阱(Windows CI 抖动根因)

Choose a tag to compare

@miqian-nomad miqian-nomad released this 05 Oct 18:18

0.5.2 — 修掉点击几何里的「转译器陷阱」(Windows CI 抖动的真正根因)

补丁版本,全部是修复与测试:无新功能、无破坏性变更、工具面与配置接口不变。

根因:一句「批注」让几何点击路径从未工作过

测试用的转译器(tsx/esbuild)会开启 \keepNames\,把要序列化送进浏览器的页内函数里的命名内部函数改写成 __name(fn, "名字")\;而 __name\ 只存在于模块作用域 —— 页面里没有 → 探测每次抛 \ReferenceError\,又被 .catch(() => null)\ 吞成无声的 \unreachable\。

后果链:①几何点击路径从未成功过 → 所有点击退化成 Playwright 兜底点击(上限 3 秒);②慢速 Windows runner 上兜底超时 → 代码把几何路径那句误导性的错误抛出来(\could not find an unobstructed click point\);③于是两周以来只有 windows 挂、ubuntu 与本地全过。

证据(非推测):\ n.toString()\ 在 tsx 下实测出 \const describe=__name(n=>…,"describe")\;对照实验证明旧版普通点击走的是兜底路径;把失败原因记录后,错误里直接带出 \ReferenceError: __name is not defined\。

修复

  • 4 个页内函数改为对转译器免疫:\hitTest\ 内联描述逻辑;\elementStabilityProbe\ / \pageStabilityProbe\ / \labelTargetProbe\(含 \labelOf\ 内部的 \�ttr\)改用实测安全的数组解构 \const [a, b] = [() => …, () => …]\(自递归也验过)。
  • **测试运行器换成
    ode --test**(Node 原生类型剥离;CI 用 Node 24)—— 从根上移除此类陷阱。同一批测试在 tsx 下红、原生下绿(实测)。
  • 失败原因不再被丢弃:按信息量排序记录、在错误里点名、连探测抛出的错误文本也带上;「确实在屏幕外」不再借用「被遮挡」的措辞。
  • 点击探测点取「元素 ∩ 视口」的中心(\elementFromPoint\ 对视口外坐标返回
    ull\),并允许有限重采样(三次、间隔 60ms)。
  • 截图改用独立超时下限(\max(导航超时, 10s)\):全页截图耗时由页面大小与 CPU 编码决定,复用导航预算会让慢机器把成功捕获判成失败。
  • 修掉 \stale-ref journey\ 的竞态:它原来拿「最近浏览」也会异步插入的商品做断言,条件只在很短的窗口内成立。

效果

  • 点击 513ms → 58ms(几何路径第一次真正跑起来),\landingNote\ 变为 \click delivered at (239,218) — landed on button.filter "Kitchen" (the targeted element itself)\。
  • CI 从「windows 必挂」变为双平台全绿(连续两次:\99e4ea2\、\d3da76a\)。
  • 新增 5 条测试:几何路径「由谁送出点击」、按可见文字点击(此前整套测试从未调用过)、高元素、屏幕外报错、以及极简复现。

验证


pm test\ 117 · 114 pass · 0 fail · 3 skipped(两轮一致)|
pm run verify\ 57 项|
pm run doctor\ 通过|格式与链接自检干净|CI windows + ubuntu 全绿。