dsh-bing-search 1.1.3 → 1.1.4

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.
package/CHANGELOG.md CHANGED
@@ -1,55 +1,63 @@
1
- # Changelog
2
-
3
- ## [1.1.3] - 2026-09-13
4
-
5
- ### Changed
6
- - 包名、GitHub 仓库、本地目录统一为 `dsh-bing-search`(无作用域、不带 `@cwsong/`)。`dsh-websearch-bing` 被 npm 判定与已占用的 `dsh-web-search-bing` 过于相似。
7
-
8
- ## [1.1.2] - 2026-09-11
9
-
10
- ### Changed
11
- - npm 包名改为 `@cwsong/dsh-websearch-bing`(无作用域名被判定与已占用的 `dsh-web-search-bing` 过于相似)
12
-
13
- ## [1.1.1] - 2026-09-11
14
-
15
- ### Changed
16
- - 包名、GitHub 仓库、loader 行和设置节统一为 `dsh-websearch-bing` / `websearch-bing`(npm 上的 `dsh-web-search-bing` 已被占用)
17
-
18
- ## [1.1.0] - 2026-09-11
19
-
20
- ### Fixed
21
- - 适配 DSH `0.1.5-rc.1`:peerDependencies 从 `^0.1.0-rc.6` 升级到 `>=0.1.5-rc.1 <0.2.0-0`(旧的 prerelease 区间不匹配 0.1.5-rc.x,导致安装/启动报错)
22
- - `cordis.patch.yml` 补回 `fetchProvider: http`:补丁是整段替换 `web` 行配置,只写 `searchProvider` 会丢掉 base 层显式设置的 fetch provider
23
- - 清理从 duckduckgo 版本遗留的注释/模块名(`web-search-duckduckgo`、`@module dsh-web-search-duckduckgo`)
24
- - 修复 README_EN.md 的 emoji 乱码
25
-
26
- ### Changed
27
- - 设置节注册改用电官方同款 `ctx.inject(["settings"])` + `settings.installSection(...)` 模式,命名空间改为普通字符串常量
28
- - provider 改为「单实例 + resolveOptions thunk」模式(对齐官方 `@deepseek-ai/dsh-web-search-deepseek`),每次搜索快照一次配置,不再每次 new 实例
29
- - `available()` 现在校验 endpoint 是合法 URL 且 maxResults 为正整数
30
- - provider 错误信息附带端点恢复指引(对齐官方错误风格)
31
- - `@deepseek-ai/schemastery` 从 peerDependencies 移到 dependencies(对齐官方插件结构)
32
- - 移除未被代码引用的 `@deepseek-ai/dsh-agent` peer 依赖
33
- - User-Agent 更新到 Chrome/131
34
- - HTML 实体解码补充 `&ensp;` `&emsp;` `&thinsp;` `&ndash;` `&mdash;` `&hellip;`
35
-
36
- ## [1.0.2] - 2026-08-18
37
-
38
- ### Changed
39
- - 重命名为 dsh-web-search-bing(从 dsh-web-search-duckduckgo 迁移)
40
- - 更新 README 和文档
41
-
42
- ## [1.0.1] - 2026-08-18
43
-
44
- ### Changed
45
- - 从 DuckDuckGo 切换到 Bing(cn.bing.com,国内可达)
46
- - 更新 HTML 解析逻辑适配 Bing 页面结构
47
- - 添加 ensearch 参数支持中英文搜索切换
48
- - 添加 accept-language 头部
49
-
50
- ## [1.0.0] - 2026-08-18
51
-
52
- ### Added
53
- - 初始版本:DuckDuckGo 免费搜索 provider
54
- - 无需 API Key,不消耗 DeepSeek 搜索配额
55
- - 支持 DSH web 能力接缝注册
1
+ # Changelog
2
+
3
+ ## [1.1.4] - 2026-09-28
4
+
5
+ ### Added
6
+ - DSH `>= 0.1.6` 的插件页配置表单(`client.js`):编辑搜索入口、语言和解析上限。旧版本仍走 `settings.installSection`。
7
+
8
+ ### Changed
9
+ - 配置字段在运行时支持 `volatile` 时标记为 volatile,搜索时解开热更新后的值;没有该方法的旧运行时保持原样。
10
+
11
+ ## [1.1.3] - 2026-09-13
12
+
13
+ ### Changed
14
+ - 包名、GitHub 仓库、本地目录统一为 `dsh-bing-search`(无作用域、不带 `@cwsong/`)。`dsh-websearch-bing` 被 npm 判定与已占用的 `dsh-web-search-bing` 过于相似。
15
+
16
+ ## [1.1.2] - 2026-09-11
17
+
18
+ ### Changed
19
+ - npm 包名改为 `@cwsong/dsh-websearch-bing`(无作用域名被判定与已占用的 `dsh-web-search-bing` 过于相似)
20
+
21
+ ## [1.1.1] - 2026-09-11
22
+
23
+ ### Changed
24
+ - 包名、GitHub 仓库、loader 行和设置节统一为 `dsh-websearch-bing` / `websearch-bing`(npm 上的 `dsh-web-search-bing` 已被占用)
25
+
26
+ ## [1.1.0] - 2026-09-11
27
+
28
+ ### Fixed
29
+ - 适配 DSH `0.1.5-rc.1`:peerDependencies 从 `^0.1.0-rc.6` 升级到 `>=0.1.5-rc.1 <0.2.0-0`(旧的 prerelease 区间不匹配 0.1.5-rc.x,导致安装/启动报错)
30
+ - `cordis.patch.yml` 补回 `fetchProvider: http`:补丁是整段替换 `web` 行配置,只写 `searchProvider` 会丢掉 base 层显式设置的 fetch provider
31
+ - 清理从 duckduckgo 版本遗留的注释/模块名(`web-search-duckduckgo`、`@module dsh-web-search-duckduckgo`)
32
+ - 修复 README_EN.md 的 emoji 乱码
33
+
34
+ ### Changed
35
+ - 设置节注册改用电官方同款 `ctx.inject(["settings"])` + `settings.installSection(...)` 模式,命名空间改为普通字符串常量
36
+ - provider 改为「单实例 + resolveOptions thunk」模式(对齐官方 `@deepseek-ai/dsh-web-search-deepseek`),每次搜索快照一次配置,不再每次 new 实例
37
+ - `available()` 现在校验 endpoint 是合法 URL 且 maxResults 为正整数
38
+ - provider 错误信息附带端点恢复指引(对齐官方错误风格)
39
+ - `@deepseek-ai/schemastery` 从 peerDependencies 移到 dependencies(对齐官方插件结构)
40
+ - 移除未被代码引用的 `@deepseek-ai/dsh-agent` peer 依赖
41
+ - User-Agent 更新到 Chrome/131
42
+ - HTML 实体解码补充 `&ensp;` `&emsp;` `&thinsp;` `&ndash;` `&mdash;` `&hellip;`
43
+
44
+ ## [1.0.2] - 2026-08-18
45
+
46
+ ### Changed
47
+ - 重命名为 dsh-web-search-bing(从 dsh-web-search-duckduckgo 迁移)
48
+ - 更新 README 和文档
49
+
50
+ ## [1.0.1] - 2026-08-18
51
+
52
+ ### Changed
53
+ - 从 DuckDuckGo 切换到 Bing(cn.bing.com,国内可达)
54
+ - 更新 HTML 解析逻辑适配 Bing 页面结构
55
+ - 添加 ensearch 参数支持中英文搜索切换
56
+ - 添加 accept-language 头部
57
+
58
+ ## [1.0.0] - 2026-08-18
59
+
60
+ ### Added
61
+ - 初始版本:DuckDuckGo 免费搜索 provider
62
+ - 无需 API Key,不消耗 DeepSeek 搜索配额
63
+ - 支持 DSH web 能力接缝注册
package/README.md CHANGED
@@ -1,93 +1,93 @@
1
- # dsh-bing-search
2
-
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![DSH Compatible](https://img.shields.io/badge/DSH-0.1.5--rc.1-brightgreen)](https://github.com/deepseek-ai/deepseek-harness)
5
- [![npm](https://img.shields.io/npm/v/dsh-bing-search.svg)](https://www.npmjs.com/package/dsh-bing-search)
6
-
7
- > DeepSeek Harness (DSH) 的免费 Bing 搜索 provider。无需 API Key,不消耗模型搜索配额。
8
-
9
- <p align="right">
10
- <a href="README_EN.md">English</a> | <b>中文</b>
11
- </p>
12
-
13
- > 本仓库是 [godchen520/dsh-web-search-bing](https://github.com/godchen520/dsh-web-search-bing) 的维护 fork,已适配 DSH `0.1.5-rc.1`。包名用 `dsh-bing-search`,因为 `dsh-web-search-bing` 已被占用,`dsh-websearch-bing` 又被 npm 判定过于相似。
14
-
15
- ## 特点
16
-
17
- - **完全免费**:使用 Bing 公共 HTML 搜索页面,无需 API Key
18
- - **国内可达**:默认使用 cn.bing.com,中国大陆可直接访问
19
- - **零配额消耗**:不消耗 DeepSeek 或任何 LLM 的搜索配额
20
- - **即插即用**:安装后自动把 `web_search` 切到 `bing-free`
21
- - **可配置**:支持切换搜索端点、中英文结果、结果数量
22
-
23
- ## 安装
24
-
25
- npm:
26
-
27
- ```sh
28
- dsh plugin --profile desktop add dsh-bing-search
29
- ```
30
-
31
- GitHub:
32
-
33
- ```sh
34
- dsh plugin --profile desktop add github:whiteS18/dsh-bing-search
35
- ```
36
-
37
- 网页版 profile 把 `desktop` 换成 `web`。装完重启 DSH。
38
-
39
- 也可以在 profile 目录手动安装:
40
-
41
- ```sh
42
- cd $DSH_HOME/profiles/desktop # 桌面版通常为 ~/.dsh/profiles/desktop
43
- pnpm add dsh-bing-search
44
- ```
45
-
46
- 然后把 `"dsh-bing-search"` 加入 `dsh.profile.bundles`(放在 `@deepseek-ai/dsh-web-app` 之后),重启 DSH。
47
-
48
- **注意:** `node_modules` 里的目录名必须等于 `package.json` 的 `name`。不要用旧名 `dsh-web-search-bing` 去 link 这个目录,否则桌面端会报 `profile package identity is invalid`。
49
-
50
- ## 配置
51
-
52
- 当前版本没有设置页卡片。改 `~/.dsh/settings.yaml`(热加载):
53
-
54
- ```yaml
55
- bing-search:
56
- endpoint: https://cn.bing.com/search
57
- maxResults: 20
58
- ensearch: 0
59
- ```
60
-
61
- 或改 profile 的 `cordis.patch.yml` 后重启:
62
-
63
- ```yaml
64
- - id: bing-search
65
- config:
66
- endpoint: https://cn.bing.com/search
67
- maxResults: 15
68
- ensearch: 0
69
- ```
70
-
71
- | 参数 | 默认值 | 说明 |
72
- |------|--------|------|
73
- | `endpoint` | `https://cn.bing.com/search` | 搜索端点 |
74
- | `maxResults` | `20` | 每次搜索最大结果数 |
75
- | `ensearch` | `0` | 0=中文结果,1=英文结果 |
76
-
77
- 设置 → 插件 → 插件配置 里的 **Web Search** 卡片属于官方 `web-search-deepseek`,不是本插件。
78
-
79
- ## 如何确认走的是 Bing
80
-
81
- 把 endpoint 临时改成无效地址后再搜一次。如果报错包含 `bing-search` 和你写的 endpoint,就是本 provider。测完改回去。
82
-
83
- ## 恢复默认搜索
84
-
85
- 把本插件 patch 中的 `searchProvider` 改回 `deepseek-official`(或从 bundles 移除本包),重启即可。
86
-
87
- ## 工作原理
88
-
89
- 调用 `cn.bing.com/search` → 解析 HTML 搜索结果(`<li class="b_algo">` 块)→ 提取标题/URL/摘要 → 返回给 DSH 的 `web_search` 工具。
90
-
91
- ## License
92
-
93
- [MIT](LICENSE)
1
+ # dsh-bing-search
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![DSH Compatible](https://img.shields.io/badge/DSH-0.1.5--rc.1-brightgreen)](https://github.com/deepseek-ai/deepseek-harness)
5
+ [![npm](https://img.shields.io/npm/v/dsh-bing-search.svg)](https://www.npmjs.com/package/dsh-bing-search)
6
+
7
+ > DeepSeek Harness (DSH) 的免费 Bing 搜索 provider。无需 API Key,不消耗模型搜索配额。
8
+
9
+ <p align="right">
10
+ <a href="README_EN.md">English</a> | <b>中文</b>
11
+ </p>
12
+
13
+ > 本仓库是 [godchen520/dsh-web-search-bing](https://github.com/godchen520/dsh-web-search-bing) 的维护 fork,已适配 DSH `0.1.5-rc.1`。包名用 `dsh-bing-search`,因为 `dsh-web-search-bing` 已被占用,`dsh-websearch-bing` 又被 npm 判定过于相似。
14
+
15
+ ## 特点
16
+
17
+ - **完全免费**:使用 Bing 公共 HTML 搜索页面,无需 API Key
18
+ - **国内可达**:默认使用 cn.bing.com,中国大陆可直接访问
19
+ - **零配额消耗**:不消耗 DeepSeek 或任何 LLM 的搜索配额
20
+ - **即插即用**:安装后自动把 `web_search` 切到 `bing-free`
21
+ - **可配置**:支持切换搜索端点、中英文结果、结果数量
22
+
23
+ ## 安装
24
+
25
+ npm:
26
+
27
+ ```sh
28
+ dsh plugin --profile desktop add dsh-bing-search
29
+ ```
30
+
31
+ GitHub:
32
+
33
+ ```sh
34
+ dsh plugin --profile desktop add github:whiteS18/dsh-bing-search
35
+ ```
36
+
37
+ 网页版 profile 把 `desktop` 换成 `web`。装完重启 DSH。
38
+
39
+ 也可以在 profile 目录手动安装:
40
+
41
+ ```sh
42
+ cd $DSH_HOME/profiles/desktop # 桌面版通常为 ~/.dsh/profiles/desktop
43
+ pnpm add dsh-bing-search
44
+ ```
45
+
46
+ 然后把 `"dsh-bing-search"` 加入 `dsh.profile.bundles`(放在 `@deepseek-ai/dsh-web-app` 之后),重启 DSH。
47
+
48
+ **注意:** `node_modules` 里的目录名必须等于 `package.json` 的 `name`。不要用旧名 `dsh-web-search-bing` 去 link 这个目录,否则桌面端会报 `profile package identity is invalid`。
49
+
50
+ ## 配置
51
+
52
+ 当前版本没有设置页卡片。改 `~/.dsh/settings.yaml`(热加载):
53
+
54
+ ```yaml
55
+ bing-search:
56
+ endpoint: https://cn.bing.com/search
57
+ maxResults: 20
58
+ ensearch: 0
59
+ ```
60
+
61
+ 或改 profile 的 `cordis.patch.yml` 后重启:
62
+
63
+ ```yaml
64
+ - id: bing-search
65
+ config:
66
+ endpoint: https://cn.bing.com/search
67
+ maxResults: 15
68
+ ensearch: 0
69
+ ```
70
+
71
+ | 参数 | 默认值 | 说明 |
72
+ |------|--------|------|
73
+ | `endpoint` | `https://cn.bing.com/search` | 搜索端点 |
74
+ | `maxResults` | `20` | 每次搜索最大结果数 |
75
+ | `ensearch` | `0` | 0=中文结果,1=英文结果 |
76
+
77
+ 设置 → 插件 → 插件配置 里的 **Web Search** 卡片属于官方 `web-search-deepseek`,不是本插件。
78
+
79
+ ## 如何确认走的是 Bing
80
+
81
+ 把 endpoint 临时改成无效地址后再搜一次。如果报错包含 `bing-search` 和你写的 endpoint,就是本 provider。测完改回去。
82
+
83
+ ## 恢复默认搜索
84
+
85
+ 把本插件 patch 中的 `searchProvider` 改回 `deepseek-official`(或从 bundles 移除本包),重启即可。
86
+
87
+ ## 工作原理
88
+
89
+ 调用 `cn.bing.com/search` → 解析 HTML 搜索结果(`<li class="b_algo">` 块)→ 提取标题/URL/摘要 → 返回给 DSH 的 `web_search` 工具。
90
+
91
+ ## License
92
+
93
+ [MIT](LICENSE)
package/README_EN.md CHANGED
@@ -1,93 +1,93 @@
1
- # dsh-bing-search
2
-
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![DSH Compatible](https://img.shields.io/badge/DSH-0.1.5--rc.1-brightgreen)](https://github.com/deepseek-ai/deepseek-harness)
5
- [![npm](https://img.shields.io/npm/v/dsh-bing-search.svg)](https://www.npmjs.com/package/dsh-bing-search)
6
-
7
- > Free Bing-backed web search provider for DeepSeek Harness (DSH). No API key needed, no search quota consumed.
8
-
9
- <p align="right">
10
- <b>English</b> | <a href="README.md">中文</a>
11
- </p>
12
-
13
- > This is a maintenance fork of [godchen520/dsh-web-search-bing](https://github.com/godchen520/dsh-web-search-bing), updated for DSH `0.1.5-rc.1`. The package is `dsh-bing-search` because `dsh-web-search-bing` is taken on npm and `dsh-websearch-bing` was rejected as too similar.
14
-
15
- ## Features
16
-
17
- - **Completely Free** — Uses Bing's public HTML search page, no API key required
18
- - **China Accessible** — Defaults to `cn.bing.com`, works in mainland China
19
- - **Zero Quota** — Doesn't consume DeepSeek or any LLM search quota
20
- - **Plug & Play** — Switches `web_search` to `bing-free` after install
21
- - **Configurable** — Switch endpoints, language, and result count
22
-
23
- ## Installation
24
-
25
- npm:
26
-
27
- ```sh
28
- dsh plugin --profile desktop add dsh-bing-search
29
- ```
30
-
31
- GitHub:
32
-
33
- ```sh
34
- dsh plugin --profile desktop add github:whiteS18/dsh-bing-search
35
- ```
36
-
37
- Use `web` instead of `desktop` for the web profile. Restart DSH after install.
38
-
39
- Or install by hand in the profile directory:
40
-
41
- ```sh
42
- cd $DSH_HOME/profiles/desktop # usually ~/.dsh/profiles/desktop for the desktop app
43
- pnpm add dsh-bing-search
44
- ```
45
-
46
- Then add `"dsh-bing-search"` to `dsh.profile.bundles` (after `@deepseek-ai/dsh-web-app`) and restart DSH.
47
-
48
- **Note:** the directory name under `node_modules` must equal `package.json` `name`. Do not link this package as `dsh-web-search-bing`, or the desktop app will fail with `profile package identity is invalid`.
49
-
50
- ## Configuration
51
-
52
- This version has no Settings card. Edit `~/.dsh/settings.yaml` (hot-reloaded):
53
-
54
- ```yaml
55
- bing-search:
56
- endpoint: https://cn.bing.com/search
57
- maxResults: 20
58
- ensearch: 0
59
- ```
60
-
61
- Or override in the profile `cordis.patch.yml` and restart:
62
-
63
- ```yaml
64
- - id: bing-search
65
- config:
66
- endpoint: https://cn.bing.com/search
67
- maxResults: 15
68
- ensearch: 0
69
- ```
70
-
71
- | Option | Default | Description |
72
- |--------|---------|-------------|
73
- | `endpoint` | `https://cn.bing.com/search` | Search endpoint |
74
- | `maxResults` | `20` | Max results parsed per search |
75
- | `ensearch` | `0` | `0` = Chinese results, `1` = English |
76
-
77
- The **Web Search** card under Settings → Plugins → Plugin configuration belongs to official `web-search-deepseek`, not this plugin.
78
-
79
- ## How to confirm Bing is in use
80
-
81
- Temporarily set `endpoint` to an invalid URL and search again. If the error names `bing-search` and the endpoint you set, this provider handled the request. Restore the endpoint afterwards.
82
-
83
- ## Restoring the default search
84
-
85
- Change `searchProvider` in this plugin's patch back to `deepseek-official` (or remove this package from `bundles`) and restart.
86
-
87
- ## How It Works
88
-
89
- Queries `cn.bing.com/search` → parses HTML result blocks (`<li class="b_algo">`) → extracts title/URL/snippet → returns to DSH's `web_search` tool.
90
-
91
- ## License
92
-
93
- [MIT](LICENSE)
1
+ # dsh-bing-search
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![DSH Compatible](https://img.shields.io/badge/DSH-0.1.5--rc.1-brightgreen)](https://github.com/deepseek-ai/deepseek-harness)
5
+ [![npm](https://img.shields.io/npm/v/dsh-bing-search.svg)](https://www.npmjs.com/package/dsh-bing-search)
6
+
7
+ > Free Bing-backed web search provider for DeepSeek Harness (DSH). No API key needed, no search quota consumed.
8
+
9
+ <p align="right">
10
+ <b>English</b> | <a href="README.md">中文</a>
11
+ </p>
12
+
13
+ > This is a maintenance fork of [godchen520/dsh-web-search-bing](https://github.com/godchen520/dsh-web-search-bing), updated for DSH `0.1.5-rc.1`. The package is `dsh-bing-search` because `dsh-web-search-bing` is taken on npm and `dsh-websearch-bing` was rejected as too similar.
14
+
15
+ ## Features
16
+
17
+ - **Completely Free** — Uses Bing's public HTML search page, no API key required
18
+ - **China Accessible** — Defaults to `cn.bing.com`, works in mainland China
19
+ - **Zero Quota** — Doesn't consume DeepSeek or any LLM search quota
20
+ - **Plug & Play** — Switches `web_search` to `bing-free` after install
21
+ - **Configurable** — Switch endpoints, language, and result count
22
+
23
+ ## Installation
24
+
25
+ npm:
26
+
27
+ ```sh
28
+ dsh plugin --profile desktop add dsh-bing-search
29
+ ```
30
+
31
+ GitHub:
32
+
33
+ ```sh
34
+ dsh plugin --profile desktop add github:whiteS18/dsh-bing-search
35
+ ```
36
+
37
+ Use `web` instead of `desktop` for the web profile. Restart DSH after install.
38
+
39
+ Or install by hand in the profile directory:
40
+
41
+ ```sh
42
+ cd $DSH_HOME/profiles/desktop # usually ~/.dsh/profiles/desktop for the desktop app
43
+ pnpm add dsh-bing-search
44
+ ```
45
+
46
+ Then add `"dsh-bing-search"` to `dsh.profile.bundles` (after `@deepseek-ai/dsh-web-app`) and restart DSH.
47
+
48
+ **Note:** the directory name under `node_modules` must equal `package.json` `name`. Do not link this package as `dsh-web-search-bing`, or the desktop app will fail with `profile package identity is invalid`.
49
+
50
+ ## Configuration
51
+
52
+ This version has no Settings card. Edit `~/.dsh/settings.yaml` (hot-reloaded):
53
+
54
+ ```yaml
55
+ bing-search:
56
+ endpoint: https://cn.bing.com/search
57
+ maxResults: 20
58
+ ensearch: 0
59
+ ```
60
+
61
+ Or override in the profile `cordis.patch.yml` and restart:
62
+
63
+ ```yaml
64
+ - id: bing-search
65
+ config:
66
+ endpoint: https://cn.bing.com/search
67
+ maxResults: 15
68
+ ensearch: 0
69
+ ```
70
+
71
+ | Option | Default | Description |
72
+ |--------|---------|-------------|
73
+ | `endpoint` | `https://cn.bing.com/search` | Search endpoint |
74
+ | `maxResults` | `20` | Max results parsed per search |
75
+ | `ensearch` | `0` | `0` = Chinese results, `1` = English |
76
+
77
+ The **Web Search** card under Settings → Plugins → Plugin configuration belongs to official `web-search-deepseek`, not this plugin.
78
+
79
+ ## How to confirm Bing is in use
80
+
81
+ Temporarily set `endpoint` to an invalid URL and search again. If the error names `bing-search` and the endpoint you set, this provider handled the request. Restore the endpoint afterwards.
82
+
83
+ ## Restoring the default search
84
+
85
+ Change `searchProvider` in this plugin's patch back to `deepseek-official` (or remove this package from `bundles`) and restart.
86
+
87
+ ## How It Works
88
+
89
+ Queries `cn.bing.com/search` → parses HTML result blocks (`<li class="b_algo">`) → extracts title/URL/snippet → returns to DSH's `web_search` tool.
90
+
91
+ ## License
92
+
93
+ [MIT](LICENSE)
package/client.js ADDED
@@ -0,0 +1,248 @@
1
+ /**
2
+ * dsh-bing-search — Client half (browser bundle, DSH >= 0.1.6 only).
3
+ *
4
+ * On DSH <= 0.1.5 the provider's settings UI is the auto-generated section the
5
+ * Host installs through `settings.installSection`, so this half stays inert
6
+ * (the `configForms` service does not exist there).
7
+ *
8
+ * On DSH >= 0.1.6 the Settings forms are driven by the plugin's Cordis Config
9
+ * volatile fields instead; this half registers a small editor into the
10
+ * Plugins page's `plugins.bundle.config` slot (keyed by the package name),
11
+ * reading and writing the `bing-search` profile entry through the shared
12
+ * `configForms` service. The registration lives only while the Host serves
13
+ * the entry, so a profile without the plugin shows no trace of the page.
14
+ *
15
+ * This file is a classic script registered through the client module loader:
16
+ * `window.__ModuleLoader__.load({ id, factory })` with a lazy CJS factory.
17
+ */
18
+ window.__ModuleLoader__.load({
19
+ id: 'dsh-bing-search',
20
+ factory: (require) => {
21
+ var module = { exports: {} };
22
+ var exports = module.exports;
23
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
24
+
25
+ const React = require('react');
26
+
27
+ /** Profile entry id of the provider row (see cordis.patch.yml). */
28
+ const ENTRY_ID = 'bing-search';
29
+ /** Bundle package name the Plugins page keys its config slot by. */
30
+ const BUNDLE_KEY = 'dsh-bing-search';
31
+ const LOCALE_NS = 'settings.bing-search';
32
+
33
+ const zh = {
34
+ title: 'Bing 免费搜索',
35
+ summary: '配置 Bing HTML 搜索入口、语言与解析上限。',
36
+ endpoint: '搜索入口 URL',
37
+ endpointHint: '默认 https://cn.bing.com/search;任何返回 HTML 结果页的 Bing 镜像都可以。',
38
+ maxResults: '解析上限',
39
+ maxResultsHint: '每次搜索最多解析的结果条数(正整数)。',
40
+ language: '结果语言',
41
+ languageZh: '中文(ensearch=0)',
42
+ languageEn: '英文(ensearch=1)',
43
+ save: '保存',
44
+ saving: '保存中…',
45
+ saved: '已保存。',
46
+ discard: '放弃修改',
47
+ readOnly: '当前部署的配置为只读。',
48
+ unavailable: 'Host 未提供 Bing 搜索配置。',
49
+ saveFailed: '保存失败,已保留你的修改。',
50
+ };
51
+ const en = {
52
+ title: 'Bing free search',
53
+ summary: 'Configure the Bing HTML endpoint, language, and parse cap.',
54
+ endpoint: 'Search endpoint URL',
55
+ endpointHint: 'Defaults to https://cn.bing.com/search; any Bing mirror serving an HTML results page works.',
56
+ maxResults: 'Parse cap',
57
+ maxResultsHint: 'Maximum results parsed per search (positive integer).',
58
+ language: 'Result language',
59
+ languageZh: 'Chinese (ensearch=0)',
60
+ languageEn: 'English (ensearch=1)',
61
+ save: 'Save',
62
+ saving: 'Saving…',
63
+ saved: 'Saved.',
64
+ discard: 'Discard',
65
+ readOnly: 'Configuration is read-only in this deployment.',
66
+ unavailable: 'The Host does not serve Bing search configuration.',
67
+ saveFailed: 'The deployment did not accept these values.',
68
+ };
69
+
70
+ function translateFactory(dict) {
71
+ return (key) => dict[key] ?? en[key] ?? key;
72
+ }
73
+
74
+ const css = {
75
+ section: { display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 640, color: 'var(--dsw-alias-label-primary)' },
76
+ title: { margin: 0, fontSize: 18, fontWeight: 600, lineHeight: '26px' },
77
+ meta: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
78
+ field: { display: 'flex', flexDirection: 'column', gap: 4 },
79
+ label: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)' },
80
+ input: {
81
+ height: 32, width: '100%', boxSizing: 'border-box',
82
+ border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8,
83
+ padding: '0 10px', font: 'inherit', fontSize: 14, lineHeight: '22px',
84
+ background: 'var(--dsw-alias-bg-layer-1)', color: 'var(--dsw-alias-label-primary)',
85
+ },
86
+ button: {
87
+ boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
88
+ height: 28, padding: '0 10px', borderRadius: 14,
89
+ border: '1px solid var(--dsw-alias-border-l2)', background: 'transparent',
90
+ color: 'var(--dsw-alias-label-primary)', font: 'inherit', fontSize: 12, lineHeight: '18px',
91
+ cursor: 'pointer',
92
+ },
93
+ primary: {
94
+ boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
95
+ height: 32, padding: '0 14px', borderRadius: 16, border: 'none',
96
+ background: 'var(--dsw-alias-button-primary-fill, #3b82f6)',
97
+ color: 'var(--dsw-alias-label-primary-foreground, #fff)',
98
+ font: 'inherit', fontSize: 13, cursor: 'pointer',
99
+ },
100
+ actions: { display: 'flex', gap: 8, justifyContent: 'flex-end', alignItems: 'center' },
101
+ error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },
102
+ ok: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-success-primary)' },
103
+ };
104
+
105
+ function BingSearchConfigPage(props) {
106
+ const scope = props.scope;
107
+ const t = typeof props.t === 'function' ? props.t : translateFactory(props.dict ?? zh);
108
+ const [snap, setSnap] = React.useState(() => scope.getSnapshot());
109
+ const [draft, setDraft] = React.useState(null);
110
+ const [busy, setBusy] = React.useState(false);
111
+ const [failed, setFailed] = React.useState(false);
112
+ const [savedFlash, setSavedFlash] = React.useState(false);
113
+
114
+ React.useEffect(() => {
115
+ setSnap(scope.getSnapshot());
116
+ return scope.subscribe(() => setSnap(scope.getSnapshot()));
117
+ }, [scope]);
118
+
119
+ const value = snap.value ?? {};
120
+ const current = {
121
+ endpoint: typeof value.endpoint === 'string' ? value.endpoint : '',
122
+ maxResults: Number.isInteger(value.maxResults) ? value.maxResults : 20,
123
+ ensearch: Number.isInteger(value.ensearch) ? value.ensearch : 0,
124
+ };
125
+ const form = draft ?? current;
126
+ const dirty = draft !== null
127
+ && (draft.endpoint !== current.endpoint
128
+ || String(draft.maxResults) !== String(current.maxResults)
129
+ || String(draft.ensearch) !== String(current.ensearch));
130
+
131
+ if (props.view === 'summary') {
132
+ return React.createElement('p', { style: css.meta }, t('summary'));
133
+ }
134
+ if (snap.status === 'unavailable') {
135
+ return React.createElement('div', { style: css.section },
136
+ React.createElement('h2', { style: css.title }, t('title')),
137
+ React.createElement('p', { style: css.error }, t('unavailable')),
138
+ );
139
+ }
140
+
141
+ const writable = snap.writable !== false && snap.status === 'ready';
142
+
143
+ async function save() {
144
+ const endpoint = String(form.endpoint ?? '').trim();
145
+ const maxResults = Number.parseInt(String(form.maxResults), 10);
146
+ const ensearch = Number.parseInt(String(form.ensearch), 10);
147
+ setBusy(true);
148
+ setFailed(false);
149
+ setSavedFlash(false);
150
+ try {
151
+ const accepted = await scope.mutate([
152
+ { op: 'set', path: ['endpoint'], value: endpoint },
153
+ { op: 'set', path: ['maxResults'], value: Number.isInteger(maxResults) && maxResults > 0 ? maxResults : 20 },
154
+ { op: 'set', path: ['ensearch'], value: ensearch === 1 ? 1 : 0 },
155
+ ], snap.revision);
156
+ if (accepted === false) throw new Error('refused');
157
+ setDraft(null);
158
+ setSavedFlash(true);
159
+ } catch {
160
+ setFailed(true);
161
+ } finally {
162
+ setBusy(false);
163
+ }
164
+ }
165
+
166
+ return React.createElement('div', { style: css.section },
167
+ React.createElement('h2', { style: css.title }, t('title')),
168
+ !writable ? React.createElement('p', { style: css.meta }, t('readOnly')) : null,
169
+ React.createElement('div', { style: css.field },
170
+ React.createElement('span', { style: css.label }, t('endpoint')),
171
+ React.createElement('input', {
172
+ style: css.input,
173
+ value: form.endpoint,
174
+ placeholder: 'https://cn.bing.com/search',
175
+ 'aria-label': t('endpoint'),
176
+ disabled: !writable || busy,
177
+ onChange: (event) => setDraft({ ...form, endpoint: event.target.value }),
178
+ }),
179
+ React.createElement('p', { style: css.meta }, t('endpointHint')),
180
+ ),
181
+ React.createElement('div', { style: css.field },
182
+ React.createElement('span', { style: css.label }, t('maxResults')),
183
+ React.createElement('input', {
184
+ style: { ...css.input, maxWidth: 120 },
185
+ value: String(form.maxResults),
186
+ inputMode: 'numeric',
187
+ 'aria-label': t('maxResults'),
188
+ disabled: !writable || busy,
189
+ onChange: (event) => setDraft({ ...form, maxResults: event.target.value }),
190
+ }),
191
+ React.createElement('p', { style: css.meta }, t('maxResultsHint')),
192
+ ),
193
+ React.createElement('div', { style: css.field },
194
+ React.createElement('span', { style: css.label }, t('language')),
195
+ React.createElement('select', {
196
+ style: { ...css.input, maxWidth: 240 },
197
+ value: String(form.ensearch),
198
+ 'aria-label': t('language'),
199
+ disabled: !writable || busy,
200
+ onChange: (event) => setDraft({ ...form, ensearch: event.target.value }),
201
+ },
202
+ React.createElement('option', { value: '0' }, t('languageZh')),
203
+ React.createElement('option', { value: '1' }, t('languageEn')),
204
+ ),
205
+ ),
206
+ failed ? React.createElement('p', { style: css.error, role: 'alert' }, t('saveFailed')) : null,
207
+ savedFlash && !dirty ? React.createElement('p', { style: css.ok, role: 'status' }, t('saved')) : null,
208
+ React.createElement('div', { style: css.actions },
209
+ React.createElement('button', {
210
+ type: 'button', style: css.button, disabled: !dirty || busy,
211
+ onClick: () => { setDraft(null); setFailed(false); },
212
+ }, t('discard')),
213
+ React.createElement('button', {
214
+ type: 'button', style: css.primary, disabled: !writable || !dirty || busy, onClick: save,
215
+ }, busy ? t('saving') : t('save')),
216
+ ),
217
+ );
218
+ }
219
+
220
+ function apply(ctx) {
221
+ // DSH <= 0.1.5 has no configForms service: the Host-installed settings
222
+ // section covers the UI there, so this half stays inert.
223
+ const configForms = typeof ctx.get === 'function' ? ctx.get('configForms') : undefined;
224
+ const slots = typeof ctx.get === 'function' ? ctx.get('slots') : undefined;
225
+ if (!configForms || typeof configForms.get !== 'function' || !slots) return;
226
+
227
+ const locale = typeof ctx.get === 'function' ? ctx.get('locale') : undefined;
228
+ let t = translateFactory(zh);
229
+ if (locale && typeof locale.register === 'function' && typeof locale.bind === 'function') {
230
+ ctx.effect(() => locale.register(LOCALE_NS, { zh, en }), 'dsh-bing-search: dictionaries');
231
+ t = locale.bind(LOCALE_NS);
232
+ }
233
+
234
+ const scope = configForms.get(ENTRY_ID);
235
+ ctx.effect(
236
+ () => configForms.whileServed([ENTRY_ID], () => slots.inject('plugins.bundle.config', () => slots.register(
237
+ { name: 'plugins.bundle.config', key: BUNDLE_KEY, locale: LOCALE_NS },
238
+ (props) => React.createElement(BingSearchConfigPage, { ...props, scope, t }),
239
+ ))),
240
+ 'dsh-bing-search: plugins page',
241
+ );
242
+ }
243
+
244
+ exports.apply = apply;
245
+ exports.inject = [];
246
+ return module.exports;
247
+ },
248
+ });
package/lib/index.js CHANGED
@@ -273,15 +273,40 @@ const name = "bing-search";
273
273
  /** The web seam this provider registers into. */
274
274
  const inject = ["web"];
275
275
 
276
+ /**
277
+ * Mark a schema field volatile when the runtime's schemastery supports it
278
+ * (DSH >= 0.1.6). Volatile fields are editable through the client's
279
+ * configuration forms and hot-committed into the running fiber; on older
280
+ * runtimes the method does not exist and the field stays a plain default.
281
+ */
282
+ function maybeVolatile(schema) {
283
+ return typeof schema?.volatile === "function" ? schema.volatile() : schema;
284
+ }
285
+
276
286
  const Config = z.object({
277
- endpoint: z.string().default(DEFAULT_ENDPOINT),
278
- maxResults: z.number().step(1).min(1).default(DEFAULT_MAX_RESULTS),
279
- ensearch: z.number().step(1).min(0).max(1).default(DEFAULT_ENSEARCH)
287
+ endpoint: maybeVolatile(z.string().default(DEFAULT_ENDPOINT)),
288
+ maxResults: maybeVolatile(z.number().step(1).min(1).default(DEFAULT_MAX_RESULTS)),
289
+ ensearch: maybeVolatile(z.number().step(1).min(0).max(1).default(DEFAULT_ENSEARCH))
280
290
  });
281
291
 
282
292
  /** Settings namespace carrying this provider's endpoint and parse cap. */
283
293
  const WEB_SEARCH_BING_SETTINGS_NAMESPACE = "bing-search";
284
294
 
295
+ /**
296
+ * Unwrap one config field that may be a live Volatile reference (DSH >= 0.1.6
297
+ * loader commits volatile writes into the box in place) or a plain value
298
+ * (older runtimes / settings-section sources).
299
+ *
300
+ * @param {unknown} value - the raw config field.
301
+ * @returns {unknown} the current plain value.
302
+ */
303
+ function unwrapVolatile(value) {
304
+ if (value && typeof value === "object" && typeof value.get === "function") {
305
+ try { return value.get(); } catch { return undefined; }
306
+ }
307
+ return value;
308
+ }
309
+
285
310
  /**
286
311
  * Project one resolved section into the options the provider serves its next
287
312
  * search with: every value it reads is already fully defaulted.
@@ -291,9 +316,9 @@ const WEB_SEARCH_BING_SETTINGS_NAMESPACE = "bing-search";
291
316
  */
292
317
  function resolveOptions(config) {
293
318
  return {
294
- endpoint: config?.endpoint ?? DEFAULT_ENDPOINT,
295
- maxResults: config?.maxResults ?? DEFAULT_MAX_RESULTS,
296
- ensearch: config?.ensearch ?? DEFAULT_ENSEARCH
319
+ endpoint: unwrapVolatile(config?.endpoint) ?? DEFAULT_ENDPOINT,
320
+ maxResults: unwrapVolatile(config?.maxResults) ?? DEFAULT_MAX_RESULTS,
321
+ ensearch: unwrapVolatile(config?.ensearch) ?? DEFAULT_ENSEARCH
297
322
  };
298
323
  }
299
324
 
@@ -301,6 +326,12 @@ function resolveOptions(config) {
301
326
  function apply(ctx, config) {
302
327
  let current = () => config;
303
328
  ctx.inject(["settings"], (settingsCtx) => {
329
+ // DSH <= 0.1.5: the settings service installs a schema-driven section and
330
+ // streams live sources through setSource. DSH >= 0.1.6 removed
331
+ // installSection: the same values ride the plugin's own Cordis Config
332
+ // (volatile fields above), which the loader hot-commits into the running
333
+ // fiber — resolveOptions unwraps the boxes on every search.
334
+ if (typeof settingsCtx.settings?.installSection !== "function") return;
304
335
  settingsCtx.settings.installSection(ctx, WEB_SEARCH_BING_SETTINGS_NAMESPACE, Config, config, {
305
336
  setSource: (source) => {
306
337
  current = source;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-bing-search",
3
3
  "description": "Free Bing-backed web search provider for DeepSeek Harness (DSH). No API key needed, uses cn.bing.com for mainland China accessibility.",
4
- "version": "1.1.3",
4
+ "version": "1.1.4",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
@@ -19,12 +19,14 @@
19
19
  "types": "./lib/types/index.d.ts",
20
20
  "default": "./lib/index.js"
21
21
  },
22
+ "./client": "./client.js",
22
23
  "./cordis.patch.yml": "./cordis.patch.yml",
23
24
  "./package.json": "./package.json"
24
25
  },
25
26
  "files": [
26
27
  "lib/index.js",
27
28
  "lib/types",
29
+ "client.js",
28
30
  "cordis.patch.yml",
29
31
  "README.md",
30
32
  "README_EN.md",
@@ -37,12 +39,15 @@
37
39
  "dsh": {
38
40
  "bundle": {
39
41
  "patch": "./cordis.patch.yml"
42
+ },
43
+ "client": {
44
+ "platform": "web"
40
45
  }
41
46
  },
42
47
  "peerDependencies": {
48
+ "@deepseek-ai/cordis": "^4.0.2",
43
49
  "@deepseek-ai/dsh-settings": ">=0.1.5-rc.1 <0.2.0-0",
44
- "@deepseek-ai/dsh-web": ">=0.1.5-rc.1 <0.2.0-0",
45
- "@deepseek-ai/cordis": "^4.0.2"
50
+ "@deepseek-ai/dsh-web": ">=0.1.5-rc.1 <0.2.0-0"
46
51
  },
47
52
  "dependencies": {
48
53
  "@deepseek-ai/schemastery": "^3.18.2"