dsh-web-search-plugin 0.3.0 → 0.3.1
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 +15 -0
- package/lib/client.js +19 -9
- package/lib/index.js +8 -7
- package/lib/rest.js +6 -2
- package/lib/shared.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
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
|
+
## [0.3.1] - 2026-08-30
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- **Tavily keyless 模式失效**:通用 REST 后端不再在 keyless 模式下强制解析 API key;无 key 时只发送 `x-tavily-access-mode: keyless`,可正常使用免费额度。
|
|
10
|
+
- **POST 类 provider 的 count 参数类型错误**:Tavily `max_results`、Serper `num`、Exa `num_results`、Firecrawl `limit` 等在 JSON body 中恢复为数字,不再被强转字符串。
|
|
11
|
+
- **Scavio / Firecrawl 设置卡严重卡死**:补齐浏览器端缺失的 `scavioApiKeyEnv` / `scavioBaseURL` / `firecrawlApiKeyEnv` / `firecrawlBaseURL` 表单字段与投影,修复切换后输入框无法输入、引擎无法切换的问题。
|
|
12
|
+
- **Scavio / Firecrawl 默认凭据名错误**:未显式填写 keyEnv 时分别回退到 `SCAVIO_API_KEY` / `FIRECRAWL_API_KEY`,不再错误使用 `EXA_API_KEY`。
|
|
13
|
+
- **Brave 环境代理回退丢失**:恢复 `HTTPS_PROXY` / `HTTP_PROXY` 环境变量回退,未显式配置 `proxy` 时也能按环境代理请求。
|
|
14
|
+
- `User-Agent` 更新为 `0.3.1`。
|
|
15
|
+
|
|
16
|
+
### 优化
|
|
17
|
+
|
|
18
|
+
- 内置 REST 渠道(Serper / SerpApi / Exa / SearXNG / Scavio / Firecrawl)的 Base URL 默认值预填到设置卡,切换渠道后不再是空 URL。
|
|
19
|
+
|
|
5
20
|
## [0.3.0] - 2026-08-25
|
|
6
21
|
|
|
7
22
|
### 新增
|
package/lib/client.js
CHANGED
|
@@ -27,12 +27,12 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
const NS = "dsh-web-search-plugin";
|
|
28
28
|
/** Built-in REST providers (mirrors `lib/providers.js`; browser bundle is self-contained). */
|
|
29
29
|
const BUILTIN_REST_PROVIDERS = [
|
|
30
|
-
{ id: "serper", name: "Serper", officialUrl: "https://serper.dev/", keyRequired: true, keyField: "serperApiKey", keyEnvField: "serperApiKeyEnv", baseURLField: "serperBaseURL" },
|
|
31
|
-
{ id: "serpapi", name: "SerpApi", officialUrl: "https://serpapi.com/", keyRequired: true, keyField: "serpapiApiKey", keyEnvField: "serpapiApiKeyEnv", baseURLField: "serpapiBaseURL" },
|
|
32
|
-
{ id: "exa", name: "Exa", officialUrl: "https://dashboard.exa.ai/", keyRequired: true, keyField: "exaApiKey", keyEnvField: "exaApiKeyEnv", baseURLField: "exaBaseURL" },
|
|
33
|
-
{ id: "searxng", name: "SearXNG", officialUrl: "https://docs.searxng.org/", keyRequired: false, keyField: "", keyEnvField: "", baseURLField: "searxngBaseURL" },
|
|
34
|
-
{ id: "scavio", name: "Scavio", officialUrl: "https://scavio.dev/", keyRequired: true, keyField: "scavioApiKey", keyEnvField: "scavioApiKeyEnv", baseURLField: "scavioBaseURL" },
|
|
35
|
-
{ id: "firecrawl", name: "Firecrawl", officialUrl: "https://firecrawl.dev/", keyRequired: true, keyField: "firecrawlApiKey", keyEnvField: "firecrawlApiKeyEnv", baseURLField: "firecrawlBaseURL" }
|
|
30
|
+
{ id: "serper", name: "Serper", officialUrl: "https://serper.dev/", defaultBaseURL: "https://google.serper.dev", keyRequired: true, keyField: "serperApiKey", keyEnvField: "serperApiKeyEnv", baseURLField: "serperBaseURL" },
|
|
31
|
+
{ id: "serpapi", name: "SerpApi", officialUrl: "https://serpapi.com/", defaultBaseURL: "https://serpapi.com", keyRequired: true, keyField: "serpapiApiKey", keyEnvField: "serpapiApiKeyEnv", baseURLField: "serpapiBaseURL" },
|
|
32
|
+
{ id: "exa", name: "Exa", officialUrl: "https://dashboard.exa.ai/", defaultBaseURL: "https://api.exa.ai", keyRequired: true, keyField: "exaApiKey", keyEnvField: "exaApiKeyEnv", baseURLField: "exaBaseURL" },
|
|
33
|
+
{ id: "searxng", name: "SearXNG", officialUrl: "https://docs.searxng.org/", defaultBaseURL: "https://searx.be", keyRequired: false, keyField: "", keyEnvField: "", baseURLField: "searxngBaseURL" },
|
|
34
|
+
{ id: "scavio", name: "Scavio", officialUrl: "https://scavio.dev/", defaultBaseURL: "https://api.scavio.dev", keyRequired: true, keyField: "scavioApiKey", keyEnvField: "scavioApiKeyEnv", baseURLField: "scavioBaseURL" },
|
|
35
|
+
{ id: "firecrawl", name: "Firecrawl", officialUrl: "https://firecrawl.dev/", defaultBaseURL: "https://api.firecrawl.dev", keyRequired: true, keyField: "firecrawlApiKey", keyEnvField: "firecrawlApiKeyEnv", baseURLField: "firecrawlBaseURL" }
|
|
36
36
|
];
|
|
37
37
|
/** Find a built-in REST provider by id. */
|
|
38
38
|
function builtinRest(id) {
|
|
@@ -364,7 +364,11 @@ window.__ModuleLoader__.load({
|
|
|
364
364
|
textField("serpapiBaseURL"),
|
|
365
365
|
textField("exaApiKeyEnv"),
|
|
366
366
|
textField("exaBaseURL"),
|
|
367
|
-
textField("searxngBaseURL")
|
|
367
|
+
textField("searxngBaseURL"),
|
|
368
|
+
textField("scavioApiKeyEnv"),
|
|
369
|
+
textField("scavioBaseURL"),
|
|
370
|
+
textField("firecrawlApiKeyEnv"),
|
|
371
|
+
textField("firecrawlBaseURL")
|
|
368
372
|
].map((spec) => [spec.field, spec]));
|
|
369
373
|
this.staged = new Map();
|
|
370
374
|
this.saving = false;
|
|
@@ -577,7 +581,7 @@ window.__ModuleLoader__.load({
|
|
|
577
581
|
restKeyRef(rest) {
|
|
578
582
|
const declared = this.sectionValue(rest.keyEnvField);
|
|
579
583
|
if (declared !== void 0 && declared.length > 0) return declared;
|
|
580
|
-
return rest.id === "serper" ? "SERPER_API_KEY" : rest.id === "serpapi" ? "SERPAPI_API_KEY" : "EXA_API_KEY";
|
|
584
|
+
return rest.id === "serper" ? "SERPER_API_KEY" : rest.id === "serpapi" ? "SERPAPI_API_KEY" : rest.id === "exa" ? "EXA_API_KEY" : rest.id === "scavio" ? "SCAVIO_API_KEY" : "FIRECRAWL_API_KEY";
|
|
581
585
|
}
|
|
582
586
|
async readCredential() {
|
|
583
587
|
const ref = this.refOf();
|
|
@@ -669,6 +673,12 @@ window.__ModuleLoader__.load({
|
|
|
669
673
|
exaApiKeyEnv: this.form.field("exaApiKeyEnv"),
|
|
670
674
|
exaBaseURL: this.form.field("exaBaseURL"),
|
|
671
675
|
searxngBaseURL: this.form.field("searxngBaseURL"),
|
|
676
|
+
scavioApiKey: this.form.field("scavioApiKey"),
|
|
677
|
+
scavioApiKeyEnv: this.form.field("scavioApiKeyEnv"),
|
|
678
|
+
scavioBaseURL: this.form.field("scavioBaseURL"),
|
|
679
|
+
firecrawlApiKey: this.form.field("firecrawlApiKey"),
|
|
680
|
+
firecrawlApiKeyEnv: this.form.field("firecrawlApiKeyEnv"),
|
|
681
|
+
firecrawlBaseURL: this.form.field("firecrawlBaseURL"),
|
|
672
682
|
apiKeyConfigured: this.form.credential.configured,
|
|
673
683
|
apiKeyWritable: this.form.credential.writable,
|
|
674
684
|
toastKey: this.form.toastKey
|
|
@@ -1075,7 +1085,7 @@ window.__ModuleLoader__.load({
|
|
|
1075
1085
|
fieldRow(t("apiKey"), keyHint, secretInput({ id: "ws-" + restMeta.id + "-key", value: state[restMeta.keyField]?.text ?? "", disabled: !state.apiKeyWritable, placeholder: state.apiKeyConfigured ? t("apiKeySet") : t("apiKeyUnset"), onChange: (e) => actions.edit(restMeta.keyField, e.target.value) })),
|
|
1076
1086
|
row(restMeta.keyEnvField, t("apiKeyEnv"), t("apiKeyEnvHint"), textInput({ id: "ws-" + restMeta.id + "-keyref", value: state[restMeta.keyEnvField]?.text ?? "", disabled, onChange: (e) => actions.edit(restMeta.keyEnvField, e.target.value) }))
|
|
1077
1087
|
) : null,
|
|
1078
|
-
row(restMeta.baseURLField, t("baseURL"), t("baseURLHint"), textInput({ id: "ws-" + restMeta.id + "-base", value: state[restMeta.baseURLField]?.text
|
|
1088
|
+
row(restMeta.baseURLField, t("baseURL"), t("baseURLHint"), textInput({ id: "ws-" + restMeta.id + "-base", value: state[restMeta.baseURLField]?.text || restMeta.defaultBaseURL || "", disabled, onChange: (e) => actions.edit(restMeta.baseURLField, e.target.value) })),
|
|
1079
1089
|
maxResultsRow
|
|
1080
1090
|
) : null,
|
|
1081
1091
|
react.createElement("div", { style: { borderTop: "1px solid var(--dsw-alias-border-l2)", justifyContent: "flex-end", alignItems: "center", gap: "8px", padding: "12px 0 4px", display: "flex" } },
|
package/lib/index.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import z from "@deepseek-ai/schemastery";
|
|
20
20
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
21
|
+
import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
21
22
|
import { MAX_RESULTS_CAP } from "./shared.js";
|
|
22
23
|
import { TAVILY_DEFAULT_API_KEY_ENV, TAVILY_DEFAULT_BASE_URL, TavilySearchProvider, resolveTavilyOptions } from "./tavily.js";
|
|
23
24
|
import { BRAVE_DEFAULT_API_KEY_ENV, BRAVE_DEFAULT_BASE_URL, BraveSearchProvider, resolveBraveOptions } from "./brave.js";
|
|
@@ -73,33 +74,33 @@ const Config = z.object({
|
|
|
73
74
|
/** Credential reference for Serper search. */
|
|
74
75
|
serperApiKeyEnv: z.string().role("credential-ref").default("SERPER_API_KEY"),
|
|
75
76
|
/** Serper endpoint base. `/search` is appended. */
|
|
76
|
-
serperBaseURL: z.string().default(""),
|
|
77
|
+
serperBaseURL: z.string().default("https://google.serper.dev"),
|
|
77
78
|
/** Literal SerpApi API key; prefer {@link serpapiApiKeyEnv}. */
|
|
78
79
|
serpapiApiKey: z.string().role("secret").default(""),
|
|
79
80
|
/** Credential reference for SerpApi search. */
|
|
80
81
|
serpapiApiKeyEnv: z.string().role("credential-ref").default("SERPAPI_API_KEY"),
|
|
81
82
|
/** SerpApi endpoint base. `/search.json` is appended. */
|
|
82
|
-
serpapiBaseURL: z.string().default(""),
|
|
83
|
+
serpapiBaseURL: z.string().default("https://serpapi.com"),
|
|
83
84
|
/** Literal Exa API key; prefer {@link exaApiKeyEnv}. */
|
|
84
85
|
exaApiKey: z.string().role("secret").default(""),
|
|
85
86
|
/** Credential reference for Exa search. */
|
|
86
87
|
exaApiKeyEnv: z.string().role("credential-ref").default("EXA_API_KEY"),
|
|
87
88
|
/** Exa endpoint base. `/search` is appended. */
|
|
88
|
-
exaBaseURL: z.string().default(""),
|
|
89
|
+
exaBaseURL: z.string().default("https://api.exa.ai"),
|
|
89
90
|
/** Optional SearXNG instance base URL (self-hosted). `/search` is appended. */
|
|
90
|
-
searxngBaseURL: z.string().default(""),
|
|
91
|
+
searxngBaseURL: z.string().default("https://searx.be"),
|
|
91
92
|
/** Literal Scavio API key; prefer {@link scavioApiKeyEnv}. */
|
|
92
93
|
scavioApiKey: z.string().role("secret").default(""),
|
|
93
94
|
/** Credential reference for Scavio search. */
|
|
94
95
|
scavioApiKeyEnv: z.string().role("credential-ref").default("SCAVIO_API_KEY"),
|
|
95
96
|
/** Scavio endpoint base. `/api/v2/google` is appended. */
|
|
96
|
-
scavioBaseURL: z.string().default(""),
|
|
97
|
+
scavioBaseURL: z.string().default("https://api.scavio.dev"),
|
|
97
98
|
/** Literal Firecrawl API key; prefer {@link firecrawlApiKeyEnv}. */
|
|
98
99
|
firecrawlApiKey: z.string().role("secret").default(""),
|
|
99
100
|
/** Credential reference for Firecrawl search. */
|
|
100
101
|
firecrawlApiKeyEnv: z.string().role("credential-ref").default("FIRECRAWL_API_KEY"),
|
|
101
102
|
/** Firecrawl endpoint base. `/v2/search` is appended. */
|
|
102
|
-
firecrawlBaseURL: z.string().default(""),
|
|
103
|
+
firecrawlBaseURL: z.string().default("https://api.firecrawl.dev"),
|
|
103
104
|
/** Literal DeepSeek API key; prefer {@link deepseekApiKeyEnv}. */
|
|
104
105
|
deepseekApiKey: z.string().role("secret").default(""),
|
|
105
106
|
/** Credential reference for DeepSeek search. */
|
|
@@ -176,7 +177,7 @@ function resolveOptions(ctx, config) {
|
|
|
176
177
|
// metadata row's default is not enough (self-hosted SearXNG, proxied
|
|
177
178
|
// providers, custom key refs).
|
|
178
179
|
const keySourceByProvider = {
|
|
179
|
-
brave: { literal: config.braveApiKey, envName: config.braveApiKeyEnv ?? BRAVE_DEFAULT_API_KEY_ENV, baseURL: config.braveBaseURL ?? "", proxy: config.proxy },
|
|
180
|
+
brave: { literal: config.braveApiKey, envName: config.braveApiKeyEnv ?? BRAVE_DEFAULT_API_KEY_ENV, baseURL: config.braveBaseURL ?? "", proxy: config.proxy || launchEnvironmentOf(ctx).get("HTTPS_PROXY")?.value || launchEnvironmentOf(ctx).get("HTTP_PROXY")?.value || "" },
|
|
180
181
|
tavily: { literal: config.apiKey, envName: config.apiKeyEnv ?? TAVILY_DEFAULT_API_KEY_ENV, baseURL: config.baseURL ?? "" },
|
|
181
182
|
serper: { literal: config.serperApiKey, envName: config.serperApiKeyEnv ?? "SERPER_API_KEY", baseURL: config.serperBaseURL ?? "" },
|
|
182
183
|
serpapi: { literal: config.serpapiApiKey, envName: config.serpapiApiKeyEnv ?? "SERPAPI_API_KEY", baseURL: config.serpapiBaseURL ?? "" },
|
package/lib/rest.js
CHANGED
|
@@ -188,7 +188,7 @@ export class RestSearchProvider {
|
|
|
188
188
|
const query = {
|
|
189
189
|
[queryParam]: request.query
|
|
190
190
|
};
|
|
191
|
-
if (countParam !== "") query[countParam] = String(count);
|
|
191
|
+
if (countParam !== "") query[countParam] = queryIn === "query" ? String(count) : count;
|
|
192
192
|
const keyed = options.keyed === true;
|
|
193
193
|
for (const param of options.params ?? []) {
|
|
194
194
|
const resolved = resolveParam(param, { keyed, ...options.settings ?? {} });
|
|
@@ -295,8 +295,12 @@ export class RestSearchProvider {
|
|
|
295
295
|
*/
|
|
296
296
|
export function resolveRestProvider(ctx, row, opts) {
|
|
297
297
|
const { config, settings } = opts;
|
|
298
|
-
|
|
298
|
+
let auth = row.auth ?? { type: "none" };
|
|
299
299
|
const keyed = opts.keyed === true;
|
|
300
|
+
// A provider can be keyless by default (e.g. Tavily keyless mode); when no
|
|
301
|
+
// key is expected, drop the auth template so the generic backend does not
|
|
302
|
+
// demand a credential before the request.
|
|
303
|
+
if (row.keyRequired === false && keyed !== true) auth = { type: "none" };
|
|
300
304
|
const base = {
|
|
301
305
|
baseURL: row.baseURL,
|
|
302
306
|
method: row.method ?? "POST",
|
package/lib/shared.js
CHANGED
|
@@ -7,7 +7,7 @@ import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
|
|
|
7
7
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
8
8
|
|
|
9
9
|
/** Attribution header sent on every request. */
|
|
10
|
-
export const USER_AGENT = "dsh-web-search-plugin/0.
|
|
10
|
+
export const USER_AGENT = "dsh-web-search-plugin/0.3.1";
|
|
11
11
|
/** Shared cap: Tavily `max_results` and Brave `count` both accept 1..20. */
|
|
12
12
|
export const MAX_RESULTS_CAP = 20;
|
|
13
13
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-web-search-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Unified web search for the DeepSeek Harness seam (ctx.web): DeepSeek (official), Tavily, Brave, Serper, SerpApi, Exa, SearXNG, Scavio, and Firecrawl backends",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh-plugin",
|