dsh-web-search-plugin 0.4.0 → 0.5.0
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 +52 -1
- package/README.md +72 -67
- package/cordis.patch.yml +10 -7
- package/lib/brave.js +4 -6
- package/lib/client.js +175 -87
- package/lib/deepseek.js +71 -15
- package/lib/index.js +137 -80
- package/lib/providers.js +7 -7
- package/lib/rest.js +10 -5
- package/lib/shared.js +1 -1
- package/lib/tavily.js +2 -2
- package/lib/usage.js +53 -30
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +12 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
本项目的重要变更都记录在此。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## [未发布]
|
|
6
|
+
|
|
7
|
+
## [0.5.0] - 2026-10-09
|
|
8
|
+
|
|
9
|
+
### 新增
|
|
10
|
+
|
|
11
|
+
- **适配 DeepSeek Harness `0.2.0-rc.2`**:四个 DSH peerDependencies 提升到 `^0.2.0-rc.2`,`@deepseek-ai/cordis` / `@deepseek-ai/schemastery` 对齐 `~4.0.4` / `~3.18.4`;版本号与 `User-Agent` 更新为 `0.5.0`。
|
|
12
|
+
- DeepSeek 后端支持账号令牌认证:会话 provider 为 `deepseek-account` 时通过 `deepseekAccount.resolveToken()` 获取令牌并以 `x-dsh-auth-token` 发送,未授权时回落到 `DEEPSEEK_API_KEY`。
|
|
13
|
+
- 有会话发起者时,DeepSeek 搜索在派发前记录脱敏请求到会话事件 `web/deepseek-search-llm-request`,与官方 provider 行为一致。
|
|
14
|
+
- 插件列表页与详情页的标题、描述随界面语言切换:`locale/en.json` 与 `locale/zh.json`,经 `"./locale/*.json"` 导出(与官方 bundle 同一机制)。卡片内文案由 `lib/client.js` 的 `zh` / `en` 字典提供。
|
|
15
|
+
- README 面向开源使用者整理:删除仅适用于本机的排查笔记与旧个人包迁移章节,修正与实现脱节的仓库布局与安装说明。
|
|
16
|
+
|
|
17
|
+
### 移除
|
|
18
|
+
|
|
19
|
+
- **`settings.installSection()` 已从 Harness 移除**:Host 侧改为全部配置字段声明 `volatile()`,并在 `apply` 中通过 `config.<field>.get()` 读取实时值。插件不再调用 `ctx.inject(["settings"])`,也不再维护 `setSource` / `current()` 这条配置来源链。
|
|
20
|
+
- **客户端 `settingsScope` 服务已移除**:浏览器端改用 `ctx.configForms`。设置分区通过 `configForms.whileServed([namespace], register)` 条件注册,Host 未下发该命名空间时不留痕迹。`inject` 中的 `settingsScope` 同步替换为 `configForms`。
|
|
21
|
+
- 设置分区不再自行注册 Host 设置卡:0.2.x 的设置由当前 Profile 的插件配置保存,`client.js` 的分区只负责展示与写入。
|
|
22
|
+
|
|
23
|
+
### 修复
|
|
24
|
+
|
|
25
|
+
- Exa 请求改用 `numResults` 与对象形态的 `contents.highlights`;SearXNG 不再发送其 API 未定义的 `count` 参数。
|
|
26
|
+
- 插件 `maxResults` 与调用方上限取较小值,避免常规 `web_search` 请求覆盖设置卡上限;未选中的引擎不再解析其凭据引用,设置卡校验引用名格式。
|
|
27
|
+
- Tavily 用量只保留当前凭据的内存数据,换 key 后立即重新对账;手动刷新限速改为 10 秒,自动刷新不受手动限速约束。
|
|
28
|
+
- Firecrawl 搜索按 API 要求发送数组形态的 `sources`;REST 元数据的空 `countParam` 不再被强制改为 `max_results`。
|
|
29
|
+
- DeepSeek 与 REST 后端按配置的 `maxResults` 截取超额结果,并标记 `truncated`。
|
|
30
|
+
- 网页搜索设置分区随 Host 配置命名空间出现和撤销;凭据写入失败时保留草稿并显示失败提示。
|
|
31
|
+
- 同时修改凭据引用名与 API key 时,密钥写入新的引用名;DeepSeek 账号令牌预检响应取消并统一映射错误。
|
|
32
|
+
- Tavily 与 DeepSeek 的空端点配置恢复环境变量回退;Tavily 手动刷新额度增加最短请求间隔。
|
|
33
|
+
- 设置卡把配置改动按同一 revision 原子提交;Host 拒绝写入时保留草稿,保存期间锁定编辑。写入新凭据时清空该引擎的字面量 key 覆盖,使新凭据生效。
|
|
34
|
+
- 更新 Serper 冒烟脚本,使用当前导出的 `RestSearchProvider` 与内置元数据。
|
|
35
|
+
|
|
36
|
+
### 文档
|
|
37
|
+
|
|
38
|
+
- 澄清本插件注册的接缝 id 是 `dsh-web-search`,`deepseek-official` 只是内部引擎选项;同步修正 bundle patch、卸载说明和会话事件说明。
|
|
39
|
+
- 对齐 README 的端点默认值、环境变量优先级和 API key 保存位置。
|
|
40
|
+
|
|
41
|
+
### 说明
|
|
42
|
+
|
|
43
|
+
- `cordis.patch.yml` 禁用内置 `web-search-deepseek`,使此 bundle 统一管理 DeepSeek 搜索配置;本插件注册的接缝 id 为 `dsh-web-search`。
|
|
44
|
+
- Bundle patch 仍使用单文件声明(0.1.7 起支持的多文件有序声明为可选能力,未启用)。
|
|
45
|
+
|
|
46
|
+
## [0.4.1] - 2026-09-10
|
|
47
|
+
|
|
48
|
+
### 变更
|
|
49
|
+
|
|
50
|
+
- **适配 DeepSeek Harness `0.1.5-rc.1`**:四个 DSH peerDependencies 提升到 `^0.1.5-rc.1`,Node.js 运行要求对齐为 `^22.19.0 || >=24.0.0`。
|
|
51
|
+
- 浏览器端显式注入 `remote.credentials`,避免 API remote 尚未挂载时访问凭据服务的启动顺序竞态。
|
|
52
|
+
- Bundle 覆盖 `web.config` 时保留上游 `fetchProvider: http`,不再因切换搜索 provider 而隐式清除抓取 provider。
|
|
53
|
+
- Brave 的 `proxy` 仅作为 provider 级显式覆盖;留空时继承 DSH 的全局代理分发器,统一支持 `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY`。
|
|
54
|
+
- 版本号及 `User-Agent` 更新为 `0.4.1`。
|
|
55
|
+
|
|
5
56
|
## [0.4.0] - 2026-09-02
|
|
6
57
|
|
|
7
58
|
### 变更
|
|
@@ -72,7 +123,7 @@
|
|
|
72
123
|
- Brave 月限额 `0` 的套餐按控制台 **Capacity** 展示每秒窗口(例如 50 次/秒),不再把月配额渲染成「已用 0 / 0」。Brave 没有花费/credits 接口。
|
|
73
124
|
- 数字框只接受不小于 1 的整数;非法输入用红色错误文案替换灰色 hint,保存会被拦住。超出 20 的结果数量会夹到上限。
|
|
74
125
|
|
|
75
|
-
## [0.2.0] - 2026-08-
|
|
126
|
+
## [0.2.0] - 2026-08-18
|
|
76
127
|
|
|
77
128
|
### 新增
|
|
78
129
|
|
package/README.md
CHANGED
|
@@ -2,39 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) web 能力接缝(`ctx.web`)的统一网页搜索插件,内置 **DeepSeek(官方,默认)/ Tavily / [Brave Search](https://brave.com/search/api/) / Serper / SerpApi / Exa / SearXNG / Scavio / Firecrawl 九个后端**。本包只向接缝注册 **一个** `WebSearchProvider`,稳定 id 为 `dsh-web-search`。在 **设置 → 网页搜索** 里切换引擎即可,不必再改 `web.searchProvider`。
|
|
4
4
|
|
|
5
|
-
- **DeepSeek(官方,默认)** — 走 DeepSeek 的 Anthropic 兼容 Messages API(原生 `web_search_20250305`
|
|
5
|
+
- **DeepSeek(官方,默认)** — 走 DeepSeek 的 Anthropic 兼容 Messages API(原生 `web_search_20250305` 工具,支持账号令牌或 `DEEPSEEK_API_KEY`),一次搜索消耗一个模型轮次。
|
|
6
6
|
- **Tavily** — `keyless`(免费、限流、无需账号)或 `keyed`(`TAVILY_API_KEY`,Bearer token)。keyed 会在设置卡显示额度进度条。
|
|
7
7
|
- **Brave Search** — `GET https://api.search.brave.com/res/v1/web/search`,请求头 `X-Subscription-Token`(凭据名 `BRAVE_API_KEY`)。一次搜索就是一次 HTTP 请求,不走模型轮次;设置卡按响应头展示 Capacity / 月配额。
|
|
8
8
|
- **Serper** — Google 结果(`POST /search`,头 `X-API-KEY`,凭据名 `SERPER_API_KEY`)。
|
|
9
9
|
- **SerpApi** — `GET /search.json`,key 走 query(`api_key=`),凭据名 `SERPAPI_API_KEY`。
|
|
10
|
-
- **Exa** — 语义/神经搜索(`POST /search`,Bearer,凭据名 `EXA_API_KEY
|
|
11
|
-
- **SearXNG** — 自建/协议免 key 的元搜索(`GET /search?format=json
|
|
10
|
+
- **Exa** — 语义/神经搜索(`POST /search`,Bearer,凭据名 `EXA_API_KEY`),请求 `contents.highlights` 作为结果摘要。
|
|
11
|
+
- **SearXNG** — 自建/协议免 key 的元搜索(`GET /search?format=json`),可填自托管实例地址;实例须启用 JSON 输出。
|
|
12
12
|
- **Scavio** — Google SERP(`POST /api/v2/google`,Bearer,凭据名 `SCAVIO_API_KEY`),响应与 SerpApi 同构。
|
|
13
|
-
- **Firecrawl** —
|
|
13
|
+
- **Firecrawl** — 网页搜索(`POST /v2/search`,Bearer,凭据名 `FIRECRAWL_API_KEY`),`sources: ["web"]`;当前请求没有启用整页抓取。
|
|
14
14
|
- **DuckDuckGo** — 无官方搜索 API,不提供独立后端;下拉中以禁用项提示:选 SearXNG 并指向聚合了 DuckDuckGo 的实例即可。
|
|
15
15
|
- **官方 key 跳转** — 需要 key 的内置 provider 在设置卡带「获取 API key ↗」一键跳官方控制台;免 key 的(SearXNG / Tavily keyless)不渲染。
|
|
16
|
-
-
|
|
16
|
+
- **会话事件** — 有会话发起者时,DeepSeek 在派发前记录脱敏模型请求到 `web/deepseek-search-llm-request`;REST 后端不追加自定义事件。
|
|
17
17
|
|
|
18
18
|
## 特性
|
|
19
19
|
|
|
20
|
-
- 与官方 `@deepseek-ai/dsh-web-search-deepseek` 相同的提供方约定:`inject: ['web']` + `
|
|
21
|
-
-
|
|
20
|
+
- 与官方 `@deepseek-ai/dsh-web-search-deepseek` 相同的提供方约定:`inject: ['web']` + 全`volatile()` 配置字段 + `ctx.web.registerSearchProvider`。
|
|
21
|
+
- 自定义配置卡片位于 **设置 → 网页搜索**。
|
|
22
22
|
- **默认引擎为 DeepSeek(官方)**:新装 / 未显式改动时,搜索直接走 `deepseek-official`,不再依赖 keyless 的 Tavily。
|
|
23
|
-
-
|
|
23
|
+
- **9 个后端**中,8 个纯 REST 后端由**静态元数据表 + 通用后端**驱动;只有 DeepSeek 官方走模型轮次。新增内置 REST 引擎还需同步配置字段和客户端引擎列表。
|
|
24
24
|
- Tavily keyless 无需密钥即可用;Brave 需要订阅 token(若本机已有 `BRAVE_API_KEY` 凭据,可直接复用)。
|
|
25
25
|
- Tavily keyed / Brave 在设置卡展示额度进度条(DeepSeek 官方与 Tavily keyless / 其余 REST 后端不展示)。
|
|
26
26
|
- 各引擎结果都规范化为接缝的 `WebSearchResult`(可选 `content` + `sources[]`),按 URL 去重。
|
|
27
|
-
-
|
|
27
|
+
- 配置由当前 Profile 的插件配置保存,全部字段声明为 `volatile()`:改引擎或端点后无需重启插件即可生效。
|
|
28
|
+
- 插件列表页与详情页的标题、描述随界面语言切换(`locale/*.json`);卡片内文案自带中英字典。
|
|
28
29
|
- 错误映射为 `WEB_PROVIDER_ERROR` / `WEB_ABORTED` / `WEB_PROVIDER_CREDENTIAL_MISSING`。
|
|
29
30
|
|
|
30
31
|
## 运行要求
|
|
31
32
|
|
|
32
|
-
- DeepSeek Harness `0.
|
|
33
|
+
- DeepSeek Harness `0.2.0-rc.2` 或兼容的 `0.2.x` 版本
|
|
34
|
+
- Node.js `^22.19.0` 或 `>=24.0.0`
|
|
33
35
|
- pnpm,用于通过 `dsh plugin` 把插件装进 profile
|
|
34
36
|
|
|
35
37
|
## 安装
|
|
36
38
|
|
|
37
|
-
本包是 **bundle**:`dsh.bundle.patch` + `cordis.patch.yml` 会插入 Host 行、把 `web.searchProvider` 设为 `dsh-web-search
|
|
39
|
+
本包是 **bundle**:`dsh.bundle.patch` + `cordis.patch.yml` 会插入 Host 行、把 `web.searchProvider` 设为 `dsh-web-search`,并禁用内置 `web-search-deepseek` Host 插件,使此 profile 的 DeepSeek 搜索由本插件统一配置。本插件注册到接缝的 id 是 `dsh-web-search`;`deepseek-official` 是内部引擎选项,和内置插件注册的 id 不同。没有 bundle 声明时,`dsh plugin add` 只写入依赖,插件不会挂载。
|
|
38
40
|
|
|
39
41
|
### 从 npm 安装
|
|
40
42
|
|
|
@@ -46,39 +48,39 @@ dsh plugin --profile web add dsh-web-search-plugin
|
|
|
46
48
|
|
|
47
49
|
## 配置
|
|
48
50
|
|
|
49
|
-
设置卡编辑的是 `dsh-web-search-plugin`
|
|
51
|
+
设置卡编辑的是 `dsh-web-search-plugin` 命名空间。设置卡中的 API key 输入框把密钥写入凭据服务;若直接在插件配置中填写 `*ApiKey` 字段,则该字面量保存在插件配置中。建议使用设置卡或凭据引用名,避免在配置文件中保存密钥。
|
|
50
52
|
|
|
51
53
|
| 键 | 默认值 | 含义 |
|
|
52
54
|
|---|---|---|
|
|
53
55
|
| `provider` | `deepseek-official` | `tavily`、`brave`、`deepseek-official`、`serper`、`serpapi`、`exa`、`searxng`、`scavio` 或 `firecrawl` |
|
|
54
56
|
| `mode` | `keyless` | 仅 Tavily:`keyless` 或 `keyed` |
|
|
55
|
-
| `apiKey` | — | Tavily API key
|
|
57
|
+
| `apiKey` | — | Tavily API key 配置字面量;设置卡的 key 输入框写入凭据服务 |
|
|
56
58
|
| `apiKeyEnv` | `TAVILY_API_KEY` | keyed Tavily 使用的凭据/环境变量名 |
|
|
57
|
-
| `baseURL` | `https://api.tavily.com
|
|
58
|
-
| `maxResults` | `8` |
|
|
59
|
+
| `baseURL` | 空 | Tavily REST 基址;留空时依次使用 `TAVILY_BASE_URL` 和 `https://api.tavily.com`,再拼 `/search` |
|
|
60
|
+
| `maxResults` | `8` | 插件返回的源数量上限,与调用方的 `request.maxResults` 取较小值。设置卡为 1–20 下拉;SearXNG 不支持请求级数量参数,只在收到结果后截取 |
|
|
59
61
|
| `searchDepth` | `basic` | 仅 Tavily:`basic` 或 `advanced` |
|
|
60
62
|
| `includeAnswer` | `true` | 仅 Tavily:请求生成摘要,写入结果 `content` |
|
|
61
63
|
| `topic` | `general` | 仅 Tavily:`general` 或 `news` |
|
|
62
|
-
| `deepseekApiKey` | — | DeepSeek API key
|
|
64
|
+
| `deepseekApiKey` | — | DeepSeek API key 配置字面量;设置卡的 key 输入框写入凭据服务 |
|
|
63
65
|
| `deepseekApiKeyEnv` | `DEEPSEEK_API_KEY` | DeepSeek 使用的凭据/环境变量名 |
|
|
64
|
-
| `deepseekBaseURL` | `https://api.deepseek.com/anthropic/v1
|
|
66
|
+
| `deepseekBaseURL` | 空 | DeepSeek Anthropic 兼容 Messages 基址;留空时依次使用 `DEEPSEEK_SEARCH_BASE_URL` 和 `https://api.deepseek.com/anthropic/v1`,再拼 `/messages` |
|
|
65
67
|
| `model` | `deepseek-v4-flash` | Anthropic 格式模型名 |
|
|
66
68
|
| `apiVersion` | `2023-06-01` | `anthropic-version` 请求头 |
|
|
67
69
|
| `maxTokens` | `4096` | Messages 请求生成 token 上限 |
|
|
68
70
|
| `maxUses` | `5` | 每次请求 `web_search` 工具的最大调用次数 |
|
|
69
|
-
| `braveApiKey` | — | Brave 订阅 token
|
|
71
|
+
| `braveApiKey` | — | Brave 订阅 token 配置字面量;设置卡的 key 输入框写入凭据服务 |
|
|
70
72
|
| `braveApiKeyEnv` | `BRAVE_API_KEY` | Brave 使用的凭据/环境变量名 |
|
|
71
73
|
| `braveBaseURL` | `https://api.search.brave.com/res/v1/web/search` | Brave 网页搜索接口 |
|
|
72
74
|
| `country` | — | Brave 的 `country`(ISO 两位码,如 `cn`);留空使用 Brave 默认 |
|
|
73
75
|
| `searchLang` | — | Brave 的 `search_lang`(如 `zh-hans`);留空使用 Brave 默认 |
|
|
74
76
|
| `freshness` | — | Brave 的 `freshness`:`pd` / `pw` / `pm` / `py` |
|
|
75
|
-
| `proxy` | — | Brave 使用的 HTTP(S)
|
|
76
|
-
| `serperApiKey` / `serperApiKeyEnv` / `serperBaseURL` | — / `SERPER_API_KEY` /
|
|
77
|
-
| `serpapiApiKey` / `serpapiApiKeyEnv` / `serpapiBaseURL` | — / `SERPAPI_API_KEY` /
|
|
78
|
-
| `exaApiKey` / `exaApiKeyEnv` / `exaBaseURL` | — / `EXA_API_KEY` /
|
|
79
|
-
| `searxngBaseURL` |
|
|
80
|
-
| `scavioApiKey` / `scavioApiKeyEnv` / `scavioBaseURL` | — / `SCAVIO_API_KEY` /
|
|
81
|
-
| `firecrawlApiKey` / `firecrawlApiKeyEnv` / `firecrawlBaseURL` | — / `FIRECRAWL_API_KEY` /
|
|
77
|
+
| `proxy` | — | Brave 使用的 HTTP(S) 代理覆盖;留空继承 DSH 的全局 `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` 策略 |
|
|
78
|
+
| `serperApiKey` / `serperApiKeyEnv` / `serperBaseURL` | — / `SERPER_API_KEY` / `https://google.serper.dev` | Serper 的配置字面量 key / 凭据引用名 / REST 基址 |
|
|
79
|
+
| `serpapiApiKey` / `serpapiApiKeyEnv` / `serpapiBaseURL` | — / `SERPAPI_API_KEY` / `https://serpapi.com` | SerpApi 的配置字面量 key / 凭据引用名 / REST 基址 |
|
|
80
|
+
| `exaApiKey` / `exaApiKeyEnv` / `exaBaseURL` | — / `EXA_API_KEY` / `https://api.exa.ai` | Exa 的配置字面量 key / 凭据引用名 / REST 基址 |
|
|
81
|
+
| `searxngBaseURL` | `https://searx.be` | SearXNG 实例基址;可改为自托管地址 |
|
|
82
|
+
| `scavioApiKey` / `scavioApiKeyEnv` / `scavioBaseURL` | — / `SCAVIO_API_KEY` / `https://api.scavio.dev` | Scavio 的配置字面量 key / 凭据引用名 / REST 基址 |
|
|
83
|
+
| `firecrawlApiKey` / `firecrawlApiKeyEnv` / `firecrawlBaseURL` | — / `FIRECRAWL_API_KEY` / `https://api.firecrawl.dev` | Firecrawl 的配置字面量 key / 凭据引用名 / REST 基址 |
|
|
82
84
|
|
|
83
85
|
## 未纳入的服务
|
|
84
86
|
|
|
@@ -91,40 +93,48 @@ dsh plugin --profile web add dsh-web-search-plugin
|
|
|
91
93
|
| **SERPJET** | 官网当前不可访问,暂不接入 |
|
|
92
94
|
| **DuckDuckGo** | 无官方搜索 API(HTML/社区库抓取不符合元数据表"纯 REST"边界)。下拉中有禁用提示,指引经 SearXNG 使用 |
|
|
93
95
|
|
|
94
|
-
环境变量:启动时 `DSH_WEB_SEARCH_PROVIDER=dsh-web-search` 会选中本接缝 id
|
|
96
|
+
环境变量:启动时 `DSH_WEB_SEARCH_PROVIDER=dsh-web-search` 会选中本接缝 id。`baseURL` 留空时,Tavily 基址回退 `TAVILY_BASE_URL`;`deepseekBaseURL` 留空时回退 `DEEPSEEK_SEARCH_BASE_URL`。
|
|
95
97
|
|
|
96
98
|
## 额度
|
|
97
99
|
|
|
98
100
|
- **Tavily keyless / DeepSeek 官方**:不展示额度条。前者是免费限流、没有账户配额;后者按次扣费、没有月度限额。
|
|
99
|
-
- **Tavily keyed**:搜索时请求 `include_usage
|
|
101
|
+
- **Tavily keyed**:搜索时请求 `include_usage`,在内存中累加当前凭据的 credits。Host 定时调用 `GET /usage`,设置卡也可手动刷新;手动刷新之间至少间隔 10 秒,自动对账不受此限制。用 `account.current_plan` / `plan_limit` 做限额,用量取当前凭据的本地累计与远端的较大值;换 key 时清空旧账户用量并立即重新对账。旧 Tavily 额度不再持久化到磁盘。
|
|
100
102
|
- **Brave**:没有 usage / 花费接口。控制台 **Capacity** 就是响应头里的每秒窗口(例如 50 次/秒)。月限额 `0` 表示不限请求次数,不是额度用完。计费 credits 只能看 [Brave API 控制台](https://api-dashboard.search.brave.com/)。
|
|
101
103
|
- 浏览器只读 `GET /dsh-web-search/usage`(不直打上游)。进度条:剩余超过 20% 为绿色,不超过 20% 为黄色,不超过 10% 为红色。
|
|
102
104
|
|
|
103
|
-
##
|
|
105
|
+
## 卸载
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
若要改用 DSH 内置的 DeepSeek host 插件,先卸载本插件及其 bundle patch:
|
|
108
|
+
|
|
109
|
+
```powershell
|
|
110
|
+
dsh plugin --profile web remove dsh-web-search-plugin
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
随后检查 profile 中实际生效的 `web.searchProvider`;若曾手工覆盖为 `dsh-web-search`,将该覆盖改回 `deepseek-official`。
|
|
106
114
|
|
|
107
115
|
## 工作方式
|
|
108
116
|
|
|
109
|
-
- **DeepSeek** — `POST {deepseekBaseURL}/messages
|
|
110
|
-
- **纯 REST 类(Tavily / Brave / Serper / SerpApi / Exa / SearXNG)** — 由静态元数据表(`lib/providers.js`)+ 通用后端(`lib/rest.js
|
|
117
|
+
- **DeepSeek** — `POST {deepseekBaseURL}/messages`,工具 `web_search_20250305`。会话运行在 DeepSeek 官方账号路由(provider 为 `deepseek-account`)且账号服务允许该端点时,用账号令牌发 `x-dsh-auth-token`;否则回落到 API key(`x-api-key` / `authorization: Bearer`,凭据名 `DEEPSEEK_API_KEY`)。有会话发起者时,在派发前把脱敏请求记录为会话事件 `web/deepseek-search-llm-request`。
|
|
118
|
+
- **纯 REST 类(Tavily / Brave / Serper / SerpApi / Exa / SearXNG / Scavio / Firecrawl)** — 由静态元数据表(`lib/providers.js`)+ 通用后端(`lib/rest.js`)驱动。请求方法 / 路径 / 查询字段名 / 鉴权(bearer / header / none / query)/ 固定参数 / 响应形态由表中的行决定。
|
|
111
119
|
- **Tavily** — `POST {baseURL}/search`。keyless 发送 `x-tavily-access-mode: keyless`;keyed 发送 `authorization: Bearer <key>`,并带 `include_usage` 回传 credits。
|
|
112
120
|
- **Brave** — `GET {braveBaseURL}?q=&count=`,请求头 `x-subscription-token`。不向 session 追加自定义事件。
|
|
113
|
-
-
|
|
121
|
+
- **其余六种 REST 引擎** — 按各自表的 `method` / `auth` / `params` 约定请求。
|
|
114
122
|
- 非 2xx 映射为 `WEB_PROVIDER_ERROR`;调用方取消映射为 `WEB_ABORTED`。
|
|
115
123
|
|
|
116
124
|
## 仓库布局
|
|
117
125
|
|
|
118
126
|
```
|
|
119
|
-
lib/index.js Host 插件:Config
|
|
120
|
-
lib/providers.js
|
|
121
|
-
lib/rest.js 通用 REST
|
|
122
|
-
lib/deepseek.js DeepSeek 后端(Anthropic Messages + web_search_20250305
|
|
123
|
-
lib/tavily.js Tavily
|
|
124
|
-
lib/brave.js Brave
|
|
127
|
+
lib/index.js Host 插件:volatile Config、按元数据分发提供方、额度路由
|
|
128
|
+
lib/providers.js 静态元数据表:8 个 REST provider(另加 deepseek.js 的 DeepSeek,共9 个后端)的请求/鉴权/响应/官方跳转(纯数据,无定制代码)
|
|
129
|
+
lib/rest.js 通用 REST 后端:按元数据驱动请求构造与响应规范化(含 Tavily / Brave 的额度与限流 hook)
|
|
130
|
+
lib/deepseek.js DeepSeek 后端(Anthropic Messages + web_search_20250305 + 账号令牌)
|
|
131
|
+
lib/tavily.js Tavily 专用后端与选项解析(当前分发使用 rest 通用后端)
|
|
132
|
+
lib/brave.js Brave 专用后端与选项解析(当前分发使用 rest 通用后端)
|
|
125
133
|
lib/usage.js Tavily/Brave 用量本地缓存与 /usage 对账
|
|
126
134
|
lib/shared.js 中止 / 凭据解析辅助
|
|
127
|
-
lib/client.js 浏览器 bundle
|
|
135
|
+
lib/client.js 浏览器 bundle:设置分区 + 引擎切换 + 官方 key 跳转 + 额度条
|
|
136
|
+
locale/en.json 插件列表页 / 详情页的英文标题与描述
|
|
137
|
+
locale/zh.json 同上,中文
|
|
128
138
|
cordis.patch.yml Bundle patch:插入 Host 行、设 searchProvider、禁用内置 deepseek
|
|
129
139
|
```
|
|
130
140
|
|
|
@@ -138,14 +148,11 @@ cordis.patch.yml Bundle patch:插入 Host 行、设 searchProvider、禁用
|
|
|
138
148
|
dsh plugin --profile web add ".\dsh-web-search-plugin"
|
|
139
149
|
```
|
|
140
150
|
|
|
141
|
-
**Windows 跨盘不要 `dsh plugin add` 绝对路径。** Profile
|
|
142
|
-
|
|
143
|
-
正确做法是快照到与 profile **同盘**,再用 `file:`:
|
|
151
|
+
**Windows 跨盘不要 `dsh plugin add` 绝对路径。** Profile 与仓库不在同一盘时,它会写成 `link:` 或 `file:` 加绝对盘符;pnpm 会把盘符当成相对路径,junction 指向一个不存在的目录。做法是先把仓库快照到与 profile **同盘**,再用相对 `file:`:
|
|
144
152
|
|
|
145
153
|
```powershell
|
|
146
154
|
$dst = "$env:USERPROFILE\.dsh\profiles\web\.local\dsh-web-search-plugin"
|
|
147
|
-
|
|
148
|
-
robocopy "D:\sample\dsh-web-search-plugin" $dst /E /XD node_modules .git .github /NFL /NDL /NJH /NJS
|
|
155
|
+
robocopy "<仓库路径>" $dst /E /XD node_modules .git .github .snapshots .workbuddy docs /NFL /NDL /NJH /NJS
|
|
149
156
|
```
|
|
150
157
|
|
|
151
158
|
在 profile 的 `package.json` 里:
|
|
@@ -160,51 +167,49 @@ robocopy "D:\sample\dsh-web-search-plugin" $dst /E /XD node_modules .git .github
|
|
|
160
167
|
dsh plugin --profile web install
|
|
161
168
|
```
|
|
162
169
|
|
|
163
|
-
`file:` 是快照:改完仓库后要再 robocopy + `install` 并重启 DSH
|
|
170
|
+
`file:` 是快照:改完仓库后要再 robocopy + `install` 并重启 DSH。改了 `package.json` 的 `files` 字段时,pnpm 可能认为快照未变化而跳过更新,需要先删掉 `node_modules/dsh-web-search-plugin` 再装。
|
|
164
171
|
|
|
165
172
|
用 `dsh --profile web --dump-config` 确认:组成树里应有 `dsh-web-search-plugin` 行,且 `web.searchProvider` 为 `dsh-web-search`。
|
|
166
173
|
|
|
167
|
-
|
|
174
|
+
### 语法检查
|
|
168
175
|
|
|
169
|
-
|
|
176
|
+
```powershell
|
|
177
|
+
npm run check
|
|
178
|
+
npm test
|
|
179
|
+
```
|
|
170
180
|
|
|
171
|
-
|
|
181
|
+
客户端 bundle 必须保持 `window.__ModuleLoader__.load({ id, factory })` 线格式 — 由 `dsh-client-modules` 加载,不是打包器。必须同时导出 `apply` 和 `inject`(`slots`、`locale`、`remote`、`remote.credentials`、`configForms`)。
|
|
172
182
|
|
|
173
|
-
|
|
174
|
-
- id: web
|
|
175
|
-
name: '@deepseek-ai/dsh-web'
|
|
176
|
-
config:
|
|
177
|
-
searchProvider: dsh-web-search
|
|
178
|
-
```
|
|
183
|
+
配置卡片的宿主就是**设置 → 网页搜索**(`settings.section`,`id: "web-search"`),数据来自 `ctx.configForms.get("dsh-web-search-plugin")`,并通过 `configForms.whileServed([namespace], register)` 条件注册:Host 未下发该命名空间时不留痕迹。
|
|
179
184
|
|
|
180
|
-
|
|
181
|
-
dsh plugin --profile web remove @dsh-ltctfer/dsh-web-search-brave
|
|
182
|
-
```
|
|
185
|
+
本插件不在插件详情页(`plugins.bundle.config`)显示配置。
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
## 本地化
|
|
185
188
|
|
|
186
|
-
|
|
189
|
+
插件列表页与详情页的标题和描述走 `locale/<lang>.json`,随界面语言切换:
|
|
187
190
|
|
|
188
|
-
```
|
|
189
|
-
|
|
191
|
+
```
|
|
192
|
+
locale/en.json {"meta": {"title": "Web search", "description": "…"}}
|
|
193
|
+
locale/zh.json {"meta": {"title": "网页搜索", "description": "…"}}
|
|
190
194
|
```
|
|
191
195
|
|
|
192
|
-
|
|
196
|
+
卡片内的文案由 `lib/client.js` 的 `zh` / `en` 两份字典提供,改动时须保持键名对齐。
|
|
193
197
|
|
|
194
198
|
## 发布
|
|
195
199
|
|
|
196
|
-
发布由 GitHub Actions(`.github/workflows/publish.yml
|
|
200
|
+
发布由 GitHub Actions(`.github/workflows/publish.yml`)自动完成:**推送与 `package.json` 版本一致的 `v*` 标签**会带 provenance 发到 npm。向 `main` 的普通推送不会发布。
|
|
197
201
|
|
|
198
|
-
1. 确认 `package.json` 的 `version`
|
|
202
|
+
1. 确认 `package.json` 的 `version` 与即将推送的标签一致,并在 `CHANGELOG.md` 中为该版本填写非空的 `## [版本号]` 章节。
|
|
199
203
|
2. 在 GitHub **Settings → Secrets and variables → Actions** 里配置有 publish 权限的 npm automation token(仓库密钥 `NPM_TOKEN`),并允许 Actions 运行。
|
|
200
204
|
3. 打标签并推送:
|
|
201
205
|
|
|
202
206
|
```powershell
|
|
203
|
-
|
|
204
|
-
git
|
|
207
|
+
$version = node -p "require('./package.json').version"
|
|
208
|
+
git tag "v$version"
|
|
209
|
+
git push origin "v$version"
|
|
205
210
|
```
|
|
206
211
|
|
|
207
|
-
工作流会先核对标签与 `package.json` 的 `version
|
|
212
|
+
工作流会先核对标签与 `package.json` 的 `version`,确认 `CHANGELOG.md` 中对应版本的发布说明非空,再执行 `npm publish --provenance --access public`。
|
|
208
213
|
|
|
209
214
|
## 参与贡献
|
|
210
215
|
|
package/cordis.patch.yml
CHANGED
|
@@ -3,18 +3,21 @@
|
|
|
3
3
|
# installs the package as a plain library: no loader row, no Host apply, no
|
|
4
4
|
# `/plugins/<name>/client.js`.
|
|
5
5
|
#
|
|
6
|
-
# `searchProvider`
|
|
7
|
-
# targeted row's whole `config`. The
|
|
8
|
-
# wins if it later overrides `web`. The
|
|
9
|
-
# backend (DeepSeek / Tavily / Brave) is the
|
|
6
|
+
# `searchProvider` and the upstream `fetchProvider` are restated in full
|
|
7
|
+
# because a Cordis patch replaces the targeted row's whole `config`. The
|
|
8
|
+
# profile's own cordis.patch.yml still wins if it later overrides `web`. The
|
|
9
|
+
# seam id is `dsh-web-search`; the backend (DeepSeek / Tavily / Brave) is the
|
|
10
|
+
# plugin's own `provider` setting.
|
|
10
11
|
- id: web
|
|
11
12
|
name: '@deepseek-ai/dsh-web'
|
|
12
13
|
config:
|
|
13
14
|
searchProvider: dsh-web-search
|
|
15
|
+
fetchProvider: http
|
|
14
16
|
|
|
15
|
-
# This
|
|
16
|
-
#
|
|
17
|
-
#
|
|
17
|
+
# This bundle selects its own `dsh-web-search` seam provider and configures
|
|
18
|
+
# DeepSeek through that provider. Disable the in-box DeepSeek host plugin so
|
|
19
|
+
# the profile has one active DeepSeek configuration surface. Its provider id
|
|
20
|
+
# is `deepseek-official`, distinct from this bundle's `dsh-web-search` id.
|
|
18
21
|
- id: web-search-deepseek
|
|
19
22
|
disabled: true
|
|
20
23
|
|
package/lib/brave.js
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { ProxyAgent } from "undici";
|
|
14
14
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
15
|
-
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
16
15
|
import { MAX_RESULTS_CAP, USER_AGENT, isAbortError, isPositiveInteger, resolveApiKey, resolveSecret, searchAborted, throwIfSearchAborted } from "./shared.js";
|
|
17
16
|
|
|
18
17
|
/** Default Brave Search API web-results endpoint. */
|
|
@@ -123,9 +122,9 @@ export class BraveSearchProvider {
|
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
/**
|
|
126
|
-
* Cached undici ProxyAgent for
|
|
127
|
-
*
|
|
128
|
-
*
|
|
125
|
+
* Cached undici ProxyAgent for an explicit provider-level proxy override.
|
|
126
|
+
* With no override, fetch inherits the dispatcher installed by DSH so its
|
|
127
|
+
* HTTP_PROXY / HTTPS_PROXY / ALL_PROXY / NO_PROXY policy remains intact.
|
|
129
128
|
*/
|
|
130
129
|
dispatcher(options) {
|
|
131
130
|
const proxy = options.proxy;
|
|
@@ -142,7 +141,6 @@ export class BraveSearchProvider {
|
|
|
142
141
|
|
|
143
142
|
/** Project the plugin section into Brave backend options. */
|
|
144
143
|
export function resolveBraveOptions(ctx, config) {
|
|
145
|
-
const environment = launchEnvironmentOf(ctx);
|
|
146
144
|
return {
|
|
147
145
|
...resolveSecret(ctx, {
|
|
148
146
|
literal: config.braveApiKey,
|
|
@@ -153,6 +151,6 @@ export function resolveBraveOptions(ctx, config) {
|
|
|
153
151
|
country: config.country,
|
|
154
152
|
searchLang: config.searchLang,
|
|
155
153
|
freshness: config.freshness,
|
|
156
|
-
proxy: config.proxy
|
|
154
|
+
proxy: config.proxy
|
|
157
155
|
};
|
|
158
156
|
}
|