dsh-builtin-browser 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.en.md +62 -491
  3. package/README.md +44 -470
  4. package/client.js +10 -10
  5. package/cordis.patch.yml +4 -4
  6. package/desktop-bridge/plugin-browser-bridge.js +1 -1
  7. package/docs/README.md +8 -4
  8. package/docs/architecture.md +82 -11
  9. package/docs/development.md +38 -0
  10. package/docs/limitations.md +49 -0
  11. package/docs/tool-reference.md +42 -42
  12. package/docs/user-guide.md +93 -37
  13. package/docs/why-browser.md +3 -3
  14. package/lib/browser/types.d.ts +2 -2
  15. package/lib/browser-electron/entry.d.ts +2 -2
  16. package/lib/browser-electron/entry.js +1 -1
  17. package/lib/browser-electron/history-store.d.ts +1 -1
  18. package/lib/browser-electron/history-store.js +1 -1
  19. package/lib/browser-electron/host-main.js +3 -3
  20. package/lib/browser-electron/provider.d.ts +9 -9
  21. package/lib/browser-electron/provider.js +14 -14
  22. package/lib/browser-electron/remote-host.js +1 -1
  23. package/lib/browser-electron/settings-store.d.ts +4 -4
  24. package/lib/browser-electron/system-browser.d.ts +60 -0
  25. package/lib/browser-electron/system-browser.js +257 -23
  26. package/lib/index.d.ts +1 -1
  27. package/lib/index.js +1 -1
  28. package/lib/tool-browser/element-fields.d.ts +4 -4
  29. package/lib/tool-browser/element-fields.js +6 -6
  30. package/lib/tool-browser/index.d.ts +6 -6
  31. package/lib/tool-browser/index.js +161 -139
  32. package/package.json +2 -2
  33. package/scripts/verify-host-compat.mjs +6 -6
  34. package/src/browser/types.ts +2 -2
  35. package/src/browser-electron/entry.ts +2 -2
  36. package/src/browser-electron/history-store.ts +1 -1
  37. package/src/browser-electron/host-main.ts +3 -3
  38. package/src/browser-electron/provider.ts +15 -15
  39. package/src/browser-electron/remote-host.ts +1 -1
  40. package/src/browser-electron/settings-store.ts +4 -4
  41. package/src/browser-electron/system-browser.ts +244 -16
  42. package/src/index.ts +1 -1
  43. package/src/tool-browser/element-fields.ts +6 -6
  44. package/src/tool-browser/index.ts +164 -140
package/CHANGELOG.md CHANGED
@@ -4,6 +4,80 @@
4
4
 
5
5
  ---
6
6
 
7
+ # 第四十五轮(2026-10-15,系统浏览器载体的启动生命周期:多开空白页 + 重启后孤儿)
8
+
9
+ 报告者给了一份很硬的证据链 —— CDP `/json/list` 每 200ms 采样的时序、五处建议改法、以及**一条自我更正**(他撤回的第一条建议:去掉启动参数里的 URL 会让 Chrome 变成两个窗口,更糟)。两个问题都发生"把浏览器拉起来"的那一刻,除此之外毫无关系。
10
+
11
+ ## 一、启动那个页面被丢弃,多出来的空白页无法关闭
12
+
13
+ **现象**:每次载体启动都看到 2 个空白标签,其中一个被关掉或残留;残留的那个**不在插件的标签模型里**(`dsh_browser_list_tabs` 只报 1 个),所以 `dsh_browser_close_tab` 关不掉它,窗口长期不关时会累积。
14
+
15
+ **根因两处**:
16
+ 1. `start()` 的启动参数末尾带 `about:blank`,而 Chrome 自己的启动页也在 —— 实测**干净退出后的全新启动也是 2 个页面**,所以不是崩溃恢复,是 Chrome 的固有行为(报告者做了对照实验排除,并保留了那句参数:去掉它会变成 2 个**窗口**)。
17
+ 2. `buildSession()` 用 `Target.createTarget({url:'about:blank'})` **新建**,而不是领养已经存在的那一个 → 启动那个被永久丢弃。
18
+
19
+ **修法**:连上后 `Target.setDiscoverTargets({discover:true})` + `Target.getTargets`,把无人认领的空白页收成**最多 1 个**(只关 `about:blank` 与 `chrome://newtab*`,且**绝不碰已在 `sessions` 里的**);`buildSession()` 先领养那个空白页,没有再新建。**`setDiscoverTargets` 不能省** —— 不先开发现,`getTargets` 可能列不出握手前就存在的页面,而**那正是需要关掉的那个**;报告者第一版补丁就是因此静默失效。
20
+
21
+ ## 二、DSH 重启后旧的载体浏览器成为孤儿,下一次 open 必定失败
22
+
23
+ **现象**:重启 dsh 后,上次拉起的 Chrome 仍在跑;第一次调用直接报 `exited immediately after launch (exit code 0)`,必须手动杀掉用该 profile 的进程才能恢复。
24
+
25
+ **根因**:`start()` 里"杀掉上一个浏览器"只在 `this.child !== undefined` 时执行,而 **DSH 重启后新进程里 `this.child` 是空的** → 旧实例没人管 → 新实例把请求移交给它然后退出 0 → 而这个退出被判成"启动失败"。
26
+
27
+ **修法**:`start()` **在 spawn 之前**先 `adoptRunningBrowser()` —— 读本 profile 的 `DevToolsActivePort`,要求端点自报家门(`/json/version` 的 `Browser` 字段 + `Browser.getVersion` 的 `product`,都必须像浏览器),通过就接管,**不 spawn**;启动回来是 hand-off(exit 0)时再试一次接管。
28
+
29
+ **为什么"接管"是安全的**:启动循环刻意不信任上一次留下的端口文件,理由正当(端口号会被复用到非浏览器进程上)。但**这里多两个前提**:文件在**本插件自己的 profile 目录**里(只有用这个 `--user-data-dir` 启动的浏览器会写它),而且端点要**两次**自证是浏览器。
30
+
31
+ **顺带撤销一处有害的"保护"**:spawn 前删除 `DevToolsActivePort` 被去掉了。它保护不到任何东西 —— `freshPort()` 本来就只认 spawn 之后写入的文件 —— 却销毁了"那个浏览器还在跑"的唯一记录,而那正是接管所需要的。测试里那条"过期文件必须已被删除"的断言随之反正。
32
+
33
+ ## 三、teardown:先请它自己关,再杀
34
+
35
+ `dispose()` 原本固定超时后 `kill()`,而 Windows 上那是 `TerminateProcess`:浏览器来不及写 profile,Chrome 会把这次结束记成崩溃 —— **下一次启动因此触发会话恢复,用户看到一个多出来的窗口**。现在先发 CDP `Browser.close`,给 `CLOSE_GRACE_MS`(6s,`unref` 的定时器,不拖住进程退出)让它自己走,仍在才杀。
36
+
37
+ ## 四、这一轮最该记住的事:同一个位置之前碰过三次,每次只修了一层
38
+
39
+ 用户的疑问是"我记得之前改过类似的,怎么又犯了"。查证后:**不是回归,是一个决定从来没被做过**。
40
+
41
+ | 轮次 | 当时解决的 | 当时没解决的 |
42
+ | --- | --- | --- |
43
+ | 第三十三轮 | "只认本次启动写下的端口"(mtime),不再误连到别的进程 | 那个**真浏览器**被留在原地没人管 |
44
+ | 第三十六轮 | `'exited'` 的文案按退出码区分,不再把崩溃码也说成残留进程 | **它已经准确描述了 hand-off 这个现象,却仍然把它当失败报出来** |
45
+ | 第三十七轮 | `forgetBrowserState` 覆盖"进程已退出、`this.child` 被清空"的形态,视图不再指向死浏览器 | 孤儿进程**仍然占着 profile** |
46
+
47
+ 三次都走到了同一个岔路口,三次都只修了自己那一面 —— **没人问"那浏览器还在跑,我们为什么不接上它"**。第三十六轮那句文案尤其值得记:它把 hand-off 描述得足够清楚,于是读到它的人和报告者都得出"去手动杀掉残留进程"的结论,而正确的结论是"这不该是一个错误"。
48
+
49
+ **测试**:254 → 260(新增 6 条生命周期守卫:接管先于启动、hand-off 读作"已经在那儿"、发现先于列举、只保留一个空白、领养而非丢弃、teardown 先请后杀)。`tsc` 干净。
50
+
51
+ ---
52
+
53
+ # 第四十四轮(2026-10-14,工具名空间:`browser_*` → `dsh_browser_*`)
54
+
55
+ 腾讯 BrowserSkill 的 DSH 插件(`@wxg-prc-cpg/browser-skill-dsh-plugin` @0.3.2)与本插件同装时,它六个工具一个都注册不上。根因不是它的 bug:双方都往**全局**工具层注册了 `browser_session`,宿主对重名直接抛错,而它把注册包在 `try/catch` 里只 `console.warn` —— 桌面版看不到 stdout,于是表现成"插件完全没反应"。用户先怀疑的是它坏了。
56
+
57
+ ## 改名
58
+
59
+ 33 个工具从 `browser_*` 移入 `dsh_browser_*`。判断依据是 issue 作者那句:**`browser_*` 是无前缀通用名空间,任何第三方浏览器插件都极可能撞上** —— 而这次六个名字里只撞了一个,就把对方整套拖下了水。
60
+
61
+ **这是破坏性变更**:文档、别人的提示词、肌肉记忆都要跟着改。**旧版本(0.4.7 及以前)不更新** —— 它们与旧版腾讯插件仍然撞,但那是已发布的事实,不做兼容层,靠 release notes 说明。
62
+
63
+ 对方上游(main 分支)也已把六个工具改名为 `bsk_*`,并把名字集中到 `tool-names.json`。两边各自划定名空间之后,同装互不干扰。
64
+
65
+ ## 一个名字不能再毁掉整套工具
66
+
67
+ 33 个工具原本是**顺序的直接注册**:第一个抛错会中断余下的 apply,用户看到的是一个"半存在"的插件——**那正是腾讯那个失败的同款形状**。现在每个调用点都走 `registerSafely`,碰撞变成一条点名报错的 WARN,其余工具照旧。
68
+
69
+ ## 两次做错的替换(自我更正)
70
+
71
+ 第一次用 PowerShell 的 `-replace` —— **它默认不区分大小写**,于是 `BROWSER_PROFILE_DIR` 和所有错误码常量一起被改名:`code === 'BROWSER_...'` 的比较全部失配,`BROWSER_PROFILE_DIR` 成了未定义标识符。tsc 当场抓到,但**测试从 250 掉到 218**(文件级崩溃)。
72
+
73
+ 第二次改用 `-creplace` 修常量,却踩了另一个:**PowerShell 5.1 把无 BOM 的 UTF-8 当 ANSI 读**,含中文的 36 个文件被双重编码,`client.js` 的界面文案全成了乱码——而 **tsc 依然通过**。
74
+
75
+ 两次都回滚重做,最终用一个 Node 脚本完成(默认区分大小写、默认 UTF-8)。**教训:批量改名这种操作,判据不能只有 tsc——它看不出编码损坏,也看不出语义错位。测试数从 250 掉到 218 才是真信号。**
76
+
77
+ **测试**:250 → 254(新增 4 条:命名空间完整性、无裸名残留、容错注册的形状、以及大写常量未被误改)。
78
+
79
+ ---
80
+
7
81
  # 第四十三轮(2026-10-12,三个"看起来无关"的缺陷:载体被写死、宿主死了不换、设置读了不生效)
8
82
 
9
83
  这一轮的共同点不是症状,而是**判断被做了一次就再也不做了**:载体在启动时选一次、连接端口在建立时记一次、`browser.channel` 在 apply 时读一次。三次都发生在"当时是对的"的情况下,三次的后果都是"之后永远不对"。