@tonydua/dsh-web-search-exa 0.1.4 → 0.1.5

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/README.i18n.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record.
4
- README.md: 9dc939e621c5ceacc4d0dffbc0eb920f039e2cf1
5
- README.zh.md: 4caca6729a7fd25894ecd9ddc6c8c3565e4e8605
4
+ README.md: 1c5d8f365ee4206b20fa5bd7c07e980266324287
5
+ README.zh.md: 8801d405bc41cace604325a7918152394a5627a6
package/README.md CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  **English** | [简体中文](README.zh.md)
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@tonydua/dsh-web-search-exa)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
5
+ [![npm version](https://img.shields.io/npm/v/@tonydua/dsh-web-search-exa?label=npm)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
6
+ [![GitHub release](https://img.shields.io/github/v/release/TonyDua/dsh-web-search-exa?label=release)](https://github.com/TonyDua/dsh-web-search-exa/releases/latest)
6
7
  [![npm downloads](https://img.shields.io/npm/dm/@tonydua/dsh-web-search-exa)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
7
8
  [![License](https://img.shields.io/npm/l/@tonydua/dsh-web-search-exa)](LICENSE)
9
+ [![dsh](https://img.shields.io/badge/dsh-0.1.2--alpha.2%20%E2%80%93%200.1.7--alpha.1-4c6?logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)
10
+ [![Node](https://img.shields.io/badge/node-%3E%3D22.19.0-339933?logo=node.js&logoColor=white)](package.json)
8
11
  [![GitHub stars](https://img.shields.io/github/stars/TonyDua/dsh-web-search-exa)](https://github.com/TonyDua/dsh-web-search-exa)
9
12
  [![GitHub issues](https://img.shields.io/github/issues/TonyDua/dsh-web-search-exa)](https://github.com/TonyDua/dsh-web-search-exa)
10
- [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white)](package.json)
11
- [![dsh](https://img.shields.io/badge/dsh-0.1.2--rc.1-4c6?logo=deepseek&logoColor=white)](https://www.npmjs.com/package/@deepseek-ai/dsh)
12
13
 
13
14
  > Zero-config [Exa](https://exa.ai) web search for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh):
14
15
  > **no API key required** — a `WebSearchProvider` for the `ctx.web` seam with an
@@ -18,17 +19,128 @@ Built with [deepseek-v4-flash](https://api-docs.deepseek.com) inside DeepSeek Ha
18
19
 
19
20
  ## Supported versions
20
21
 
21
- `@tonydua/dsh-web-search-exa@0.1.4` is tested and supported with:
22
+ **Every published dsh version from `0.1.2-alpha.2` to `0.1.7-alpha.1` is
23
+ verified**, not merely declared: each one is installed in isolation, the plugin
24
+ is typechecked against that version's own declarations, and the test suite runs
25
+ against it. Reproduce with `bash scripts/compat-matrix.sh`.
22
26
 
23
- - `@deepseek-ai/dsh` `0.1.2-rc.1` (the current npm `latest` release)
24
- - `@deepseek-ai/dsh-web` `0.1.2-rc.1`
25
- - `@deepseek-ai/dsh-settings` `0.1.2-rc.1` (optional; enables live Settings integration)
26
- - `@deepseek-ai/dsh-launch-environment` `0.1.2-rc.1`
27
- - `@deepseek-ai/cordis` `4.0.2`
28
- - Node.js `>=18`
27
+ | dsh line | Verified | Notes |
28
+ |---|---|---|
29
+ | `0.1.2-alpha.2` … `0.1.2-alpha.5` | ✅ | oldest supported |
30
+ | `0.1.2-rc.1` | ✅ | |
31
+ | `0.1.3-alpha.2` | ✅ | |
32
+ | `0.1.5-alpha.1`, `0.1.5-alpha.2` | ✅ | |
33
+ | `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3` | ✅ | `0.1.5-rc.2` also verified end to end: a real `dsh --profile headless` task searched through the anonymous MCP path with no API key present |
34
+ | `0.1.6-alpha.1`, `0.1.6-alpha.2` | ✅ | |
35
+ | `0.1.7-alpha.1` | ✅ | settings service changed shape — see below |
36
+
37
+ ### Why the peer range looks like that
38
+
39
+ ```jsonc
40
+ "@deepseek-ai/dsh-web": ">=0.1.2-alpha.2 || >=0.1.3-alpha.2 || >=0.1.4-0 || >=0.1.5-alpha.1 || >=0.1.6-alpha.1 || >=0.1.7-alpha.1 || >=0.1.8"
41
+ ```
42
+
43
+ That enumeration is not decoration — it is the only form that installs on **every**
44
+ published version under **both** pnpm and npm. The rule that forces it:
45
+
46
+ > A pre-release version satisfies a range only if some comparator in that range
47
+ > carries a pre-release **on the same `major.minor.patch` tuple**.
48
+
49
+ So `>=0.1.2-rc.1` does **not** match `0.1.5-rc.2` — the tuples differ. A single
50
+ open-ended lower bound therefore cannot cover a project published as a series of
51
+ prereleases, and `*` would accept even a breaking `1.0`. Each `0.1.x` line that
52
+ ever shipped a prerelease needs its own comparator; `>=0.1.8` then carries every
53
+ future stable release, so the list only needs a new entry when dsh opens a new
54
+ `0.1.x` prerelease line.
55
+
56
+ Measured, on the real published tarball:
57
+
58
+ | range | npm installs | pnpm |
59
+ |---|---|---|
60
+ | `>=0.1.2-rc.1` (the earlier attempt) | **1 / 14** versions | 14 / 14 |
61
+ | enumerated (current) | **14 / 14** versions | 14 / 14 |
62
+
63
+ This was found by testing rather than reasoning: the open-ended range is fine on
64
+ pnpm, which is what `dsh plugin add` uses, and fails on npm for 13 of the 14
65
+ versions with `ERESOLVE`. If you install with npm and hit that on an older
66
+ release of this package, either upgrade, or pass `--legacy-peer-deps`.
67
+
68
+ ### What differs across versions
69
+
70
+ Auditing the real export surfaces of all 14 versions found the `ctx.web` seam
71
+ completely stable — `WebError` is exported from `dsh-web` and still extends
72
+ `HarnessError`, `launchEnvironmentOf` is present, and the settings service is
73
+ mounted at `ctx.settings` in every version. Two things do differ:
74
+
75
+ 1. **`0.1.7-alpha.1` replaced the settings API.** `SettingsProvider.installSection`
76
+ is gone; the service is now `SettingsForms`, which derives a configuration
77
+ page from the Config schema the Loader already holds for the entry
78
+ (`SettingsDescriptor.schema`, `autoGenerate`). Calling the old method
79
+ unconditionally threw a `TypeError` on that host, so the plugin loaded but
80
+ failed. It now probes for the method, calls it only when present, and
81
+ otherwise does nothing — on `0.1.7+` the Loader's schema is what feeds the
82
+ form, so there is nothing to register.
83
+ 2. **`0.1.7-alpha.1` peers `@deepseek-ai/cordis` `^4.0.3`** while the cordis
84
+ `latest` dist-tag still points at `4.0.2`. `4.0.3` is published; the tag is
85
+ simply behind. Install `@deepseek-ai/cordis@4.0.3` alongside a `0.1.7` host.
86
+ The matrix script pins this per version.
87
+
88
+ Also supported with: `@deepseek-ai/dsh-web`, `dsh-settings` (optional),
89
+ `dsh-launch-environment` across that whole range, and Node.js `>=22.19.0` (the
90
+ harness's own floor).
91
+
92
+ ### Profile-install note
93
+
94
+ dsh profiles set `autoInstallPeers: false`, and the harness's own services are
95
+ supplied at runtime by the dsh host instead of being resolved by pnpm. Add this
96
+ to the profile's `pnpm-workspace.yaml` so `dsh plugin add` stays warning-free:
97
+
98
+ ```yaml
99
+ peerDependencyRules:
100
+ ignoreMissing:
101
+ - '@deepseek-ai/cordis'
102
+ - '@deepseek-ai/dsh-*'
103
+ ```
29
104
 
30
- The dsh `0.1.2-rc.1` API is the compatibility baseline. The `0.1.5-alpha.1`
31
- alpha line is not part of this release's tested support matrix.
105
+ ### Degradation and failover
106
+
107
+ The keyless channel is a shared, best-effort endpoint. The provider reports its
108
+ own health rather than pretending to always work:
109
+
110
+ - 3 consecutive transient failures (5xx, 429, network, unparseable body) open a
111
+ circuit breaker for 5 minutes, during which `available()` returns `false`. One
112
+ successful search closes it again.
113
+ - A 4xx other than 429 does not trip it — that failure would repeat forever, so
114
+ hiding it would only delay the same error.
115
+ - Anonymous 429s raise `WEB_RATE_LIMITED` (not a generic `WEB_PROVIDER_ERROR`)
116
+ with a message naming `EXA_API_KEY`.
117
+ - The keyed REST path ignores the breaker: a paid endpoint's failures are yours
118
+ to see.
119
+
120
+ **Whether that turns into automatic failover is a harness-side decision.** The
121
+ seam picks exactly one usable provider and has no priority chain — with two
122
+ usable providers it raises `WEB_PROVIDER_AMBIGUOUS`. So:
123
+
124
+ - Pinning `searchProvider: exa` gives deterministic selection but *no* fallback:
125
+ when the breaker opens you get `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`.
126
+ - Leaving `searchProvider` unset gives up determinism: a degraded Exa stops
127
+ being a candidate, but if another provider (say `deepseek-official` with a
128
+ valid `DEEPSEEK_API_KEY`) is also usable, the seam reports ambiguity instead
129
+ of choosing it.
130
+
131
+ Pick whichever failure mode you prefer; the plugin cannot choose for you.
132
+
133
+ ### Building from source
134
+
135
+ ```sh
136
+ pnpm install
137
+ pnpm run build # tsdown -> lib/index.js + lib/index.d.ts
138
+ pnpm run typecheck # tsc --noEmit
139
+ pnpm test # builds, then runs the node:test suite against lib/
140
+ ```
141
+
142
+ `src/` is the source of truth; `lib/` is committed because both the published
143
+ tarball and git-based installs consume it.
32
144
 
33
145
  ## Features
34
146
 
@@ -83,18 +195,36 @@ The anonymous MCP path sends no credentials; attribution rides the
83
195
  `x-exa-source: dsh-anything` header. Results are normalized to the seam's
84
196
  `WebSearchSource` shape (`url`, `title`, `snippet`, `publishedAt`) and the seam
85
197
  enforces `maxResults` on the way back. Anonymous usage is rate-limited by Exa:
86
- an HTTP 429 surfaces as a `WEB_PROVIDER_ERROR` with a hint to configure an API
87
- key (which also switches to the REST path automatically).
198
+ an HTTP 429 surfaces as a distinct `WEB_RATE_LIMITED` code — not a generic
199
+ provider failure — with a hint to configure an API key (which also switches to
200
+ the REST path automatically).
88
201
 
89
202
  ## Installation (into a dsh profile)
90
203
 
91
- **One command from npm** (v0.1.4+ ships the `dsh.bundle` manifest — the bundle
92
- patch inserts the provider row, so no manual patch editing is needed):
204
+ > **One artifact, two doors.** CI packs this version's tarball, verifies it
205
+ > against every supported dsh version, attaches it to the GitHub Release, and
206
+ > publishes **that artifact** to npm — so the release asset and the npm tarball
207
+ > are one file, not two builds that happen to match.
208
+
209
+ **From npm** (v0.1.4+ ships the `dsh.bundle` manifest, so the bundle patch
210
+ inserts the provider row with no manual patch editing):
93
211
 
94
212
  ```powershell
95
213
  dsh plugin --profile web add @tonydua/dsh-web-search-exa
96
214
  ```
97
215
 
216
+ **From the GitHub Release** — the same tarball, for when npm is unreachable:
217
+
218
+ ```powershell
219
+ dsh plugin --profile web add https://github.com/TonyDua/dsh-web-search-exa/releases/latest/download/dsh-web-search-exa.tgz
220
+ ```
221
+
222
+ **From the repository** (tracks `main`, includes work not yet released):
223
+
224
+ ```powershell
225
+ dsh plugin --profile web add github:TonyDua/dsh-web-search-exa
226
+ ```
227
+
98
228
  Restart `dsh web`. **Without an API key** the official DeepSeek search
99
229
  provider is unavailable, so the seam auto-selects this provider — fully
100
230
  zero-config. **With a key configured**, select Exa explicitly in your own
@@ -244,9 +374,10 @@ only; a UI card is planned for the next version. Configure through
244
374
  [In the Web panel](#in-the-web-panel)).
245
375
 
246
376
  **Q: Which dsh versions are supported?**
247
- This release supports dsh `0.1.2-rc.1` and its matching `dsh-web`,
248
- `dsh-settings`, and `dsh-launch-environment` packages. The `0.1.5-alpha.1`
249
- line is not tested by this release.
377
+ Every published dsh version from `0.1.2-alpha.2` to `0.1.7-alpha.1`, plus future
378
+ `0.1.8+` stable releases. Each version is installed in isolation, typechecked
379
+ against its own declarations, and run through this package's test suite in CI —
380
+ see [Why the peer range looks like that](#why-the-peer-range-looks-like-that).
250
381
 
251
382
  ## Acknowledgements
252
383
 
package/README.zh.md CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  [English](README.md) | **简体中文**
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@tonydua/dsh-web-search-exa)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
6
- [![npm downloads](https://img.shields.io/npm/dm/@tonydua/dsh-web-search-exa)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
5
+ [![npm 版本](https://img.shields.io/npm/v/@tonydua/dsh-web-search-exa?label=npm)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
6
+ [![GitHub Release](https://img.shields.io/github/v/release/TonyDua/dsh-web-search-exa?label=release)](https://github.com/TonyDua/dsh-web-search-exa/releases/latest)
7
+ [![npm 下载量](https://img.shields.io/npm/dm/@tonydua/dsh-web-search-exa)](https://www.npmjs.com/package/@tonydua/dsh-web-search-exa)
7
8
  [![License](https://img.shields.io/npm/l/@tonydua/dsh-web-search-exa)](LICENSE)
9
+ [![dsh](https://img.shields.io/badge/dsh-0.1.2--alpha.2%20%E2%80%93%200.1.7--alpha.1-4c6?logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)
10
+ [![Node](https://img.shields.io/badge/node-%3E%3D22.19.0-339933?logo=node.js&logoColor=white)](package.json)
8
11
  [![GitHub stars](https://img.shields.io/github/stars/TonyDua/dsh-web-search-exa)](https://github.com/TonyDua/dsh-web-search-exa)
9
12
  [![GitHub issues](https://img.shields.io/github/issues/TonyDua/dsh-web-search-exa)](https://github.com/TonyDua/dsh-web-search-exa)
10
- [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white)](package.json)
11
- [![dsh](https://img.shields.io/badge/dsh-0.1.2--rc.1-4c6?logo=deepseek&logoColor=white)](https://www.npmjs.com/package/@deepseek-ai/dsh)
12
13
 
13
14
  > 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)提供**零配置**的 [Exa](https://exa.ai) 网页搜索:
14
15
  > **无需 API key** —— 一个 `ctx.web` seam 的 `WebSearchProvider`,内置匿名 MCP 兜底 + 带 key 的 REST 路径。
@@ -17,16 +18,110 @@
17
18
 
18
19
  ## 当前支持的版本
19
20
 
20
- `@tonydua/dsh-web-search-exa@0.1.4` 已针对以下版本测试并支持:
21
+ **npm 上从 `0.1.2-alpha.2` 到 `0.1.7-alpha.1` 的每一个 dsh 版本都经过实测**,不是只写在文档里:
22
+ 每个版本会被独立安装、用**该版本自己的类型声明**做类型检查、并跑完整测试套件。
23
+ 复现命令:`bash scripts/compat-matrix.sh`。
21
24
 
22
- - `@deepseek-ai/dsh` `0.1.2-rc.1`(当前 npm `latest` 发布线)
23
- - `@deepseek-ai/dsh-web` `0.1.2-rc.1`
24
- - `@deepseek-ai/dsh-settings` `0.1.2-rc.1`(可选;用于启用实时 Settings 集成)
25
- - `@deepseek-ai/dsh-launch-environment` `0.1.2-rc.1`
26
- - `@deepseek-ai/cordis` `4.0.2`
27
- - Node.js `>=18`
25
+ | dsh 版本线 | 实测 | 说明 |
26
+ |---|---|---|
27
+ | `0.1.2-alpha.2` … `0.1.2-alpha.5` | ✅ | 最老的受支持基线 |
28
+ | `0.1.2-rc.1` | ✅ | |
29
+ | `0.1.3-alpha.2` | ✅ | |
30
+ | `0.1.5-alpha.1`、`0.1.5-alpha.2` | ✅ | |
31
+ | `0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.5-rc.3` | ✅ | `0.1.5-rc.2` 另有端到端验证:无 API key 下用真实 `dsh --profile headless` 走通匿名 MCP |
32
+ | `0.1.6-alpha.1`、`0.1.6-alpha.2` | ✅ | |
33
+ | `0.1.7-alpha.1` | ✅ | settings 服务改了形态,见下 |
34
+
35
+ ### 为什么 peer 范围长这样
36
+
37
+ ```jsonc
38
+ "@deepseek-ai/dsh-web": ">=0.1.2-alpha.2 || >=0.1.3-alpha.2 || >=0.1.4-0 || >=0.1.5-alpha.1 || >=0.1.6-alpha.1 || >=0.1.7-alpha.1 || >=0.1.8"
39
+ ```
40
+
41
+ 这串枚举不是装饰,而是**在 pnpm 与 npm 下都能装遍所有已发布版本**的唯一写法。迫使它成立的规则是:
42
+
43
+ > 一个 prerelease 版本要满足某范围,该范围中必须存在一个比较器,其 prerelease 位于
44
+ > **相同的 `major.minor.patch` tuple** 上。
45
+
46
+ 所以 `>=0.1.2-rc.1` **匹配不到** `0.1.5-rc.2`——tuple 不同。这意味着单一开区间下界
47
+ 无法覆盖"以一串 prerelease 形式发布的项目",而 `*` 又会连未来的破坏性 `1.0` 一起放行。
48
+ 凡是发布过 prerelease 的 `0.1.x` 线都需要自己的比较器;`>=0.1.8` 负责承接之后所有
49
+ 稳定版,因此只有当 dsh 开出新的 `0.1.x` prerelease 线时才需要追加条目。
50
+
51
+ 在**真实发布物**上实测:
52
+
53
+ | 范围 | npm 可安装版本数 | pnpm |
54
+ |---|---|---|
55
+ | `>=0.1.2-rc.1`(先前写法) | **1 / 14** | 14 / 14 |
56
+ | 枚举写法(当前) | **14 / 14** | 14 / 14 |
57
+
58
+ 这个结论是**测出来的,不是推出来的**:开区间在 pnpm(也就是 `dsh plugin add` 实际使用的
59
+ 包管理器)下没问题,但在 npm 下会对 14 个版本中的 13 个报 `ERESOLVE`。若你用 npm 安装
60
+ 旧版本的本插件时遇到该错误,升级即可,或临时加 `--legacy-peer-deps`。
61
+
62
+ ### 各版本之间究竟差在哪
63
+
64
+ 我把 14 个版本的**真实导出面**逐个探测过,结论是 `ctx.web` seam **完全稳定**:
65
+ `WebError` 始终由 `dsh-web` 导出且继承 `HarnessError`,`launchEnvironmentOf` 始终存在,
66
+ `ctx.settings` 在每个版本都被挂载。真正有差异的只有两处:
67
+
68
+ 1. **`0.1.7-alpha.1` 换掉了 settings API。** `SettingsProvider.installSection` 被移除,
69
+ 服务变为 `SettingsForms`,它直接从 Loader 已持有的 Config schema 派生配置页
70
+ (`SettingsDescriptor.schema`、`autoGenerate`)。旧代码无条件调用该方法会在该版本上抛
71
+ `TypeError`——插件能加载但会失败。现在改为探测该方法:存在才调用,不存在则什么都不做;
72
+ 在 `0.1.7+` 上由 Loader 的 schema 驱动表单,插件无需注册任何东西。
73
+ 2. **`0.1.7-alpha.1` 依赖 `@deepseek-ai/cordis` `^4.0.3`**,而 cordis 的 `latest`
74
+ dist-tag 仍指向 `4.0.2`。`4.0.3` 已发布,只是 tag 落后。搭配 `0.1.7` 宿主时请安装
75
+ `@deepseek-ai/cordis@4.0.3`。矩阵脚本已按版本固化这一点。
76
+
77
+ 同样支持:`@deepseek-ai/dsh-web`、`dsh-settings`(可选)、`dsh-launch-environment`
78
+ 覆盖上述整个范围;Node.js `>=22.19.0`(与 harness 自身下限一致)。
79
+
80
+ ### profile 安装注意事项
81
+
82
+ dsh profile 默认 `autoInstallPeers: false`,且 harness 自身的服务由 dsh 宿主在运行时
83
+ 提供、并不经 pnpm 解析。把下面这段加进 profile 的 `pnpm-workspace.yaml`,
84
+ `dsh plugin add` 就不会再报警告:
85
+
86
+ ```yaml
87
+ peerDependencyRules:
88
+ ignoreMissing:
89
+ - '@deepseek-ai/cordis'
90
+ - '@deepseek-ai/dsh-*'
91
+ ```
28
92
 
29
- dsh `0.1.2-rc.1` 是本版本的兼容基线;`0.1.5-alpha.1` alpha 线不在本版本的测试支持矩阵内。
93
+ ### 降级与回退
94
+
95
+ 免 key 通道是共享的尽力而为端点。本 provider 会如实汇报自身健康状况,而不是假装永远可用:
96
+
97
+ - 连续 3 次瞬时失败(5xx、429、网络错误、响应体无法解析)会打开熔断器并冷却 5 分钟,
98
+ 期间 `available()` 返回 `false`;任意一次成功搜索即关闭熔断器。
99
+ - 429 以外的 4xx **不会**触发熔断——这类失败会永远重复,把它藏进冷却期只是推迟同一个错误。
100
+ - 匿名通道的 429 抛出 `WEB_RATE_LIMITED`(而非笼统的 `WEB_PROVIDER_ERROR`),
101
+ 错误信息里直接点名 `EXA_API_KEY`。
102
+ - 配了 key 的 REST 路径不受熔断影响:付费端点的失败应当让你看到。
103
+
104
+ **但"是否真的自动回退"取决于 harness 侧。** seam 只会选中唯一一个可用 provider,
105
+ 并不存在优先级链——同时存在多个可用 provider 时会抛 `WEB_PROVIDER_AMBIGUOUS`。因此:
106
+
107
+ - 写死 `searchProvider: exa`:选择确定,但**没有**回退;熔断打开时得到
108
+ `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`。
109
+ - 不写 `searchProvider`:牺牲确定性。Exa 降级后确实不再是候选,但如果另一个
110
+ provider(比如带有效 `DEEPSEEK_API_KEY` 的 `deepseek-official`)也可用,
111
+ seam 会报"多个可用"而不是替你挑一个。
112
+
113
+ 两种失败模式各有利弊,插件无法替你做这个决定。
114
+
115
+ ### 从源码构建
116
+
117
+ ```sh
118
+ pnpm install
119
+ pnpm run build # tsdown -> lib/index.js + lib/index.d.ts
120
+ pnpm run typecheck # tsc --noEmit
121
+ pnpm test # 先构建,再对 lib/ 跑 node:test 套件
122
+ ```
123
+
124
+ `src/` 是唯一事实来源;`lib/` 仍然提交进仓库,因为 npm 发布包与基于 git 的安装都依赖它。
30
125
 
31
126
  ## 特性
32
127
 
@@ -63,16 +158,30 @@ DeepSeek Harness 自带官方 Exa 提供方 [`@deepseek-ai/dsh-web-search-exa`](
63
158
  | 配置了 `apiKey` / `EXA_API_KEY` | REST `POST /search`,`Authorization: Bearer` | `https://api.exa.ai/search`(可用 `baseURL` 配置) |
64
159
  | 未配置任何 key | 匿名 MCP `tools/call web_search_exa`(JSON-RPC 2.0,无凭据) | `https://mcp.exa.ai/mcp`(可配置) |
65
160
 
66
- 匿名 MCP 路径不发送任何凭据,来源标识通过 `x-exa-source: dsh-anything` 头携带。结果按 seam 的 `WebSearchSource` 形状规范化(`url`、`title`、`snippet`、`publishedAt`),`maxResults` 由 seam 在返回路径上强制执行。匿名使用受 Exa 限流:HTTP 429 会以 `WEB_PROVIDER_ERROR` 呈现,并提示配置 API key(配置后自动切换到 REST 路径)。
161
+ 匿名 MCP 路径不发送任何凭据,来源标识通过 `x-exa-source: dsh-anything` 头携带。结果按 seam 的 `WebSearchSource` 形状规范化(`url`、`title`、`snippet`、`publishedAt`),`maxResults` 由 seam 在返回路径上强制执行。匿名使用受 Exa 限流:HTTP 429 会以独立的 `WEB_RATE_LIMITED` 码呈现(而非笼统的 provider 失败),并提示配置 API key(配置后自动切换到 REST 路径)。
67
162
 
68
163
  ## 安装(装入 dsh profile)
69
164
 
70
- **一条命令从 npm 安装**(v0.1.4+ 自带 `dsh.bundle` manifest——bundle patch 会自动插入 provider 行,无需手动改 patch):
165
+ > **同一个产物,两个入口。** CI 打包本版本的 tarball、在**每一个**受支持的 dsh 版本上验证、把它挂到 GitHub Release,并把**这个产物本身**发布到 npm——因此 Release 附件与 npm 上的 tarball 是同一个文件,而不是两次恰好一致的构建。
166
+
167
+ **从 npm 安装**(v0.1.4+ 自带 `dsh.bundle` manifest,bundle patch 会自动插入 provider 行,无需手动改 patch):
71
168
 
72
169
  ```powershell
73
170
  dsh plugin --profile web add @tonydua/dsh-web-search-exa
74
171
  ```
75
172
 
173
+ **从 GitHub Release 安装**——同一份 tarball,npm 不可达时用:
174
+
175
+ ```powershell
176
+ dsh plugin --profile web add https://github.com/TonyDua/dsh-web-search-exa/releases/latest/download/dsh-web-search-exa.tgz
177
+ ```
178
+
179
+ **从仓库安装**(跟随 `main`,包含尚未发布的改动):
180
+
181
+ ```powershell
182
+ dsh plugin --profile web add github:TonyDua/dsh-web-search-exa
183
+ ```
184
+
76
185
  重启 `dsh web` 生效。**无 API key 时**官方 DeepSeek 搜索提供方不可用,seam 会自动选中本插件——完全零配置。**配了 key 时**,需在你的 `$DSH_HOME/profiles/web/cordis.patch.yml`(在 bundle patch 之后应用)里显式选中 Exa:
77
186
 
78
187
  ```yaml
@@ -175,7 +284,7 @@ dsh plugin --profile web add ../plugins/dsh-web-search-exa
175
284
  本版本只在服务端注册了 `web-search-exa` 设置命名空间;UI 卡片计划在下一版本提供。现阶段通过 `cordis.patch.yml` 或环境变量配置(见[在 Web 面板中的呈现](#在-web-面板中的呈现))。
176
285
 
177
286
  **Q: 支持哪些 dsh 版本?**
178
- 本版本支持 dsh `0.1.2-rc.1` 及其匹配的 `dsh-web`、`dsh-settings`、`dsh-launch-environment` 包;`0.1.5-alpha.1` 线未经本版本测试。
287
+ `0.1.2-alpha.2` 到 `0.1.7-alpha.1` 之间**每一个**已发布的 dsh 版本,以及未来的 `0.1.8+` 稳定版。每个版本都会在 CI 里被独立安装、用该版本自己的类型声明做类型检查,并跑一遍本包的测试套件——见[为什么 peer 范围长这样](#为什么-peer-范围长这样)。
179
288
 
180
289
  ## 致谢(Acknowledgements)
181
290