dsh-web-search-pro 0.2.0 → 0.2.3

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 (266) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/README.md +197 -32
  3. package/assets/skills/dsh-web-search-pro/SKILL.md +14 -13
  4. package/assets/skills/dsh-web-search-pro/references/config.md +36 -0
  5. package/assets/skills/dsh-web-search-pro/references/sources.md +19 -4
  6. package/assets/skills/dsh-web-search-pro/references/troubleshooting.md +3 -1
  7. package/catalog/sources.v1.json +22 -12
  8. package/lib/actions/approval.js +50 -1
  9. package/lib/actions/approval.js.map +1 -1
  10. package/lib/actions/config.js +274 -0
  11. package/lib/actions/config.js.map +1 -0
  12. package/lib/actions/errors.js +8 -1
  13. package/lib/actions/errors.js.map +1 -1
  14. package/lib/actions/format.js +10 -0
  15. package/lib/actions/format.js.map +1 -1
  16. package/lib/actions/index-view.js +2 -2
  17. package/lib/actions/index-view.js.map +1 -1
  18. package/lib/actions/read.js +7 -3
  19. package/lib/actions/read.js.map +1 -1
  20. package/lib/actions/registry.js +3 -1
  21. package/lib/actions/registry.js.map +1 -1
  22. package/lib/actions/run.js +2 -2
  23. package/lib/actions/run.js.map +1 -1
  24. package/lib/actions/schema.js +18 -0
  25. package/lib/actions/schema.js.map +1 -1
  26. package/lib/actions/search.js +12 -6
  27. package/lib/actions/search.js.map +1 -1
  28. package/lib/actions/sources.js +116 -41
  29. package/lib/actions/sources.js.map +1 -1
  30. package/lib/actions/types.js +19 -1
  31. package/lib/actions/types.js.map +1 -1
  32. package/lib/browser-access.js +25 -0
  33. package/lib/browser-access.js.map +1 -1
  34. package/lib/catalog/recommend.js +11 -3
  35. package/lib/catalog/recommend.js.map +1 -1
  36. package/lib/catalog/schema.js +3 -1
  37. package/lib/catalog/schema.js.map +1 -1
  38. package/lib/cli/builtin-specs.js +341 -0
  39. package/lib/cli/builtin-specs.js.map +1 -0
  40. package/lib/cli/chain.js +193 -0
  41. package/lib/cli/chain.js.map +1 -0
  42. package/lib/cli/chains-spec.js +118 -0
  43. package/lib/cli/chains-spec.js.map +1 -0
  44. package/lib/cli/engine.js +99 -0
  45. package/lib/cli/engine.js.map +1 -0
  46. package/lib/cli/opencli-sites.js +83 -0
  47. package/lib/cli/opencli-sites.js.map +1 -0
  48. package/lib/cli/opencli.js +153 -0
  49. package/lib/cli/opencli.js.map +1 -0
  50. package/lib/cli/probe.js +128 -0
  51. package/lib/cli/probe.js.map +1 -0
  52. package/lib/cli/runner.js +564 -0
  53. package/lib/cli/runner.js.map +1 -0
  54. package/lib/cli/spec.js +496 -0
  55. package/lib/cli/spec.js.map +1 -0
  56. package/lib/cli/time-text.js +79 -0
  57. package/lib/cli/time-text.js.map +1 -0
  58. package/lib/cli/trial.js +57 -0
  59. package/lib/cli/trial.js.map +1 -0
  60. package/lib/client/SettingsCard.js +14 -32
  61. package/lib/client/SettingsCard.js.map +1 -1
  62. package/lib/client/agent-mark.js +27 -0
  63. package/lib/client/agent-mark.js.map +1 -0
  64. package/lib/client/card-frame.js +118 -0
  65. package/lib/client/card-frame.js.map +1 -0
  66. package/lib/client/cli-form.js +257 -0
  67. package/lib/client/cli-form.js.map +1 -0
  68. package/lib/client/field-docs.js +130 -0
  69. package/lib/client/field-docs.js.map +1 -0
  70. package/lib/client/field-keys.js +81 -0
  71. package/lib/client/field-keys.js.map +1 -0
  72. package/lib/client/field-rows.js +71 -0
  73. package/lib/client/field-rows.js.map +1 -0
  74. package/lib/client/fill.js +3 -0
  75. package/lib/client/fill.js.map +1 -0
  76. package/lib/client/form-specs.js +15 -1
  77. package/lib/client/form-specs.js.map +1 -1
  78. package/lib/client/form.js +296 -27
  79. package/lib/client/form.js.map +1 -1
  80. package/lib/client/group-advanced.js +23 -0
  81. package/lib/client/group-advanced.js.map +1 -0
  82. package/lib/client/group-agent.js +28 -0
  83. package/lib/client/group-agent.js.map +1 -0
  84. package/lib/client/group-cli.js +136 -0
  85. package/lib/client/group-cli.js.map +1 -0
  86. package/lib/client/group-common.js +29 -0
  87. package/lib/client/group-common.js.map +1 -0
  88. package/lib/client/group-judge.js +67 -0
  89. package/lib/client/group-judge.js.map +1 -0
  90. package/lib/client/group-order.js +201 -0
  91. package/lib/client/group-order.js.map +1 -0
  92. package/lib/client/group-runtime.js +33 -0
  93. package/lib/client/group-runtime.js.map +1 -0
  94. package/lib/client/group-sources.js +83 -0
  95. package/lib/client/group-sources.js.map +1 -0
  96. package/lib/client/groups-basic.js +36 -0
  97. package/lib/client/groups-basic.js.map +1 -0
  98. package/lib/client/index.js.map +1 -1
  99. package/lib/client/json-list.js +50 -0
  100. package/lib/client/json-list.js.map +1 -0
  101. package/lib/client/locales.js +666 -215
  102. package/lib/client/locales.js.map +1 -1
  103. package/lib/client/order.js +192 -0
  104. package/lib/client/order.js.map +1 -0
  105. package/lib/client/overlay-ops.js +46 -0
  106. package/lib/client/overlay-ops.js.map +1 -0
  107. package/lib/client/sources-table.js +1 -0
  108. package/lib/client/sources-table.js.map +1 -1
  109. package/lib/client/styles.js +74 -47
  110. package/lib/client/styles.js.map +1 -1
  111. package/lib/client/text-view.js +98 -0
  112. package/lib/client/text-view.js.map +1 -0
  113. package/lib/client/ui-kit.js +120 -0
  114. package/lib/client/ui-kit.js.map +1 -0
  115. package/lib/client/validators.js +22 -0
  116. package/lib/client/validators.js.map +1 -1
  117. package/lib/client.js +9151 -1987
  118. package/lib/client.js.map +1 -1
  119. package/lib/config-fillers.js +53 -0
  120. package/lib/config-fillers.js.map +1 -0
  121. package/lib/config-groups.js +49 -0
  122. package/lib/config-groups.js.map +1 -0
  123. package/lib/config.js +87 -107
  124. package/lib/config.js.map +1 -1
  125. package/lib/default-chain.js +66 -0
  126. package/lib/default-chain.js.map +1 -0
  127. package/lib/deps.js +109 -102
  128. package/lib/deps.js.map +1 -1
  129. package/lib/engine-error.js +19 -0
  130. package/lib/engine-error.js.map +1 -0
  131. package/lib/engines.js +32 -115
  132. package/lib/engines.js.map +1 -1
  133. package/lib/fetch.js +23 -4
  134. package/lib/fetch.js.map +1 -1
  135. package/lib/index.js +30 -5
  136. package/lib/index.js.map +1 -1
  137. package/lib/overlay-types.js +88 -0
  138. package/lib/overlay-types.js.map +1 -0
  139. package/lib/overlay.js +683 -0
  140. package/lib/overlay.js.map +1 -0
  141. package/lib/pipeline/candidates.js +27 -1
  142. package/lib/pipeline/candidates.js.map +1 -1
  143. package/lib/pipeline/compile.js +63 -0
  144. package/lib/pipeline/compile.js.map +1 -1
  145. package/lib/pipeline/confidence.js +64 -0
  146. package/lib/pipeline/confidence.js.map +1 -0
  147. package/lib/pipeline/fusion.js +50 -3
  148. package/lib/pipeline/fusion.js.map +1 -1
  149. package/lib/pipeline/plan.js +2 -2
  150. package/lib/pipeline/plan.js.map +1 -1
  151. package/lib/pipeline/render.js +20 -2
  152. package/lib/pipeline/render.js.map +1 -1
  153. package/lib/pipeline/run.js +21 -7
  154. package/lib/pipeline/run.js.map +1 -1
  155. package/lib/pipeline/sources-spec.js +19 -3
  156. package/lib/pipeline/sources-spec.js.map +1 -1
  157. package/lib/providers/builtin.js +13 -14
  158. package/lib/providers/builtin.js.map +1 -1
  159. package/lib/providers/deepseek.js +348 -0
  160. package/lib/providers/deepseek.js.map +1 -0
  161. package/lib/providers/http.js.map +1 -1
  162. package/lib/providers/keyed-meta.js +7 -0
  163. package/lib/providers/keyed-meta.js.map +1 -1
  164. package/lib/providers/platforms.js +118 -58
  165. package/lib/providers/platforms.js.map +1 -1
  166. package/lib/providers/registry.js.map +1 -1
  167. package/lib/restart.js +47 -0
  168. package/lib/restart.js.map +1 -0
  169. package/lib/router.js +203 -27
  170. package/lib/router.js.map +1 -1
  171. package/lib/rpc-types.js +15 -0
  172. package/lib/rpc-types.js.map +1 -0
  173. package/lib/rpc.js +85 -0
  174. package/lib/rpc.js.map +1 -0
  175. package/lib/tools.js +10 -4
  176. package/lib/tools.js.map +1 -1
  177. package/lib/types/actions/approval.d.ts +11 -0
  178. package/lib/types/actions/config.d.ts +54 -0
  179. package/lib/types/actions/format.d.ts +9 -2
  180. package/lib/types/actions/run.d.ts +1 -0
  181. package/lib/types/actions/schema.d.ts +7 -1
  182. package/lib/types/actions/sources.d.ts +29 -3
  183. package/lib/types/actions/types.d.ts +28 -2
  184. package/lib/types/browser-access.d.ts +17 -0
  185. package/lib/types/catalog/recommend.d.ts +4 -0
  186. package/lib/types/catalog/schema.d.ts +2 -0
  187. package/lib/types/cli/builtin-specs.d.ts +38 -0
  188. package/lib/types/cli/chain.d.ts +83 -0
  189. package/lib/types/cli/chains-spec.d.ts +48 -0
  190. package/lib/types/cli/engine.d.ts +50 -0
  191. package/lib/types/cli/opencli-sites.d.ts +24 -0
  192. package/lib/types/cli/opencli.d.ts +77 -0
  193. package/lib/types/cli/probe.d.ts +53 -0
  194. package/lib/types/cli/runner.d.ts +126 -0
  195. package/lib/types/cli/spec.d.ts +269 -0
  196. package/lib/types/cli/time-text.d.ts +12 -0
  197. package/lib/types/cli/trial.d.ts +19 -0
  198. package/lib/types/client/SettingsCard.d.ts +17 -1
  199. package/lib/types/client/agent-mark.d.ts +22 -0
  200. package/lib/types/client/card-frame.d.ts +35 -0
  201. package/lib/types/client/cli-form.d.ts +97 -0
  202. package/lib/types/client/field-docs.d.ts +15 -0
  203. package/lib/types/client/field-keys.d.ts +41 -0
  204. package/lib/types/client/field-rows.d.ts +70 -0
  205. package/lib/types/client/fill.d.ts +2 -0
  206. package/lib/types/client/form-specs.d.ts +12 -3
  207. package/lib/types/client/form.d.ts +79 -1
  208. package/lib/types/client/group-advanced.d.ts +9 -0
  209. package/lib/types/client/group-agent.d.ts +20 -0
  210. package/lib/types/client/group-cli.d.ts +52 -0
  211. package/lib/types/client/group-common.d.ts +12 -0
  212. package/lib/types/client/group-judge.d.ts +17 -0
  213. package/lib/types/client/group-order.d.ts +26 -0
  214. package/lib/types/client/group-runtime.d.ts +15 -0
  215. package/lib/types/client/group-sources.d.ts +46 -0
  216. package/lib/types/client/groups-basic.d.ts +18 -0
  217. package/lib/types/client/index.d.ts +8 -0
  218. package/lib/types/client/json-list.d.ts +21 -0
  219. package/lib/types/client/locales.d.ts +1079 -196
  220. package/lib/types/client/order.d.ts +102 -0
  221. package/lib/types/client/overlay-ops.d.ts +37 -0
  222. package/lib/types/client/styles.d.ts +56 -41
  223. package/lib/types/client/text-view.d.ts +22 -0
  224. package/lib/types/client/ui-kit.d.ts +200 -0
  225. package/lib/types/client/validators.d.ts +6 -0
  226. package/lib/types/config-fillers.d.ts +9 -0
  227. package/lib/types/config-groups.d.ts +23 -0
  228. package/lib/types/config.d.ts +265 -216
  229. package/lib/types/default-chain.d.ts +63 -0
  230. package/lib/types/deps.d.ts +25 -23
  231. package/lib/types/engine-error.d.ts +12 -0
  232. package/lib/types/engines.d.ts +45 -10
  233. package/lib/types/fetch.d.ts +12 -3
  234. package/lib/types/index.d.ts +164 -146
  235. package/lib/types/overlay-types.d.ts +98 -0
  236. package/lib/types/overlay.d.ts +141 -0
  237. package/lib/types/pipeline/candidates.d.ts +19 -0
  238. package/lib/types/pipeline/compile.d.ts +11 -0
  239. package/lib/types/pipeline/confidence.d.ts +35 -0
  240. package/lib/types/pipeline/fusion.d.ts +23 -1
  241. package/lib/types/pipeline/render.d.ts +16 -0
  242. package/lib/types/pipeline/run.d.ts +3 -1
  243. package/lib/types/pipeline/sources-spec.d.ts +5 -2
  244. package/lib/types/pipeline/types.d.ts +37 -0
  245. package/lib/types/providers/deepseek.d.ts +130 -0
  246. package/lib/types/providers/http.d.ts +2 -1
  247. package/lib/types/providers/keyed-meta.d.ts +5 -0
  248. package/lib/types/providers/platforms.d.ts +15 -3
  249. package/lib/types/providers/registry.d.ts +10 -0
  250. package/lib/types/restart.d.ts +52 -0
  251. package/lib/types/router.d.ts +69 -9
  252. package/lib/types/rpc-types.d.ts +79 -0
  253. package/lib/types/rpc.d.ts +41 -0
  254. package/lib/types/tools.d.ts +6 -0
  255. package/lib/types/util.d.ts +16 -1
  256. package/lib/util.js +17 -4
  257. package/lib/util.js.map +1 -1
  258. package/package.json +51 -43
  259. package/scripts/check-client-bundle.mjs +1 -1
  260. package/scripts/cli-login-check.mjs +214 -0
  261. package/lib/client/RubricEditor.js +0 -47
  262. package/lib/client/RubricEditor.js.map +0 -1
  263. package/lib/client/fields.js +0 -31
  264. package/lib/client/fields.js.map +0 -1
  265. package/lib/types/client/RubricEditor.d.ts +0 -6
  266. package/lib/types/client/fields.d.ts +0 -65
package/CHANGELOG.md CHANGED
@@ -1,5 +1,184 @@
1
1
  # 更新记录
2
2
 
3
+ ## 0.2.3(2026-10-28)
4
+
5
+ 接入 **DeepSeek Search / Fetch API**([文档](https://api-docs.deepseek.com/guides/search/),2026-10-17 上线)并标为“**常用**”。兼容范围见下面“同时支持 DSH 0.2.0-rc.2 与 0.2.1-rc.2 ~ rc.4”(DSH `>=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-rc.2 <0.2.2-0`,可选 `@anweat/dsh-browser ^0.2.0`),没有破坏性变更,Config schema 只增不改(`deepseekFetch`、`keyedSources.deepseek`;`playwright.snapshotDir` 现在声明为 volatile,见下文面板保存的修复)。
6
+
7
+ ### 请先看:默认行为会变
8
+
9
+ - **已经有 `DEEPSEEK_API_KEY`(环境变量、DSH 凭据或设置里的密钥字段)的用户,升级后默认的 `search.run` 和证据模式自动规划会先用 DeepSeek 搜索,而它按请求计费**:Search 与 Fetch 各为每 1000 次成功请求 $0.75(空结果、失败、超时不计费,`count` 不影响价格)。文档写明经 DeepSeek Harness / Claude Code / Codex 的服务端工具调用在 2026-11-01 00:00(北京时间)前免费,但没写插件直接调用算不算,**所以本插件按会计费处理**。
10
+ - 不想用:在 `sources.disabled` 里加 `deepseek`;只想在需要时用:自己写一份 `engines`(自定义过的 `engines` 列表保持你写的顺序,不会被自动加入);想限量:`sources.budget.deepseek: { total, daily }`;`evidence.sourcePolicy: anonymous-only` 也不会自动使用它。没有 Key 时行为与 0.2.2 完全一样。
11
+ - **登录类平台的默认后端顺序变了**:小红书、知乎、Reddit、X 现在先试独立 `opencli`(用你日常 Chrome 的登录态),再试独立 CLI(`xhs`、`zhihu`、`rdt`、`twitter`),最后是 dsh-browser 的后端——知乎、Reddit、Twitter 原来是独立 CLI 在前(小红书本来就是 opencli 在前)。已经登录了独立 CLI 并想继续优先用它的,在设置 `platformBackends` 里写出顺序(显式配置原样优先,不受影响)。没装独立 opencli 的用户行为不变:它被跳过并写明原因,链继续往下走。见下文“登录类平台改走 OpenCLI”。
12
+
13
+ ### 同时支持 DSH 0.2.0-rc.2 与 0.2.1-rc.2 ~ rc.4(2026-10-28)
14
+
15
+ 0.2.2 把 peer 移到了 0.2.1 线,留在 0.2.0-rc.2 宿主上的用户(0.2.1 的 peer 是 `<0.2.1-0`)就没有可装的新版本;DSH 又出了 0.2.1-rc.4。0.2.3 两条线都接。**运行时代码没有为此改动**——评估了插件用到的每个宿主接口,0.2.0-rc.2 上都在、语义相同,真实宿主验证也都通过:
16
+
17
+ - peer:每个受管 `@deepseek-ai/dsh-*` 改为 `>=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-rc.2 <0.2.2-0`,cordis 改为 `~4.0.4`(0.2.0-rc.2 宿主依赖 `~4.0.4`,0.2.1-rc.x 依赖 `~4.0.5`,干净安装都解析到 4.0.5)。
18
+ - 对照结果(`git diff dsh-v0.2.0-rc.2 dsh-v0.2.1-rc.3`):设置面板的原子 `mutate` 与 `.volatile()` 校验(`dsh-settings` / `dsh-client-ui-settings`)两个版本相同,只有 rc.3 去掉了远程浏览器的“内存模式”,插件不读它;私有 RPC 通道(`connection.rpc.handle`、`webServer`)的注册接口相同(rc.3 只多了 Peer 的概念,处理函数的签名不变);凭据服务(`credentialRef`、`credentials.resolve`、面板的 `remote.credentials`)相同,只是 rc.2 在“只读来源遮蔽引用”时拒绝写入、面板会原样显示这条拒绝;`tools/pre-execute` 的 `allow | deny | ask` 与“没有审批通道的 ask 变成拒绝”(`serviceAsk`)两个版本一致;`dsh-client-ui-primitives` 的 `Button / Modal / Switch / Tag / Tooltip`、`plugins.bundle.config` 槽位、`configForms.whileServed` 都在 rc.2 里。没有需要“缺了就降级”的分支,原有的降级(没有 `connection` / `webServer` 时不注册 RPC、没有 skill 注册表时用内嵌指南)继续保留。
19
+ - 真实宿主(隔离 `DSH_HOME`,从 npm 装进 scratchpad 的 `@deepseek-ai/dsh` 0.2.0-rc.2 / 0.2.1-rc.3 / 0.2.1-rc.4,用 `pnpm pack` 出的 tgz 以 `file:` 安装):三个宿主上 `dsh plugin add` 退出码 0、无警告;headless 下 `web_index` / `web_call` 都在,`sources.status`、`config.inspect|set_order|reset` 可用,`config.upsert_cli` 被拒绝而不是执行;Web profile 下设置卡片渲染,保存一项后 `cordis.patch.yml` 里恰好多一个键,恢复默认后移除,agent 覆盖层的“采纳到配置”(原子 `mutate`)和“撤销”(私有 RPC)、自定义 CLI 的试运行(RPC `trial`)都工作,保存之后插件**不重启**就读到新配置(`ctx.fiber.config` 热更新)。反向对照:未改 peer 的 0.2.3-dev.1 装进 0.2.0-rc.2、旧的 0.2.1 装进 rc.4,都被安装闸门以 `incompatible` 拒绝。
20
+ - `test/compat/check-peer-ranges.js`:受支持基线 `0.2.0-rc.2`、`0.2.1-rc.2` ~ `rc.4`;`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.1-alpha.5`、`0.2.1-rc.1`、`0.2.2-alpha.1` 在两种解析模式下都拒绝;cordis 4.0.4 / 4.0.5 通过、4.1.0 拒绝;`--selftest` 25 条已知行为(比之前多的 9 条是两段范围的断言),范围判定与 `semver` 7 逐版本核对一致。
21
+ - 可选 Browser peer `^0.2.0`:0.2.0 线宿主上 Browser `0.2.0` / `0.2.1`(及 0.2.2)都能装;0.2.1 线宿主上,装 Browser 0.2.0 / 0.2.1 会被安装闸门拒绝(`nothing was installed`,连同这次一起装的本插件也没装上),要用 0.2.2 起。
22
+
23
+ ### 修复:新用户没有浏览器时的提示(2026-10-28)
24
+
25
+ `@anweat/dsh-browser` 装上不等于能开浏览器:新用户机器上没有 Playwright 的 Chromium。dsh-browser 0.2.2 会自己判断能否启动、有没有系统 Chrome / Edge 可用(`status().browser`);本插件把这一点说出来,而不是一律显示“ready”:
26
+
27
+ - `sources.status` 的 `browser` 多一个 `launch: { ready, source?, path?, advice? }`;没有可启动的浏览器时那一行写成 `❌ browser:dsh-browser [loaded, no browser to launch] — <dsh-browser 的建议>`,用系统浏览器时写 `uses the system browser <路径>`。dsh-browser 没有报这个字段(旧版本)或查询失败时什么都不说。
28
+ - `read.fetch mode=auto`:浏览器渲染失败的原因原来只记成 160 个字符的 `detail`、正文里完全看不到;现在浏览器那一条保留 500 个字符,页面文字末尾多一行 `Browser render did not run: <原因>`,所有后端都失败时错误里带上 `(browser render failed: <原因>)`。
29
+
30
+ ### 分发:干净安装与同装(2026-10-28)
31
+
32
+ 用户原话:npm 有可能遇上 opencli 或 playwright 的分发问题,可以顺便修。
33
+
34
+ - **本插件自己**:没有 `playwright` / `patchright` / `@jackwener/opencli` 依赖,也没有安装脚本;运行时只依赖 `cross-spawn`、`js-yaml`、`node-html-parser`。pnpm 11 下 `dsh plugin add <tgz>` 在 0.2.0-rc.2 / 0.2.1-rc.3 / 0.2.1-rc.4 三个宿主上退出码 0、无警告,共 20 个包、约 10 MB、1–2 秒(pnpm 存储已有缓存;没有测冷缓存)。
35
+ - **和旧 Browser 同装会被连带失败**(复现):`@anweat/dsh-browser` 0.2.1 及更早的 dev 版把 `@jackwener/opencli`(有 postinstall)列为依赖,pnpm 11 默认拦下它的构建脚本,`dsh plugin add` 以 `ERR_PNPM_IGNORED_BUILDS` 退出码 1 失败。包其实装上了,但宿主只把“新增依赖”加入 profile 的 `dsh.profile.bundles`,所以失败的那次里的**两个插件都没有被启用**;此后在这个 profile 里再 `add` 任何包(本插件单独装、甚至无关的 `is-odd`)都因为同一个被忽略的构建而失败,pnpm 还会往 `pnpm-workspace.yaml` 里写一条 `allowBuilds: '@jackwener/opencli': set this to true or false`。本插件不能在自己这边修这个问题;修在 Browser 0.2.2(OpenCLI 改为可选 peer,按 PATH、全局 npm 目录等找;系统 Chrome / Edge 顶替没下载过的 Playwright Chromium)。用 Browser 0.2.2 的候选包验证:三个宿主上 Browser + 本插件一起装、先本插件后 Browser、再装无关包,退出码都是 0,bundles 里两个插件都在。
36
+ - **playwright / patchright**:两个包都没有 install / postinstall 脚本,不会在安装时下载浏览器,所以不存在“脚本被忽略后怎么办”;缺的是浏览器本身,见上一节(本插件说清楚,dsh-browser 自动用系统 Chrome / Edge)。同装 74 MB(旧 Browser,含 OpenCLI)→ 50 MB(Browser 0.2.2,不含 OpenCLI)。
37
+ - **README**:新增“新用户第一次安装 / 第一次使用”两段,写明 Browser 0.2.2 起才能同装、OpenCLI 与浏览器怎么装、没有浏览器时会看到什么。
38
+
39
+ ### 验证(宿主两条线与分发,2026-10-28)
40
+
41
+ 见 `docs/validation/2026-10-28-host-lines-and-install.md`。
42
+
43
+ ### 设置里的搜索顺序、自定义 CLI 与置信度(2026-10-27)
44
+
45
+ 用户原话:“把默认搜索参考顺序和 cli 加入都做到设置中,允许自定义加入可用的 cli 软件和命令,并将结果打包返回,自定义的也可以加入置信度”。Config schema 只增不改(`sources.confidence`,volatile);兼容范围不变。
46
+
47
+ - **搜索顺序(新分组,在“常用”之后,默认折叠)**:`engines` 是可排序列表(位置、名称、就绪、置信度、上移 / 下移 / 移除、下拉添加、“恢复默认顺序”= `unset engines`),下面是实际生效顺序和一句“为什么和列表不同”(新纯函数 `explainChain`,与 `effectiveChain` 同一规则);平台后端顺序每个平台一行 + 弹窗,可选后端 = 内置后端 + 声明服务该平台的自定义 CLI,“恢复默认”删除该平台的键。`platformBackends` / `cliAdapters` 的原始 JSON 移到“高级”。
48
+ - **自定义 CLI(新分组)**:列表 + 表单弹窗(命令、参数 token、自动推出的子命令、输出格式、整段输出、结果路径、字段路径、超时、用途、置信度、高级项)+ “查看 JSON” 双向同步;服务端的校验函数实时校验,消息显示在对应控件旁。规格新增 `engine`、`confidence`、`search.output.lines: { mode: whole }`(见 README“CLI 适配”);`platforms` 现在是声明服务的平台(只做通用引擎时可为空)。**偏离旧行为**:`platformBackends.<平台>` 里放 `custom-cli:<id>` 现在要求它的 `platforms` 声明了该平台(之前任何平台都接受)。
49
+ - **整段输出**:stdout 整体一条结果,标题“`<id>: <查询>`”、链接 `cli://<id>?q=…`、文本截断并注明;证据管线**不抓取**这种链接(只列为来源,不占读页名额)。
50
+ - **结果打包**:自定义 CLI 的结果与其他来源同一结构,另带 `via`、`confidence`(低于 1 时)、`tag`(“user-defined, not verified by the plugin (用户自定义,未经插件验证)”);`search.run` 输出 schema、文本渲染、证据摘录行、缓存与 `sources.status` 同步(`providers[].confidence`、`chain[].confidence`、`sources.confidence`)。
51
+ - **置信度**:`factor(c) = 1 − 0.2·(1 − c)`,乘到每个来源的 RRF 项上,并乘到“只有存疑来源返回的 URL”的加分上;c = 1 不乘,所以内置来源全是 1 时融合分数与原来逐位相同(单元测试固定,`bench:eval-pack` v1 / v2 报告与改动前逐字段相同,bench 94 项测试通过);存疑来源最好的一条仍保证留在展示的列表里。
52
+ - **试运行 / 实时就绪**:插件在宿主的连接传输上注册私有通道 `/dsh-web-search-pro`(照 dsh-browser 的做法,没有该服务的宿主上不注册,面板照常工作并提示改用 `web_call` 的 `sources.status` / `search.run` 测试):`status`(各来源和链上各后端的就绪、原因、置信度)和 `trial`(用面板里还没保存的规格真的探测并搜一次,显示解析出的前三条和工具原话;先过同一套只读校验,不登录,不保存任何东西,受 `enableCliBackends` 控制)。
53
+ - 测试:新增 55 项(置信度公式与不变性、整段输出、规格字段、顺序编辑产生的 patch、`explainChain`、表单 ↔ JSON、面板渲染、RPC)。
54
+
55
+ ### 设置顺序、重启项标注、agent 覆盖层与 `config.*` 动作(2026-10-27)
56
+
57
+ 用户原话:“设置文件怎么调整下设置顺序,怎么方便用户配置也可以想想,要不要设置外置便于agent修改和实时载入,还是某些写在设置profile中需要重启插件列表。”批准的方案:profile 配置是用户的基线,留在原处并重新排序;标出要重启的项;新增一个 agent 可改、实时生效的覆盖层和 `config.*` 动作;**不做**“agent 直接改一个被监听的自由文件”。Config schema 的字段名、默认值和语义不变,兼容范围不变。
58
+
59
+ - **顺序**:`Config` 的字段按设置页的分组重排(常用 → 搜索顺序 → 来源与密钥 → 自定义 CLI → 读取与证据 → 平台与运行时 → 高级与诊断;`sources` 内为 priority、disabled、confidence、budget)。宿主的通用配置视图、`--dump-config` 和设置面板的文本视图都按这个顺序;文本视图改为按同样的分组分节,带中文 / 英文分节标题。一个测试把 schema 钉在 `src/config-groups.ts` 的分组表上,每个字段属于且只属于一个分组。
60
+ - **重启项**:查明启动时只读一次的设置有 8 个——`toolSurface`、`playwright.snapshotDir`、`providerId`、`registerProvider`、`timeoutMs`(每个动作的截止时间与宿主工具上限;搜索内部的协作超时预算是热的)、`memoryCacheEntries`、`dbPath`、`verbose`,集中在 `src/restart.ts` 的 `RESTART_FIELDS`。设置面板里这些行带“重启插件后生效”标记和原因,保存时改到了它们保存条会提示,状态摘要显示“待重启 N 项”;`sources.status` 和 `config.inspect` 写出“待重启:<字段>(当前生效 X,配置为 Y)”。一个测试扫描源码里对启动时配置副本的读取,新增而没登记会失败。
61
+ - **agent 覆盖层**:`$DSH_HOME/data/web-search-pro/overlay.json`(与 `dbPath` 同目录),含版本、`revision`(乐观锁)、sha256 校验和、`values`、50 条审计;临时文件 + 改名,0600;按 mtime 和大小缓存重读,无 fs.watch。只能覆盖 `engines`、`sources.priority` / `disabled`、`sources.confidence.<id>`、`platformBackends.<平台>`、`cliAdapters.<id>`,且只能新增或替换。`dynamic()` 返回“基线 + 覆盖层”,所有消费者下次调用就生效;加载时用与改动同一批函数严格校验,解析失败 / 校验和不符 / 值被拒 / 与基线不再一致就**整体忽略**并在 `sources.status` / `config.inspect` 报告,绝不只应用一半。做法照 `@anweat/dsh-browser` 的策略覆盖层,没有跨包 import。
62
+ - **`config.*`(新动作组,`web_index` 里出现)**:`inspect`、`set_order`、`set_confidence`、`upsert_cli`、`remove_cli`、`reset`、`export`。每次写入要有 3–300 字符的 `reason`,可带 `expectedRevision`;成功后 `revision + 1`、写审计(时间、动作、键、前后值摘要、reason、sessionId)、立即生效;没有变化的写入不落盘。审批:**`config.upsert_cli` 一律先问用户**(提示里写命令、搜索与探测参数模板、环境变量名),其余不问;`dsh-browser.automationMode=read-only` 时全部拒绝(偏离简报:简报只说“不需要审批”,这里让只读模式也拦住写入,与 `cache.clear` 等一致)。**headless 下**宿主对“询问”一律拒绝(无 `approval` 服务按插件理由拒绝;有服务但无应答者得 `unavailable` 也拒绝;策略 `never` 直接拒绝),所以 headless 的 agent 加不了 CLI;测试把宿主这几处逻辑固定住,宿主升级后若行为变了会报警。
63
+ - **设置面板**:被覆盖的行(引擎顺序、优先 / 停用、平台后端链、置信度、自定义 CLI)带“agent 修改”标记,悬停看值、reason、时间;新分组“agent 修改”列出全部覆盖项与最近审计,每项“采纳到配置”(用保存条同款的原子 `mutate` 写进 profile,成功后才经 RPC 删掉这一项覆盖)和“撤销”,另有“全部撤销”(也用来修复被忽略的坏文件)和“刷新”;只在覆盖层里的 CLI 列在自定义 CLI 列表下面。RPC 通道 `/dsh-web-search-pro` 新增 `overlay`、`overlay.revoke`,`status` 带上覆盖层摘要与待重启项。
64
+ - **测试**:新增 54 项(935 → 989,另有 8 项需真实命令的跳过)(分组顺序、覆盖层文件读写 / 损坏 / 冲突 / 审计上限 / 合并、各 `config.*` 的参数与效果、审批与 headless、重启项检测与扫描、面板标记与采纳 / 撤销的 patch 与 RPC 调用)。测试进程使用一次性的 `DSH_HOME`,不会碰真实目录。
65
+
66
+ ### 审批前先校验参数(2026-10-27)
67
+
68
+ 要审批的动作(`config.upsert_cli`、`cache.clear`、`history.delete`、`rules.*`、`sources.install`)现在先做和 `runAction` 同一套的参数校验(参数 schema,外加动作可选的 `precheck`;`config.upsert_cli` 的 `precheck` 就是写覆盖层时用的那套完整 `id` / `spec` 校验,一条一条列出问题),不合法就不问用户:钩子直接放行,`runAction` 在 `execute` 之前返回 `INVALID_ARGS` 和 schema,不执行、不写覆盖层、不记审计。之前真实模型会话里 `config.upsert_cli` 连 `id` 和 `spec` 都没给就进了审批,headless 下被拒(“requires approval, but no approval channel is available”),模型拿不到 schema。参数合法时审批判定不变;`automationMode=read-only` 下,合法的写入照样被拒,不合法的返回 `INVALID_ARGS`(没有东西会执行,给模型 schema 比一句拒绝更有用)。校验自身出错不会卡住调用。新增 8 项测试(含 flat 工具面与无审批渠道的宿主)。
69
+
70
+ ### 修复:热重载后设置页样式丢失(2026-10-28)
71
+
72
+ 宿主热重载插件时页面不刷新,旧版本注入的 `<style id="web-search-pro-settings-styles">` 还在,而注入函数见到它就直接返回,新版本的类名没有任何规则,设置卡片只剩文字、没有框(桌面端升级后出现,完全重启桌面端后恢复)。现在已有的 `<style>` 会换成当前版本的 CSS。
73
+
74
+ ### DeepSeek 搜索(引擎 `deepseek`,“常用”)
75
+
76
+ - 新的原生搜索引擎,描述符 `common: true`(成本层级 `paid`,语言 `zh` + `en`)。Key 与插件其他密钥一致:设置里的密钥字段 → DSH 凭据引用(默认 `DEEPSEEK_API_KEY`,`keyedSources.deepseek.apiKeyEnv` 可改)→ 进程环境变量;`keyedSources.deepseek.baseUrl` 可改接口,默认 `https://api.deepseek.com/v1/tools`。值不进日志、状态输出、错误信息和历史。
77
+ - **“常用”的优先级**:解析到 Key 时,中英文任务都排第一——证据模式的自动规划里排在 Exa / 博查 / 其余需 Key 来源之前(规划排序:已配置 Key → 常用 → 描述符 `priority`),没有 `engines` 的普通搜索也先用它(`engines` 仍是默认列表时)。顺序规则:调用里的 `engines` / `platform` > `sources.priority` / `sources.disabled` > 默认规则;请求额度 `sources.budget.deepseek` 照常生效。`sources.status` 的“常用 (common)”行、provider 行、`search.recommend` 的“常用”标记都会显示;没有 Key 时它不进默认链,`sources.status` 写明配置方法。
78
+ - **字段映射**:`summary`(给模型看的长摘要)→ 摘录文本(截到 1000 字符,去掉与标题重复的一级标题;没有就用 `snippet`),`date` → `publishedAt`,`rerank_score`(0–3)→ `rerankScore`、`authority`(0–4,-1 视为未知不给)→ `authority`,作为来源元数据随结果输出并显示为 `[rerank 2.3/3, authority 4/4]`(`search.run` 的输出 schema 新增这两个可选字段)。不请求 `content`。
79
+ - **证据评分里的权重(保守)**:只用在候选融合(S3),与已有的新鲜度 / 权威域名加分同类:加分 = `0.3 ×(0.7 × rerank/3 + 0.3 × authority/4)` 个“排名单位”。三项加分全拿满也只有 0.75 个单位,所以只在并列时分先后,不能把第 10 名抬到第 1 名之上;一个 URL 被多个来源返回时每个字段取最大值,不叠加;其他来源不带这些字段,一分不加。块级评分不变。
80
+ - **查询**:编成服务支持的运算符——硬 `site` / `exclude_site` / `exclude_term` / `time_window` → `site:a OR site:b`、`-site:`、`-词`、`after:YYYY-MM-DD`;`ori_question` 用任务的 `goal`(没有则第一个关键 need),截到 128 字符。查询超过服务的 128 字符上限时,保留全部运算符、截掉自由文本的尾部,并在结果说明里写明(引擎的 `notes` 现在也会进入证据包的 `notes`)。
81
+ - **错误与重试**:408 / 429 / 5xx 最多共 3 次、等待取 `Retry-After`,否则 0.8 s / 1.6 s(`Retry-After` 超过 8 s 不干等,作为冷却时间);**402 → `ENGINE_QUOTA` 并提示“余额不足”、去 platform.deepseek.com 充值**;401 / 403 → `ENGINE_AUTH`;其余 4xx 不重试;错误信息带 `request_id`,不含 Key。只有“有结果的 200”记入用量账本(`deepseek-search` / `deepseek-fetch`)。
82
+ - **抓取后端(默认关闭)**:设置 `deepseekFetch: true` 后 `read.fetch` 的 `mode: auto` 在 Jina、直连都没拿到正文时用 Fetch API(Markdown),之后才轮到浏览器渲染;`mode: deepseek` 显式指定(未开启时说明原因,不会偷偷花钱)。403 / 404 是 DeepSeek 自己的判断,不是目标页面的 HTTP 状态,错误信息这样写,`auto` 下记成失败的尝试。
83
+ - 设置面板:需 Key 的来源里第一项就是“DeepSeek Search / Fetch”,带“常用”标记,默认展开,含 `deepseekFetch` 开关;README、skill(含 `references/`)、目录(`catalog/sources.v1.json`,`common: true`)同步更新。
84
+ - 其他:`ProviderDescriptor.common`、目录条目 `common` 为新增字段;`ProviderOutcome` 的 `notes` 与 `SearchOutcome.sources` 的 `rerankScore` / `authority` 为新增可选字段;`search.recommend` 对没配置 Key 的付费来源仍按“免费的在前、付费的在后”排,所以 DeepSeek 没配置 Key 时不会被强行塞进三个推荐(直接问 `platform: deepseek` 会得到配置说明)。
85
+
86
+ ### 设置面板改版(照 `@anweat/dsh-browser` 0.2.2 的做法)
87
+
88
+ 用户反馈 Browser 设置页“文字和框体太多、不知道怎么配”,Web Search Pro 的面板有同样的问题(实测:10 个分组、110 个控件、119 条说明行,全部展开约 10,000 px;默认展开 3 组就是 19 个控件、20 条说明)。重做后保存结构、Config schema、运行时行为不变,现有测试继续成立。
89
+
90
+ - **信息架构**:顶部状态摘要(实际尝试顺序的默认引擎链、已配置 Key 的来源数、“常用”的 DeepSeek 是否就绪、内置 web 路由、评分模型、工具面、校验提示数);九个手风琴分组(常用 / 来源 / 读取与证据 / 评分模型 / 内置 web 工具路由 / 运行时与后端 / 网络与缓存 / 排序加权 / 高级:文本模式),**默认只展开“常用”**(默认展开:3 个控件、6 条说明);固定在底部的保存条(“N 项未保存的修改”、放弃、保存)。
91
+ - **控件**:下拉(工具面、来源策略、评分 / 覆盖模式、provider)、开关(带“网络 / 计费 / 调用模型”风险标注)、带单位的数字输入、引擎 / 来源 id / 域名的标签输入;每个字段一行说明,长说明在 `?` 气泡里;“已覆盖”徽标 + 就地“恢复默认”。
92
+ - **列表 + 弹窗**:来源(每行:名称、付费 / 免费额度 / 免 Key、“常用”、Key 已设 / 未设、请求上限;弹窗:Key 引用名、遮罩的写入式密钥、接口地址、请求额度);评分 rubric(每行:生效版本、默认 / 已覆盖;弹窗:默认文本与你的覆盖左右对照,窄屏上下);JSON 对象(自定义 provider、按 provider 上限、Playwright、平台后端顺序、CLI 适配、自定义平台、选择器覆盖、浏览器绑定:每项一个弹窗,校验信息与服务端一致);Twitter / X 登录值(弹窗)。弹窗里的改动先留在草稿里,点保存才写入。
93
+ - **高级:文本模式**:当前草稿的设置(不含密钥)打印成每个键前都带注释的 YAML(用途、取值或范围、默认值、风险,中英文随界面语言);只读视图,可复制;去掉 `#` 行后恰好是你覆盖的键(测试覆盖往返、嵌套、密钥不出现)。
94
+ - 新增可选 peer `@deepseek-ai/dsh-client-ui-primitives`(与其他 `dsh-client-*` 同一范围;开关、按钮、徽标、弹窗、`?` 气泡用宿主的组件),客户端 bundle 的允许清单、`minimumReleaseAgeExclude`、devDependencies 同步;新增纯模块 `default-chain.ts`(路由器与摘要共用的默认引擎链规则)和 `config-fillers.ts`(服务端与面板共用的“去掉宿主 schema 填的空容器”)。
95
+
96
+ ### 修复:面板保存 `evidence` / `provider` / `sources` / `playwright` 在 rc.3 宿主上被拒绝
97
+
98
+ - 旧面板(0.2.2 与 0.2.3 开发版改版前的同一份代码)把 `evidence`、`provider`、`sources` 作为**整个容器**写回,宿主(`dsh-settings`)只接受声明了 `.volatile()` 的字段之下的编辑,整体写容器报 “Config field … is not volatile”,面板显示“Host 未接受全部修改”,同一次保存里排在后面的修改(包括密钥)也不会落盘。在 rc.3 的真实面板上复现并修复(rc.2 宿主没有测):现在一次保存是**一个原子的 `mutate`,按字段路径逐项写**(`evidence.maxRounds`、`sources.budget.deepseek.daily`……),恢复默认 `unset` 那一项;`playwright` 逐键写(`snapshotDir` 现在声明为 volatile)。密钥只在设置写入成功之后才写。不认识的同级键原样保留;宿主不会清掉恢复默认后留下的空容器(`evidence: {}`),无害。
99
+ - 另一个老问题:宿主的 schema 解析器会在 `evidence.coverage` 下填一个空的 `thresholds: {}`,面板把它当成“给了阈值”,默认就报 “thresholds.weak must be a number in 0..1”;现在和服务端一样先去掉这些填充物再校验。
100
+ - 测试里的 Host 替身现在按宿主的规则拒绝不在 volatile 字段之下的路径,并有一条测试固定“卡片写的每个字段都在 volatile 节点之下”。
101
+
102
+ ### 登录类平台改走 OpenCLI(2026-10-27 实测,opencli 1.8.8)
103
+
104
+ **原则:登录完全交给用户。** 插件不提供登录流程,不运行任何登录命令,不读浏览器 Cookie;“缺登录文件就跳过”的闸门不变。推荐在日常 Chrome 里登录小红书、知乎、Reddit、X,并装好 OpenCLI 与 OpenCLI Browser Bridge 扩展;独立 CLI 是可选后备,用工具自带的登录命令,由用户自己运行。
105
+
106
+ - **默认链**:reddit `opencli → rdt → browser-opencli`,twitter `opencli → twitter → browser-opencli`,zhihu `opencli → zhihu → browser-search`(xiaohongshu 仍是 `opencli → xhs → browser-opencli`)。前一个后端失败(未登录、被风控、opencli 不可用)就落到下一个,结果的 `notes` 写 `skipped backends: <id>: <原因>`;只有一个后端出错而其他后端被跳过时,错误现在也列出被跳过的后端(原来只有那一个错误)。用户显式配置的 `platformBackends` 不变。
107
+ - **调用参数**:opencli 的浏览器命令支持 `--window`、`--site-session`、`--keep-tab`(默认值已是后台窗口、临时会话、不保留标签页;omnireach 也显式传)。插件现在显式传 `--window background --site-session ephemeral --keep-tab false`,不抢 Chrome 焦点、用完关标签页;只对 `browser: true` 且 `opencli <site> search --help` 列出这三个参数的命令传(本地探测,不搜索),否则不传。`scripts/cli-login-check.mjs` 使用同一份计划(`planOpencliSearch`)。
108
+ - **按站点映射**:twitter(**live**)——标题是正文,摘要 `@作者 · likes · views · 时间`(照 twitter 后端),`created_at`(`Tue Oct 27 08:38:08 +0000 2026`)转成 ISO;zhihu(**live**)——`类型 · 作者 · 赞同 N`,工具本身没有摘要和时间,不编造。xiaohongshu、reddit 这次没拿到成功输出(见下),映射取自 opencli 1.8.8 适配器源码的输出列,**未实测**,仍是 contract-only,并在规格说明、`sources.status` 和目录里写明。
109
+ - **opencli 错误映射**(规格新字段 `errorCodes`,与其他规格字段同样校验):`AUTH_REQUIRED` / 退出码 77 → `CLI_NOT_LOGGED_IN`,提示“在你的 Chrome 里登录 <站点> 后重试(opencli 使用 Chrome 的登录态)”,不再提示 `opencli <站点> login`(规格新字段 `login.hint`);`LOGIN_WALL`、`SECURITY_BLOCK`、`RATE_LIMITED`、`FORBIDDEN` 以及 Reddit 实测的 `Unexpected token '<' … is not valid JSON` → `CLI_FAILED`,消息写“站点返回了网页而不是数据,可能是风控、IP 被拦或需要登录”并给出检查办法;`EMPTY_RESULT` → `ENGINE_EMPTY`,`BROWSER_CONNECT` → 连不上浏览器扩展,`TIMEOUT` → `ENGINE_TIMEOUT`,参数类错误 → `CLI_CONTRACT_MISMATCH`,其余保留 `[CODE] 原话`。任何 json / yaml 命令输出了 HTML 文档,对所有规格都按“站点返回了网页”报告(原来是笼统的 `CLI_CONTRACT_MISMATCH`)。
110
+ - **xhs**:规格改为 **live**(用户自己的登录实测)。`corner_tag_info` 里的 `publish_time` 映射为 `publishedAt`(新字段来源 `{ time }`:`MM-DD` 取最近一个不晚于今天的日期、`YYYY-MM-DD`、`N分钟前` / `N小时前` / `N天前`、`昨天 HH:MM`,按中国时间;认不出的不填);结果里的 `hot_query` 建议卡片(有 id 无 `note_card`)用新的 `output.skipWhen` 丢弃,不计入“无链接被跳过”。
111
+ - **规格新增(有校验,不影响已有规格)**:`{ time: 路径 }` 字段与拼接项、路径里的 `key=value` 段(取数组里第一个字段等于该值的元素)、`output.skipWhen`、`search.errorCodes`、`login.hint`(与 `login.command` 二选一)。
112
+ - **目录与说明**:`sources.status`、`search.recommend`、`catalog/sources.v1.json`(四个平台条目、`opencli` 条目的安装说明与验证记录;新增 `twitter-cli` 条目,与 `xhs` / `zhihu-cli` / `rdt` 并列)、README、skill、`docs/validation/cli-login-checklist.md` 同步。`search.recommend` 的“未验证”标注现在跟着平台的首选后端走(Twitter 经 OpenCLI 已是 live,不再被标成未验证)。
113
+ - **夹具与测试**:`test/fixtures/cli/live-opencli-twitter-search.json`、`live-opencli-zhihu-search.json`、`live-xhs-search.json`、`live-opencli-xiaohongshu-auth-required.yaml`、`live-opencli-reddit-html-error.yaml` 按真实输出的**形状**做,内容一律是合成的占位值(仓库公开);`constructed-opencli-reddit-search.json` 是未实测的 Reddit 形状。新增 `cli-opencli-chain`(回退顺序、被跳过的原因、调用参数)、`cli-opencli-sites`、`cli-opencli-errors`、`cli-time-text`、`cli-xhs-live` 共 20 项测试。
114
+
115
+ **实测结论(2026-10-27,用户在场,opencli 1.8.8,未升级)**:`opencli twitter search` 与 `zhihu search` 成功;`opencli xiaohongshu search` 退出码 77 `AUTH_REQUIRED`(那个 Chrome 里没有小红书登录);`opencli reddit search` 退出码 1,Reddit 返回的是网页——这台机器的网络是代理出口,Reddit 登录页也显示“网络安全部门已屏蔽你的请求”,**Reddit 在代理 IP 下会被拦**;`xhs search` 用用户保存的登录成功,一次 22 项,其中 2 项是 `hot_query` 卡片。修复后各重跑一次:xhs 3 条带发布时间,opencli twitter 3 条(带 ISO 时间),opencli zhihu 3 条,xiaohongshu / reddit 报出新的、明确的错误。没有运行 zhihu / rdt / twitter 三个独立 CLI 的搜索(没有登录文件,闸门会跳过)。
116
+
117
+ ### 验证
118
+
119
+ - 真实面板(DSH 0.2.1-rc.3 桌面端运行时,隔离 home,Web profile,端口 3395):改版前后截图、浅 / 深色、420 px 窄宽度、来源弹窗、rubric 弹窗;真的点了一次保存:`cordis.patch.yml` 的 `web-search-pro` 条目写出 `searchMaxResults`、`deepseekFetch`、`evidence.maxRounds`、`sources.disabled`、`sources.budget.deepseek.daily`,密钥只进了凭据文件,不在 patch 里;之后把文件还原。
120
+ - 831 项插件测试(新增面板、保存路径、文本视图、列表与弹窗、状态摘要共 16 项,并重写了面板渲染测试)、94 项 bench 测试、类型检查、`test:peers`、构建与客户端 bundle 检查通过。
121
+
122
+ ### 验证(DeepSeek 搜索 / 抓取)
123
+
124
+ - 真实调用 **8 次 Search + 4 次 Fetch**(`docs/validation/dsh-web-search-pro-0.2.3.md`):结果结构与全部字段、`site:` / `-site:` / `after:` 运算符、128 字符上限(HTTP 400)、401 / 404 错误体、Fetch 的 Markdown 都按预期;与文档不一致的地方(`summary` / `snippet` 长度远超文档所写;乱码查询仍返回低 `rerank_score` 的结果而不是空数组)写在记录里。**没有亲眼验证**:403 的 Fetch 拒绝、402、408 / 429 / 5xx 与重试路径(只用模拟响应测试)。
125
+ - DeepSeek 部分新增 25 项测试(见上文总数)。
126
+
127
+ ## 0.2.2(未单独发布,内容并入 0.2.3)
128
+
129
+ > 0.2.2 没有发布到 npm:它的设置面板在 rc.3 宿主上保存部分分组会被拒绝,修复依赖 0.2.3 的代码,所以直接发布 0.2.3。下面的内容都包含在 0.2.3 里。
130
+
131
+ 适配 DSH 0.2.1-rc.2 / 0.2.1-rc.3 宿主,并修复 twitter 平台源返回错误结果的问题([#38](https://github.com/anweat/dsh-web-search-pro/issues/38),感谢 @gogososonono 的报告与根因分析)。npm 上的 0.2.1 的 peer 是 `<0.2.1-0`,在 0.2.1-rc.x 宿主上会被安装闸门拒绝;本版是第一个能装进 0.2.1-rc.x 宿主的发布(0.2.2 / 0.2.3 曾在内部分开记录,从未发布,这里合并为 0.2.2)。除下面 #38 的修复外没有功能变更,也没有破坏性变更;0.2.1 的配置与数据照常可用。
132
+
133
+ ### 宿主适配(DSH 0.2.1-rc.2 / 0.2.1-rc.3)
134
+
135
+ - peer 基线:所有受管的 `@deepseek-ai/dsh-*` peer 从 `>=0.2.0-rc.2 <0.2.1-0` 移到 `>=0.2.1-rc.2 <0.2.2-0`,同时覆盖 `0.2.1-rc.2` 与 `0.2.1-rc.3`(单基线策略不变:0.2.0 线整体拒绝,0.2.2 的预发布也被上界挡掉)。`@deepseek-ai/cordis` peer `~4.0.5`、`@deepseek-ai/schemastery` peer `^3.18.5`;可选 Browser peer 仍是 `^0.2.0`(与 0.2.1-rc.x 配套的是 `@anweat/dsh-browser` 0.2.2 起)。
136
+ - devDependencies:`@deepseek-ai/dsh-*` 全部 → `0.2.1-rc.3`;cordis `^4.0.5`、schemastery `^3.18.5`、cordis-plugin-group `1.0.5`、cordis-plugin-include `1.0.10`、cosmokit `~1.8.6`、`@anweat/dsh-browser` `0.2.1`(最新已发布版)。rc.2 → rc.3 的宿主改动(导航入口自定义、菜单图标、analytics、DeepSeek API-key 路线跟随官方 /models)不涉及插件使用的 API,插件源码与 `lib/` 无需改动。
137
+ - 随宿主生态调整:`@deepseek-ai/dsh-invariants` 在 0.2.1 线不再发布,本插件也从未 import 它,从 devDependencies 移除;0.2.1 线把 `dsh-llm` / `dsh-session` 改成 peer-only,宿主 `autoInstallPeers: false` 不会自动装,故显式加入 devDependencies;新增宿主包 `dsh-working-directory`(原 `dsh-invariants` peer 位置)。不补这些时,测试在 import `@deepseek-ai/dsh-tools` 时会报 `ERR_MODULE_NOT_FOUND: @deepseek-ai/dsh-llm`。
138
+ - `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude` 改为 `@0.2.1-rc.3`。
139
+ - `test/compat/check-peer-ranges.js`:受支持基线为 `0.2.1-rc.2` 与 `0.2.1-rc.3`;`0.1.7-rc.2` / `0.2.0-rc.1` / `0.2.0-rc.2` / `0.2.2-alpha.1` 在两种解析模式下都拒绝;`--selftest` 16 条已知行为通过。
140
+
141
+ ### 修复 #38(twitter)
142
+
143
+ - 修复:twitter 规格原来用 `url-lines` 解析 `twitter search` 的默认输出,而 twitter-cli 在 stdout 不是终端时默认打印 YAML,于是头像、视频、`t.co` 链接和 `profileImageUrl:` 之类的字段名都被当成结果。现在执行 `twitter search QUERY -n N --json`,按 `{ ok, schema_version, data[] }` 信封解析:永久链接由 `author.screenName` 与 `id` 拼出,标题取推文正文(折成一行),摘要含作者、赞 / 转 / 阅读数与 ISO 时间;失败信封里的 `error.message` 原样给出,未登录按工具自己的错误码判定。探测要求 `search --help` 含 `--json`,旧版 twitter-cli 报 `incompatible` 并写明已知可用的最低版本 0.8.5。
144
+ - twitter 凭据:`TWITTER_AUTH_TOKEN` / `TWITTER_CT0` 现在和插件其他密钥一样解析——设置里的 `twitterAuthToken` / `twitterCt0`(密钥角色)→ DSH 凭据引用 `twitterAuthTokenEnv` / `twitterCt0Env`(默认即这两个变量名)→ 宿主进程环境变量;值只通过子进程环境变量交给 `twitter`,不进命令行、日志,错误信息里已脱敏。缺失时 `sources.status` 写明查过哪里,并提示“只写 shell 配置文件宿主读不到”。设置面板新增两个密钥与两个引用名字段。
145
+ - 防止同类问题:内置规格不得使用 `url-lines`,且每条命令必须显式要求机器可读输出(`--json` / `-f json` / `--print` 等,测试覆盖全部内置与生成的 OpenCLI 规格;唯一例外 wx-search-cli 的帮助写明它只输出 JSON);文本模式(`url-lines` / `tsv`,自定义适配会用到)遇到整段是 JSON 或 YAML 文档的输出时报 `CLI_CONTRACT_MISMATCH`,不再返回行;`url-lines` 丢弃只有 `key:` 标签的行,`tsv` 跳过没有制表符的行;失败时优先引用工具自己的错误信息。
146
+ - `sources.status` 与 `search.recommend` 对未经真实搜索验证的后端加一句标注(`contract-only — output parser not verified with a real search`),“已检测 + 已配置”不再容易被读成“能用”。
147
+ - yt-dlp 验证为 live:用规格的原始参数真实搜索一次并解析(夹具 `live-yt-dlp-search.txt`)。twitter 仍是 contract-only:契约来自 twitter-cli 0.8.5 源码与 issue 中的真实输出,我们自己没有用 Token 跑过搜索。
148
+
149
+ ### 免登录 CLI 实测(2026-10-26,`docs/validation/cli-live-2026-10-26.md`)
150
+
151
+ 对 bili、gh、omnireach、yt-dlp、wx-search-cli 和 mcporter→Exa 用插件自己的探测与运行器做了真实中文/英文/特殊字符/无结果查询,并在 rc.3 宿主上跑了 3 个 headless 会话。发现并修复:
152
+
153
+ - gh:查询原来作为一个参数交给 `gh search`,gh 会把它当精确短语,四个词以上的查询搜不到东西,`repo:x 多个词` 还是无效查询。新增占位符 `{terms}`(按词拆成多个参数,引号保持短语),gh 三条搜索都用它;issues / code 搜索首次真实运行并解析。
154
+ - yt-dlp:直播没有播放量和时长,yt-dlp 输出 `NA`,规格只把 `None` 当空值,摘要出现 “NA views | NA”;现在两者都当空值,时长改为 `H:M:S`(`duration_string` 对短视频只打印秒数 “11”)。
155
+ - omnireach:多源命令原来用工具自己的自动路由,PATH 上有 `opencli` 时它会加入走你 Chrome 的 google / twitter 源(实测确认,并用替身 `opencli` 复现);现在显式 `--on hackernews,youtube,github,wechat,bilibili`,测试用替身 `opencli` 断言从不被调用。
156
+ - omnireach / httpx:DSH 宿主进程的 `NO_PROXY` 含 `[::1]`,httpx 因此对所有源报 “Invalid port: ':1]'”,wechat 链悄悄回退到 wx-search-cli(宿主会话里发现);`buildCliEnv` 现在把 IPv6 条目写成不带方括号的形式。
157
+ - bili:约五分之一的条目是没有链接的课程/系列,请求 3 条只返回 2 条;规格新增 `overfetch`(多要 3 条再截取)。
158
+ - 平台链回退说明:被跳过的后端原来只写 “unavailable”,现在带原因(“the omnireach command is not installed: install it …”)。
159
+ - wx-search-cli 升为 live;规格 `live` 现为 bili、gh(repos/issues/code)、omnireach、yt-dlp、wx-search-cli。新增 `test/cli-live.test.ts`(默认用夹具离线跑;`WSP_LIVE_CLI=1 pnpm run test:live-cli` 才跑真实工具)。
160
+ - 登录类 CLI 陪测:`docs/validation/cli-login-checklist.md` 与 `scripts/cli-login-check.mjs`(由用户本人运行;默认不运行任何登录命令)。
161
+
162
+ ### 验证
163
+
164
+ - 790 项插件测试、94 项 bench 测试、类型检查、`test:peers`、构建与客户端 bundle 检查通过。
165
+ - 真实宿主验收见 `docs/validation/dsh-0.2.1-rc.3.md`。
166
+
167
+ ## 0.2.1(2026-10-07)
168
+
169
+ 独立 CLI 适配层。兼容范围与 0.2.0 相同(DSH `>=0.2.0-rc.2 <0.2.1-0`,可选 `@anweat/dsh-browser ^0.2.0`),没有破坏性变更。
170
+
171
+
172
+ - 新增声明式 CLI 适配层(`src/cli/`):规格校验(只读保证:子命令白名单 + 全局拒绝表 + 拒绝读取 Cookie / 带凭据的 flag)、不经 shell 的执行器(最小环境、UTF-8、超时 / 取消 / 输出上限、结构化错误)、本地带缓存的契约探测(同名但契约不同的程序报 `incompatible`)。
173
+ - 内置规格:bili、yt-dlp、twitter 迁到规格执行器;新增 xhs、zhihu、rdt、omnireach、独立 opencli(按 `opencli list` 的 search 命令)、gh,以及按文档的 wx-search-cli、tanso;每个规格记录已验证版本与验证程度(live / contract-only / docs-only)。
174
+ - 每个平台是一条有序后端链(独立 CLI → 独立 OpenCLI → dsh-browser),新增 `platformBackends`、`cliAdapters` 设置;新增平台 `wechat`(公众号)与多源 `omnireach`、`tanso`;`sources.deps` / `sources.status` 显示每个 CLI 的安装状态与每条后端的就绪 / 跳过原因。
175
+ - 默认值写入 README 快速使用并有测试固定:Jev 默认关闭,博查只在配置了 Key 时使用。
176
+ - 平台搜索结果标明实际使用的后端(`Platform: bilibili (via bili)`,输出新增可选字段 `backend`,历史与证据包同样记录);后端链顺延时在说明里列出被跳过的后端与原因。
177
+ - `sources.status` 的平台摘要按后端链的真实状态显示:`ready`(有后端现在就能运行)、`login unverified`(只能依赖你自己的浏览器会话,本地无法确认)、`needs login`、`unavailable`,并给出下一步(例如自行运行 `xhs login`)。
178
+ - `bili` 返回的无视频号条目改用条目自带的链接;没有任何链接的条目跳过并在说明里注明数量。
179
+ - 需要登录的 CLI(xhs、zhihu、rdt)只在本地已有保存的登录信息时才运行(只检查文件是否存在);插件不会触发登录,也不会让 CLI 读取浏览器 Cookie。
180
+ - 验证:774 项插件测试、94 项 bench 测试、类型检查、peer 范围、构建与客户端 bundle 检查通过;真实宿主(DSH 0.2.0-rc.2)上全默认配置可用,`bili` 与 `omnireach` 两条后端实测通过。xhs、zhihu、rdt、twitter、独立 opencli 的搜索只核对了命令契约,未用真实登录态验证。
181
+
3
182
  ## 0.2.0(2026-10-05)
4
183
 
5
184
  正式发布 0.2.0 基线。继承下方 rc.1 的工具面、来源、证据管线与兼容范围;真实验证的宿主仍为 DSH 0.2.0-rc.2,可选 Browser 0.2.0。