@klarkxy/dsh-web-search-manager 0.1.7 → 0.1.9
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.md +27 -6
- package/docs/README.zh-CN.md +27 -6
- package/icon.svg +15 -0
- package/lib/client.inner.cjs +5 -7
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +5 -7
- package/lib/index.js +108 -9
- package/lib/index.js.map +1 -1
- package/package.json +47 -23
package/README.md
CHANGED
|
@@ -6,25 +6,46 @@ Configure DSH web search providers and page fetching from one settings page.
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
Requires Node.js ≥22 and DSH `0.1.7-rc.2`. No repository build is needed.
|
|
9
|
+
Requires Node.js ≥22 and DSH `0.1.7-rc.2`. No repository build is needed. Install:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @klarkxy/dsh-web-search-manager
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then load it:
|
|
10
16
|
|
|
11
17
|
```sh
|
|
12
18
|
dsh plugin --profile web add @klarkxy/dsh-web-search-manager
|
|
13
19
|
```
|
|
14
20
|
|
|
15
|
-
Restart DSH Web and open **
|
|
21
|
+
Restart DSH Web and open **Plugins → Web search**. For agent access, add this entry to the plugin list in its `agent.cordis.yml`:
|
|
16
22
|
|
|
17
23
|
```yaml
|
|
18
24
|
- name: '@klarkxy/dsh-web-search-manager/tools'
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
DSH Editor already includes the plugin, its tools, and the Tavily adapter.
|
|
22
|
-
|
|
23
27
|
## Configure search
|
|
24
28
|
|
|
25
|
-
DuckDuckGo is enabled by default and requires no key. Enable another provider and configure credentials in settings if needed; one ready provider is enough.
|
|
29
|
+
The settings page has two tabs: **搜索服务** manages providers, and **请求限制** sets request limits. DuckDuckGo is enabled by default and requires no key. Enable another provider and configure credentials in settings if needed; one ready provider is enough. Drag providers to reorder them; the first ready provider by priority handles each request. Missing credentials are skipped; failed requests do not automatically switch providers.
|
|
30
|
+
|
|
31
|
+
Built-in search backends:
|
|
32
|
+
|
|
33
|
+
- **DuckDuckGo**: no registration and no key; the default backend, free of charge.
|
|
34
|
+
- **DeepSeek 搜索**: shares the DeepSeek API key from model settings (`DEEPSEEK_API_KEY`); may incur additional search charges on top of model usage.
|
|
35
|
+
- **Exa**: an independent Search API; requires its own key (`DSH_EDITOR_WEB_EXA_API_KEY`). The Exa provider is declared as a runtime dependency of this plugin.
|
|
36
|
+
- **Brave**: the Brave Search API; requires `DSH_EDITOR_WEB_BRAVE_API_KEY`.
|
|
37
|
+
- **博查**: a Chinese web search API; requires `DSH_EDITOR_WEB_BOCHA_API_KEY`.
|
|
38
|
+
- **Serper**: a Google results API; requires `DSH_EDITOR_WEB_SERPER_API_KEY`.
|
|
39
|
+
- **Firecrawl**: a web search and extraction API; requires `DSH_EDITOR_WEB_FIRECRAWL_API_KEY`.
|
|
40
|
+
- **Tavily**: a Search API fixed to basic search depth, never auto-upgraded; requires `DSH_EDITOR_WEB_TAVILY_API_KEY`. The adapter is built into this plugin; the former standalone `dsh-web-search-tavily` package is retired.
|
|
41
|
+
|
|
42
|
+
Enabling search also enables public-page fetching. The built-in `http` route first requests `https://r.jina.ai/<target URL>` anonymously and returns Reader's text/Markdown. HTTP errors (including 429), Reader timeouts, network failures, and empty or invalid responses fall back once to the existing direct HTTP fetcher. This fallback is only for page fetching; search backend selection is unchanged. No Jina registration, API key, billing configuration, or new dependency is required.
|
|
43
|
+
|
|
44
|
+
Target URLs are sent to Jina, a third-party service; do not submit URLs containing private tokens or other sensitive data. No cookies or authorization headers are added. IP literals, obvious local hostnames, credentialed URLs, and already-prefixed Reader URLs skip Jina and remain subject to the original HTTP fetcher's policy. Both stages use the existing HTTP transport; the Reader stage uses at most half of `timeoutMs`, capped at 15 seconds, leaving time for direct fallback. Caller cancellation, disabling network access, or the overall deadline stops the request without starting a fallback. Results retain the requested URL rather than the Reader proxy URL.
|
|
45
|
+
|
|
46
|
+
Request limits cap each query's results (`maxResults`), queries per tool call (`maxQueries`), the total request timeout (`timeoutMs`), and fetched page characters (`maxFetchChars`). Each HTTP response retains the 5 MB byte cap. A connection test sends a fixed query, not manuscript content, and may incur a provider charge.
|
|
26
47
|
|
|
27
|
-
|
|
48
|
+
With the tools entry enabled, the agent receives the official `web_search` and `web_fetch` tools from `@deepseek-ai/dsh-tool-web`, mounted only while search and fetching are enabled.
|
|
28
49
|
|
|
29
50
|
Only configure trusted HTTPS endpoints: API keys are sent to them. Credentials use DSH storage; protection at rest depends on its configured backend. Usage counters are call attempts, not billing statements or a spending cap.
|
|
30
51
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -6,25 +6,46 @@
|
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
|
-
需要 Node.js ≥22、DSH `0.1.7-rc.2
|
|
9
|
+
需要 Node.js ≥22、DSH `0.1.7-rc.2`,无需构建本仓库。安装:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @klarkxy/dsh-web-search-manager
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
然后加载:
|
|
10
16
|
|
|
11
17
|
```sh
|
|
12
18
|
dsh plugin --profile web add @klarkxy/dsh-web-search-manager
|
|
13
19
|
```
|
|
14
20
|
|
|
15
|
-
重启 DSH Web
|
|
21
|
+
重启 DSH Web,打开「插件 → 网络搜索」。模型需要使用搜索工具时,在所用 `agent.cordis.yml` 的插件列表加入:
|
|
16
22
|
|
|
17
23
|
```yaml
|
|
18
24
|
- name: '@klarkxy/dsh-web-search-manager/tools'
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
DSH Editor 已内置此插件、模型工具及 Tavily 适配器。
|
|
22
|
-
|
|
23
27
|
## 配置搜索
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
设置页分两个标签页:「搜索服务」管理供应商,「请求限制」设置请求限额。默认开启 DuckDuckGo,无需密钥。需要其它后端时在设置中开启并填写凭据;一个可用后端即可。拖拽后端调整顺序;按优先级使用第一个就绪后端,缺密钥的跳过,失败不自动换家。
|
|
30
|
+
|
|
31
|
+
内置搜索后端:
|
|
32
|
+
|
|
33
|
+
- **DuckDuckGo**:无需注册、无需密钥,默认后端,免费。
|
|
34
|
+
- **DeepSeek 搜索**:与模型设置共用 DeepSeek API Key(`DEEPSEEK_API_KEY`),可能在模型费用外产生额外搜索费用。
|
|
35
|
+
- **Exa**:独立 Search API,需要单独的 Key(`DSH_EDITOR_WEB_EXA_API_KEY`)。Exa 供应商已声明为本插件的运行时依赖。
|
|
36
|
+
- **Brave**:Brave Search API,需要 `DSH_EDITOR_WEB_BRAVE_API_KEY`。
|
|
37
|
+
- **博查**:中文网页搜索 API,需要 `DSH_EDITOR_WEB_BOCHA_API_KEY`。
|
|
38
|
+
- **Serper**:Google 结果 Search API,需要 `DSH_EDITOR_WEB_SERPER_API_KEY`。
|
|
39
|
+
- **Firecrawl**:网页搜索与提取 API,需要 `DSH_EDITOR_WEB_FIRECRAWL_API_KEY`。
|
|
40
|
+
- **Tavily**:Search API,固定 basic 检索深度,不自动升级;需要 `DSH_EDITOR_WEB_TAVILY_API_KEY`。适配器已并入本插件,原独立的 `dsh-web-search-tavily` 包已退役。
|
|
41
|
+
|
|
42
|
+
开启搜索也会启用公开网页读取。内置 `http` 路由优先匿名请求 `https://r.jina.ai/<目标 URL>`,返回 Reader 的文本/Markdown;HTTP 错误(含 429)、Reader 超时、网络失败、空内容或无效响应时,回退一次到原有 HTTP 直连读取。此回退仅用于网页读取,不改变搜索后端选择。无需注册 Jina、配置 API Key 或计费设置,也不增加依赖。
|
|
43
|
+
|
|
44
|
+
目标 URL 会发送给第三方服务 Jina,请勿提交含私密令牌或其它敏感信息的 URL。请求不附带 Cookie 或授权头。IP 字面量、明显的本地主机名、带用户名密码的 URL,以及已经带 Reader 前缀的地址跳过 Jina,继续受原 HTTP 读取器的策略约束。两阶段均复用原 HTTP 传输;Reader 最多使用 `timeoutMs` 的一半,且不超过 15 秒,为直连回退预留时间。用户取消、关闭联网或总超时到期时直接停止,不再发起回退。结果保留请求的目标 URL,不将 Reader 代理地址当作来源。
|
|
45
|
+
|
|
46
|
+
请求限制包括每条查询的结果上限(`maxResults`)、每次工具调用的查询上限(`maxQueries`)、整个请求的总超时(`timeoutMs`)和网页正文字符上限(`maxFetchChars`)。每次 HTTP 响应仍受 5 MB 字节上限约束。连接测试发送固定查询,不发送作品内容,可能产生一次调用费用。
|
|
26
47
|
|
|
27
|
-
|
|
48
|
+
加入 tools 入口后,模型获得 `@deepseek-ai/dsh-tool-web` 的官方 `web_search` 与 `web_fetch` 工具,仅在搜索与网页读取开启时挂载。
|
|
28
49
|
|
|
29
50
|
只配置可信 HTTPS 端点,密钥会发送到该地址。凭据由 DSH 保存,落盘保护取决于其后端配置。用量计数是调用尝试,不等于账单或消费上限。
|
|
30
51
|
|
package/icon.svg
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36" fill="none">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="globe" x1="6" y1="6" x2="26" y2="26" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#4D75F5"/>
|
|
5
|
+
<stop offset="1" stop-color="#8EB6FF"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<g transform="translate(3.6 3.6) scale(.8)">
|
|
9
|
+
<circle cx="15" cy="15" r="10" fill="url(#globe)"/>
|
|
10
|
+
<path d="M5.5 15H24.5M15 5.2C11.5 8 11.5 22 15 24.8C18.5 22 18.5 8 15 5.2Z" stroke="#B8D7FF" stroke-width="1.8"/>
|
|
11
|
+
<circle cx="23" cy="23" r="6.5" fill="#0E2A56" fill-opacity=".3"/>
|
|
12
|
+
<circle cx="23" cy="23" r="5.5" stroke="#65DED0" stroke-width="2.6"/>
|
|
13
|
+
<path d="M27.2 27.2L31 31" stroke="#55D9CE" stroke-width="3" stroke-linecap="round"/>
|
|
14
|
+
</g>
|
|
15
|
+
</svg>
|
package/lib/client.inner.cjs
CHANGED
|
@@ -509,11 +509,11 @@ function NetworkSearchSettings({ client }) {
|
|
|
509
509
|
}),
|
|
510
510
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
511
511
|
className: "web-search-provider-info",
|
|
512
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: info.description }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [info.pricing, info.pricingUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExternalLink, {
|
|
512
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: info.description }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: "费用说明" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [info.pricing, info.pricingUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExternalLink, {
|
|
513
513
|
url: info.pricingUrl,
|
|
514
514
|
label: provider.label + " 费用说明",
|
|
515
515
|
children: provider.id === "ddg" ? "官网" : "费用说明"
|
|
516
|
-
})] })] })]
|
|
516
|
+
})] })] })] })]
|
|
517
517
|
}),
|
|
518
518
|
participating && !provider.configured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
519
519
|
className: "web-search-meta",
|
|
@@ -685,11 +685,9 @@ function apply(ctx) {
|
|
|
685
685
|
document.head.appendChild(style);
|
|
686
686
|
return () => style.remove();
|
|
687
687
|
}, "web-search-manager.styles");
|
|
688
|
-
ctx.effect(() => client.slots.inject("
|
|
689
|
-
name: "
|
|
690
|
-
|
|
691
|
-
order: 70,
|
|
692
|
-
label: "网络搜索"
|
|
688
|
+
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
689
|
+
name: "plugins.bundle.config",
|
|
690
|
+
key: "@klarkxy/dsh-web-search-manager"
|
|
693
691
|
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSearchSettings, { client }))), "web-search-manager.settings");
|
|
694
692
|
}
|
|
695
693
|
//#endregion
|
package/lib/client.inner.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.inner.cjs","names":["useState","useRef","useId"],"sources":["../src/provider-info.ts","../src/contracts.ts","../src/client.tsx"],"sourcesContent":["import type { ProviderView } from './contracts.ts'\n\n// Public prices checked on 2026-09-21; links remain available for current account terms.\nexport const PROVIDER_PRICING_CHECKED = '2026-09-21'\n\ninterface ProviderInfo {\n description: string\n pricing: string\n pricingUrl?: string\n}\n\nconst providerInfo: Record<string, ProviderInfo> = {\n ddg: {\n description: '搜索 DuckDuckGo 公开网页,无需注册或 API Key。',\n pricing: '免费使用,不产生搜索 API 费用。',\n pricingUrl: 'https://duckduckgo.com/',\n },\n bocha: {\n description: '面向中文内容的网页搜索,返回标题、链接和摘要。',\n pricing: '按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。',\n pricingUrl: 'https://aq6ky2b8nql.feishu.cn/wiki/JYSbwzdPIiFnz4kDYPXcHSDrnZb',\n },\n brave: {\n description: '使用 Brave 独立网页索引,搜索网页、新闻等内容。',\n pricing: '每月赠 $5(约 1,000 次);超出 $5/千次,激活需绑卡。',\n pricingUrl: 'https://brave.com/search/api/',\n },\n 'deepseek-official': {\n description: '由 DeepSeek 模型调用内置搜索,复用模型 API Key。',\n pricing: '默认 Flash 按 tokens 计费:每百万输入 ¥1–2(缓存命中 ¥0.02–0.04)、输出 ¥4–8,随峰谷时段变化;赠送余额和搜索工具费用以平台为准。',\n pricingUrl: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/',\n },\n exa: {\n description: '面向 AI 的网页搜索,提供相关网页与内容片段。',\n pricing: '注册赠 $20,此后每月赠 $10;$7/千次(每次最多 10 条),超过 10 条每多一条加 $1/千次。',\n pricingUrl: 'https://exa.ai/pricing',\n },\n firecrawl: {\n description: '搜索网页,并可配合网页内容提取。',\n pricing: '每月免费 1,000 积分,搜索每 10 条结果扣 2 积分;Hobby 含 5,000 积分/月,年付折合 $16/月,额外 $5/千积分。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n serper: {\n description: '获取 Google 的网页搜索结果。',\n pricing: '注册赠 2,500 次(一次性);充值 $50/5万次($1/千次),付费额度有效期 6 个月。',\n pricingUrl: 'https://serper.dev/',\n },\n tavily: {\n description: '面向 AI 的网页搜索,当前使用基础搜索。',\n pricing: '每月免费 1,000 积分,基础搜索 1 积分/次;按量付费 $0.008/积分($8/千次)。',\n pricingUrl: 'https://docs.tavily.com/documentation/api-credits',\n },\n}\n\nexport function searchProviderInfo(provider: ProviderView): ProviderInfo {\n const known = Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : undefined\n return {\n description: known?.description ?? provider.description,\n pricing: provider.pricing ?? known?.pricing ?? (provider.billing === 'none' ? '此后端不收取搜索费用。'\n : provider.billing === 'model-and-tools' ? '按模型用量和工具调用计费;免费额度及单价请查看供应商说明。'\n : '按 API 调用计费;免费额度及单价请查看供应商说明。'),\n pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl,\n }\n}\n","import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Execution and source vocabulary belong to ctx.web. */\nexport interface ProviderDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface. The page must not delete it. */\n readonly credentialShared?: boolean\n /** Optional notice naming the settings surface that owns a shared credential. */\n readonly credentialHint?: string\n readonly billing: 'request' | 'model-and-tools' | 'none'\n /** Optional provider-supplied billing summary. */\n readonly pricing?: string\n /** Public HTTPS page explaining provider charges. */\n readonly pricingUrl?: string\n /** Public HTTPS page where the user can create an API key. */\n readonly signupUrl?: string\n}\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { useEffect, useId, useRef, useState } from 'react'\nimport { PROVIDER_PRICING_CHECKED, searchProviderInfo } from './provider-info.ts'\nimport {\n WEB_SEARCH_RPC_CHANNEL, pickActiveSearch, resolveSearchOrder,\n type ProviderView, type RpcResult, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport const name = 'dsh-web-search-manager-client'\nexport const inject = ['slots', 'connection', 'remote', 'remote.credentials'] as const\n\ninterface Credentials {\n describe(refs: string[]): Promise<RpcResult<Record<string, { configured: boolean; writable: boolean; source?: string }>>>\n set(ref: string, value: string): Promise<RpcResult<unknown>>\n unset(ref: string): Promise<RpcResult<unknown>>\n}\ninterface Client {\n connection: { rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> } }\n remote: { credentials: Credentials }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; id: string; label: string; order: number }, render: unknown): () => void\n }\n}\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\nfunction editable(settings: WebSettings): Omit<WebSettings, 'revision'> {\n const { revision: _revision, ...rest } = settings\n return rest\n}\n\nexport function isProviderOn(status: WebStatus, provider: ProviderView): boolean {\n const enabled = provider.kind === 'search' ? status.searchActive : status.fetchActive\n const selected = status.settings[provider.kind === 'search' ? 'searchProvider' : 'fetchProvider']\n return enabled && selected === provider.id\n}\n\nexport function searchBackends(status: WebStatus): ProviderView[] {\n const all = status.providers.filter(provider => provider.kind === 'search')\n const order = resolveSearchOrder(\n all.map(provider => provider.id),\n status.settings,\n id => Boolean(all.find(provider => provider.id === id)?.credentialRef),\n )\n return order.flatMap(id => all.find(provider => provider.id === id) ?? [])\n}\n\nexport function catalogSearchBackends(status: WebStatus): ProviderView[] {\n const enabled = searchBackends(status)\n const rest = status.providers.filter(provider => provider.kind === 'search' && !enabled.some(row => row.id === provider.id))\n return [...enabled, ...rest]\n}\n\nexport function selectedSearchBackend(status: WebStatus): ProviderView | undefined {\n const backends = searchBackends(status)\n const id = pickActiveSearch(backends.map(provider => provider.id), id => Boolean(backends.find(provider => provider.id === id)?.configured))\n return backends.find(provider => provider.id === id)\n}\n\nfunction ExternalLink({ url, label, children }: { url: string; label: string; children: string }) {\n return <a href={url} target=\"_blank\" rel=\"noreferrer noopener\" aria-label={label}\n onClick={event => {\n const bridge = (globalThis as { dshWindow?: { openExternal?(url: string): void } }).dshWindow\n if (bridge?.openExternal) {\n event.preventDefault()\n bridge.openExternal(url)\n }\n }}>{children}</a>\n}\n\nexport function canEnableSearch(\n order: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return order.some(id => {\n const provider = providers.find(item => item.kind === 'search' && item.id === id)\n if (!provider) return false\n if (provider.configured) return true\n const typed = (keys[provider.id] ?? '').trim()\n return Boolean(provider.credentialRef && typed && writable[provider.credentialRef] !== false)\n })\n}\n\nexport function nextSearchEnabled(\n currentlyEnabled: boolean,\n nextOrder: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable)\n}\n\nfunction ConfirmButton(props: { label: string; confirmLabel: string; disabled?: boolean; onConfirm(): void }) {\n const [armed, setArmed] = useState(false)\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n useEffect(() => () => clearTimeout(timer.current), [])\n function disarm() {\n clearTimeout(timer.current)\n setArmed(false)\n }\n return <button type=\"button\" className=\"web-search-danger\" disabled={props.disabled}\n onClick={() => {\n if (!armed) {\n setArmed(true)\n timer.current = setTimeout(() => setArmed(false), 3000)\n return\n }\n disarm()\n props.onConfirm()\n }}\n onBlur={disarm}>{armed ? props.confirmLabel : props.label}</button>\n}\n\nexport function NetworkSearchSettings({ client }: { client: Client }) {\n const tabsId = useId()\n const [tab, setTab] = useState<'providers' | 'limits'>('providers')\n const [status, setStatus] = useState<WebStatus>()\n const [draft, setDraft] = useState<WebSettings>()\n const [keys, setKeys] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [dragSource, setDragSource] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n const [sortAnnouncement, setSortAnnouncement] = useState('')\n const [note, setNote] = useState('')\n const [error, setError] = useState('')\n const [writable, setWritable] = useState<Record<string, boolean>>({})\n\n async function call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload) as RpcResult<T>)\n }\n async function load() {\n const next = await call<WebStatus>('status')\n setStatus(next); setDraft(next.settings)\n const refs = next.providers.flatMap(provider => provider.credentialRef ? [provider.credentialRef] : [])\n if (refs.length) {\n const credentials = unwrap(await client.remote.credentials.describe(refs))\n setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])))\n }\n return next\n }\n useEffect(() => { void load().catch(() => setError('无法读取网络搜索设置,请检查 Host 连接后重试。')) }, [client])\n\n async function action(run: () => Promise<void>) {\n setBusy(true); setNote(''); setError('')\n try { await run() }\n catch (cause) { setError(cause instanceof Error ? cause.message : '操作失败。'); await load().catch(() => {}) }\n finally { setBusy(false) }\n }\n async function update(settings: Omit<WebSettings, 'revision'>, expectedRevision: number) {\n const next = await call<WebStatus>('update', { settings, expectedRevision })\n setStatus(next); setDraft(next.settings)\n return next\n }\n\n async function persist(on: boolean, order: string[]) {\n if (!status || !draft) return\n for (const provider of status.providers) {\n if (provider.kind !== 'search') continue\n const snapshot = keys[provider.id] ?? ''\n const typed = snapshot.trim()\n if (!provider.credentialRef || !typed) continue\n if (writable[provider.credentialRef] === false) throw new Error('当前凭据为只读,无法写入。')\n unwrap(await client.remote.credentials.set(provider.credentialRef, typed))\n setKeys(current => current[provider.id] === snapshot ? { ...current, [provider.id]: '' } : current)\n }\n const bounded = (key: (typeof limits)[number][0]) => {\n const row = limits.find(item => item[0] === key)!\n return Math.min(row[3], Math.max(row[2], Number.isFinite(draft[key]) ? draft[key] : row[2]))\n }\n await update({\n ...editable(status.settings),\n maxResults: bounded('maxResults'), maxQueries: bounded('maxQueries'),\n timeoutMs: bounded('timeoutMs'), maxFetchChars: bounded('maxFetchChars'),\n endpoints: draft.endpoints, searchOrder: order,\n searchEnabled: on, fetchProvider: 'http', fetchEnabled: on,\n }, status.settings.revision)\n await load()\n }\n\n async function removeKey(provider: ProviderView) {\n if (!provider.credentialRef || provider.credentialShared || !status) return\n unwrap(await client.remote.credentials.unset(provider.credentialRef))\n setKeys(current => ({ ...current, [provider.id]: '' }))\n await load()\n setNote('已删除 Key。')\n }\n\n if (!status || !draft) {\n return <section className=\"web-search-settings\">\n <p role=\"status\">{error || '正在读取设置…'}</p>\n {error ? <button type=\"button\" onClick={() => void action(async () => { await load() })}>重新连接</button> : null}\n </section>\n }\n\n const enabled = searchBackends(status)\n const backends = catalogSearchBackends(status)\n const selected = selectedSearchBackend(status)\n const on = status.settings.searchEnabled\n const order = enabled.map(provider => provider.id)\n const canEnable = canEnableSearch(order, status.providers, keys, writable)\n const limits = [\n ['maxResults', '每条查询的结果上限', 1, 20],\n ['maxQueries', '每次工具调用的查询上限', 1, 5],\n ['timeoutMs', '请求超时(毫秒)', 1000, 120000],\n ['maxFetchChars', '网页正文字符上限', 1000, 200000],\n ] as const\n\n function move(id: string, nextIndex: number) {\n const index = order.indexOf(id)\n if (busy || index < 0 || nextIndex < 0 || nextIndex >= order.length || index === nextIndex) return\n const next = [...order]\n const [item] = next.splice(index, 1)\n next.splice(nextIndex, 0, item)\n void action(async () => {\n await persist(on, next)\n setSortAnnouncement((backends.find(provider => provider.id === id)?.label ?? id) + ' 已移至第 ' + (nextIndex + 1) + ' 位')\n })\n }\n\n function endDrag() {\n setDragSource(null)\n setDropTarget(null)\n }\n\n return <section className=\"web-search-settings\" data-testid=\"web-search-settings\">\n <div className=\"web-search-tabs\" role=\"tablist\" aria-label=\"网络搜索设置\" onKeyDown={event => {\n if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return\n const buttons = [...event.currentTarget.querySelectorAll<HTMLButtonElement>('[role=\"tab\"]')]\n const index = buttons.indexOf(event.target as HTMLButtonElement)\n if (index < 0) return\n event.preventDefault()\n const next = event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1\n : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length\n buttons[next]?.focus()\n buttons[next]?.click()\n }}>\n {([['providers', '搜索服务'], ['limits', '请求限制']] as const).map(([key, label]) => (\n <button key={key} type=\"button\" role=\"tab\" id={tabsId + '-' + key + '-tab'}\n aria-controls={tabsId + '-' + key + '-panel'} aria-selected={tab === key}\n tabIndex={tab === key ? 0 : -1} onClick={() => setTab(key)}>{label}</button>\n ))}\n </div>\n <span className=\"web-search-sr-only\" role=\"status\" aria-live=\"polite\">{sortAnnouncement}</span>\n {error && <p role=\"alert\" className=\"web-search-error\">{error}</p>}\n {note && <p role=\"status\">{note}</p>}\n {status.storageFailed && <p role=\"alert\">配置保存异常,当前运行已暂停网络访问。请重新保存。</p>}\n <div role=\"tabpanel\" id={tabsId + '-providers-panel'} aria-labelledby={tabsId + '-providers-tab'}\n hidden={tab !== 'providers'} tabIndex={0}>\n {backends.length === 0 ? <p className=\"web-search-empty\">还没有可用的搜索后端。</p> : <article\n className=\"web-search-card\"\n data-testid=\"web-search-tool\"\n data-on={on ? 'true' : 'false'}>\n <header>\n <div>\n <h3>联网搜索</h3>\n {!canEnable && <p className=\"web-search-meta\">请选择搜索服务并配置 Key。</p>}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${on ? ' is-on' : ''}`}\n aria-checked={on}\n aria-label={on ? '关闭联网搜索' : '启用联网搜索'}\n disabled={busy || (!on && !canEnable)}\n onClick={() => void action(() => persist(!on, order))}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </header>\n <ol className=\"web-search-rank\" aria-label=\"搜索后端\">\n {backends.map(provider => {\n const participating = order.includes(provider.id)\n const rank = order.indexOf(provider.id)\n const active = status.searchActive && selected?.id === provider.id\n const info = searchProviderInfo(provider)\n const needsKey = Boolean(provider.credentialRef)\n const shared = Boolean(provider.credentialShared)\n const showKey = needsKey && participating && (!shared || !provider.configured)\n const canSort = participating && order.length > 1\n return <li key={provider.id} className={active ? 'is-active' : undefined} data-testid={`web-search-rank-${provider.id}`}\n data-dragging={dragSource === provider.id || undefined}\n data-drop={dropTarget === provider.id && dragSource ? (order.indexOf(dragSource) < rank ? 'after' : 'before') : undefined}\n onDragOver={event => {\n if (busy || !canSort || !dragSource || dragSource === provider.id) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(provider.id)\n }}\n onDragLeave={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropTarget(null)\n }}\n onDrop={event => {\n if (busy || !canSort || !dragSource) return\n event.preventDefault()\n move(dragSource, rank)\n endDrag()\n }}>\n {canSort ? <button type=\"button\" className=\"web-search-drag-handle\"\n draggable={!busy} aria-disabled={busy}\n aria-label={'拖动排序 ' + provider.label}\n title=\"拖动排序,或用上下方向键调整\"\n onDragStart={event => {\n if (busy) { event.preventDefault(); return }\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('application/x-dsh-search-provider', provider.id)\n const row = event.currentTarget.closest('li')\n if (row) event.dataTransfer.setDragImage(row, 16, 16)\n setDragSource(provider.id)\n setSortAnnouncement('')\n }}\n onDragEnd={endDrag}\n onKeyDown={event => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(provider.id, rank + (event.key === 'ArrowUp' ? -1 : 1))\n }}>\n <svg width=\"16\" height=\"20\" viewBox=\"0 0 16 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"5\" cy=\"5\" r=\"1.5\" /><circle cx=\"11\" cy=\"5\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"10\" r=\"1.5\" /><circle cx=\"11\" cy=\"10\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"15\" r=\"1.5\" /><circle cx=\"11\" cy=\"15\" r=\"1.5\" />\n </svg>\n </button> : <span className=\"web-search-rank-index\">{participating ? rank + 1 : '—'}</span>}\n <div className=\"web-search-provider\">\n <div className=\"web-search-provider-heading\">\n <strong>{provider.label}</strong>\n {active && <span className=\"web-search-meta\">当前使用</span>}\n {provider.signupUrl && <ExternalLink url={provider.signupUrl} label={provider.label + ' 去注册'}>去注册</ExternalLink>}\n </div>\n <div className=\"web-search-provider-info\">\n <p>{info.description}</p>\n <p>{info.pricing}{info.pricingUrl && <> <ExternalLink url={info.pricingUrl}\n label={provider.label + ' 费用说明'}>{provider.id === 'ddg' ? '官网' : '费用说明'}</ExternalLink></>}</p>\n </div>\n {participating && !provider.configured && <p className=\"web-search-meta\">{needsKey ? '待配置 Key' : '暂不可用'}</p>}\n {shared && participating && <small>{provider.credentialHint ?? '与其他设置共用凭据'}</small>}\n {showKey ? <label>API Key\n <input type=\"password\" autoComplete=\"off\" spellCheck={false}\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n value={keys[provider.id] ?? ''}\n placeholder={provider.configured ? '已配置,留空不替换' : '请输入 API Key'}\n onChange={event => setKeys(current => ({ ...current, [provider.id]: event.target.value }))} />\n </label> : null}\n {needsKey && !shared && provider.configured ? <ConfirmButton\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n label=\"删除 Key\" confirmLabel=\"确认删除 Key?\"\n onConfirm={() => void action(() => removeKey(provider))} /> : null}\n </div>\n <div className=\"web-search-rank-move\">\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${participating ? ' is-on' : ''}`}\n aria-checked={participating}\n aria-label={`${participating ? '关闭' : '开启'} ${provider.label}`}\n disabled={busy}\n onClick={() => {\n const next = participating ? order.filter(id => id !== provider.id) : [...order, provider.id]\n void action(() => persist(nextSearchEnabled(status.settings.searchEnabled, next, status.providers, keys, writable), next))\n }}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </div>\n </li>\n })}\n </ol>\n <p className=\"web-search-meta\">价格核对于 {PROVIDER_PRICING_CHECKED},实际额度与费用以供应商账户为准。</p>\n {on ? <div className=\"web-search-card-actions\">\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n const result = await call<{ sources: number }>('test')\n await load()\n setNote(`连接正常,返回 ${result.sources} 条来源。`)\n })}>测试连接(可能计费)</button>\n </div> : null}\n </article>}\n </div>\n <div role=\"tabpanel\" id={tabsId + '-limits-panel'} aria-labelledby={tabsId + '-limits-tab'}\n hidden={tab !== 'limits'} tabIndex={0}>\n <fieldset className=\"web-search-limits\" disabled={busy} aria-label=\"请求限制\">\n {limits.map(([key, label, min, max]) => (\n <label key={key}>{label}\n <input type=\"number\" min={min} max={max} step={1} value={draft[key]}\n onChange={event => setDraft({ ...draft, [key]: Number(event.target.value) })} />\n </label>\n ))}\n </fieldset>\n </div>\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n setNote('已保存。')\n })}>保存</button>\n </section>\n}\n\nconst styles = `\n.web-search-settings{max-width:760px;display:grid;gap:24px;color:inherit;font:400 var(--font-size-2,14px)/1.5 var(--default-font-family,system-ui,sans-serif)}\n.web-search-settings p{margin:0;line-height:1.5}\n.web-search-settings small,.web-search-meta,.web-search-empty{font-size:var(--font-size-1,13px);color:var(--gray-11,inherit)}\n.web-search-error{color:var(--red-11,#b42318)}\n.web-search-card{display:grid;gap:12px;padding:16px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent));border-radius:12px}\n.web-search-card header{display:flex;justify-content:space-between;gap:12px;align-items:center;padding-bottom:8px}\n.web-search-card h3{margin:0;font-size:var(--font-size-3,16px);font-weight:600}\n.web-search-provider{display:grid;gap:8px;min-width:0}\n.web-search-provider-info{display:grid;gap:3px;font-size:var(--font-size-1,13px);color:var(--gray-11,inherit);overflow-wrap:anywhere}\n.web-search-provider-heading{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;min-height:24px}\n.web-search-provider-heading strong{font-weight:500}\n.web-search-card label{display:grid;gap:6px}\n.web-search-settings input{box-sizing:border-box;width:100%;min-width:0;padding:8px 10px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 22%,transparent));border-radius:8px;background:var(--color-surface,transparent);color:inherit;font:inherit;transition:border-color 150ms ease,box-shadow 150ms ease}\n.web-search-settings input:focus{border-color:var(--accent-9,#3b82f6);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent-9,#3b82f6) 25%,transparent)}\n.web-search-settings button:not([role=\"switch\"]){min-height:34px;padding:6px 12px;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;background:transparent;color:inherit;cursor:pointer;font:inherit;justify-self:start;transition:background-color 150ms ease,color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,transform 150ms ease}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):hover:not(:disabled){background:var(--gray-3,color-mix(in srgb,currentColor 6%,transparent));border-color:color-mix(in srgb,currentColor 35%,transparent)}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):active:not(:disabled){transform:scale(.97)}\n.web-search-danger:hover:not(:disabled){border-color:var(--red-11,#b42318);color:var(--red-11,#b42318);background:color-mix(in srgb,var(--red-11,#b42318) 8%,transparent)}\n.web-search-settings button:disabled{opacity:.45;cursor:not-allowed}\n.web-search-tabs{display:flex;gap:20px;border-bottom:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-tabs button[role=\"tab\"]{padding:8px 0;border:0;border-bottom:2px solid transparent;border-radius:0;color:var(--gray-11,inherit)}\n.web-search-tabs button[role=\"tab\"]:hover{color:inherit}\n.web-search-tabs button[aria-selected=\"true\"]{border-bottom-color:var(--accent-9,#3b82f6);color:var(--accent-11,inherit);font-weight:600}\n.web-search-settings [role=\"tabpanel\"]{border-radius:12px}\n.web-search-settings .web-search-switch{all:unset;box-sizing:border-box;position:relative;display:inline-block;width:36px;height:20px;flex:none;border-radius:999px;background:var(--gray-7,color-mix(in srgb,currentColor 28%,transparent));cursor:pointer}\n.web-search-switch.is-on{background:var(--accent-9,#3b82f6)}\n.web-search-switch:disabled{cursor:not-allowed;opacity:.7}\n.web-search-switch-thumb{position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:999px;background:#fff;transition:transform 150ms ease}\n.web-search-switch.is-on .web-search-switch-thumb{transform:translateX(16px)}\n.web-search-settings :focus-visible{outline:2px solid var(--accent-9,currentColor);outline-offset:3px}\n.web-search-card-actions{display:flex;flex-wrap:wrap;gap:8px}\n.web-search-rank{margin:0;padding:0;list-style:none}\n.web-search-rank li{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto;gap:10px;align-items:start;padding:14px 0;border-top:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-settings button.web-search-drag-handle{display:grid;place-items:center;width:24px;height:24px;min-height:24px;padding:0;border:0;cursor:grab;color:inherit;opacity:.65}\n.web-search-settings button.web-search-drag-handle:hover{opacity:1;background:var(--gray-3,color-mix(in srgb,currentColor 8%,transparent))}\n.web-search-settings button.web-search-drag-handle:active{cursor:grabbing}\n.web-search-settings button.web-search-drag-handle[aria-disabled=\"true\"]{cursor:wait;opacity:.4}\n.web-search-rank li[data-dragging]{opacity:.45}\n.web-search-rank li[data-drop]::after{content:'';position:absolute;left:0;right:0;height:2px;background:var(--accent-9,#3b82f6);pointer-events:none}\n.web-search-rank li[data-drop=\"before\"]::after{top:-1px}\n.web-search-rank li[data-drop=\"after\"]::after{bottom:-1px}\n.web-search-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n.web-search-rank-index{opacity:.6;font-variant-numeric:tabular-nums;line-height:24px}\n.web-search-rank-move{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-end;padding-top:2px}\n.web-search-settings a{color:var(--accent-11,currentColor);font-size:var(--font-size-1,13px);text-underline-offset:3px}\n.web-search-limits{margin:0;border:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 20px}\n.web-search-limits label{display:grid;gap:6px;font-size:var(--font-size-2,14px)}\n@media(max-width:560px){.web-search-rank-move{flex-direction:column;align-items:flex-end}.web-search-limits{grid-template-columns:minmax(0,1fr)}}\n@media(prefers-reduced-motion:reduce){.web-search-switch-thumb{transition:none}.web-search-settings button:not([role=\"switch\"]),.web-search-settings input{transition:none}}\n`\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-web-search-manager'); style.textContent = styles\n style.dataset.dshWebSearch = ''; document.head.appendChild(style)\n return () => style.remove()\n }, 'web-search-manager.styles')\n ctx.effect(() => client.slots.inject('settings.section', () => client.slots.register({\n name: 'settings.section', id: 'web-search', order: 70, label: '网络搜索',\n }, () => <NetworkSearchSettings client={client} />)), 'web-search-manager.settings')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.inner.cjs","names":["useState","useRef","useId"],"sources":["../src/provider-info.ts","../src/contracts.ts","../src/client.tsx"],"sourcesContent":["import type { ProviderView } from './contracts.ts'\n\n// Public prices checked on 2026-09-21; links remain available for current account terms.\nexport const PROVIDER_PRICING_CHECKED = '2026-09-21'\n\ninterface ProviderInfo {\n description: string\n pricing: string\n pricingUrl?: string\n}\n\nconst providerInfo: Record<string, ProviderInfo> = {\n ddg: {\n description: '搜索 DuckDuckGo 公开网页,无需注册或 API Key。',\n pricing: '免费使用,不产生搜索 API 费用。',\n pricingUrl: 'https://duckduckgo.com/',\n },\n bocha: {\n description: '面向中文内容的网页搜索,返回标题、链接和摘要。',\n pricing: '按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。',\n pricingUrl: 'https://aq6ky2b8nql.feishu.cn/wiki/JYSbwzdPIiFnz4kDYPXcHSDrnZb',\n },\n brave: {\n description: '使用 Brave 独立网页索引,搜索网页、新闻等内容。',\n pricing: '每月赠 $5(约 1,000 次);超出 $5/千次,激活需绑卡。',\n pricingUrl: 'https://brave.com/search/api/',\n },\n 'deepseek-official': {\n description: '由 DeepSeek 模型调用内置搜索,复用模型 API Key。',\n pricing: '默认 Flash 按 tokens 计费:每百万输入 ¥1–2(缓存命中 ¥0.02–0.04)、输出 ¥4–8,随峰谷时段变化;赠送余额和搜索工具费用以平台为准。',\n pricingUrl: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/',\n },\n exa: {\n description: '面向 AI 的网页搜索,提供相关网页与内容片段。',\n pricing: '注册赠 $20,此后每月赠 $10;$7/千次(每次最多 10 条),超过 10 条每多一条加 $1/千次。',\n pricingUrl: 'https://exa.ai/pricing',\n },\n firecrawl: {\n description: '搜索网页,并可配合网页内容提取。',\n pricing: '每月免费 1,000 积分,搜索每 10 条结果扣 2 积分;Hobby 含 5,000 积分/月,年付折合 $16/月,额外 $5/千积分。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n serper: {\n description: '获取 Google 的网页搜索结果。',\n pricing: '注册赠 2,500 次(一次性);充值 $50/5万次($1/千次),付费额度有效期 6 个月。',\n pricingUrl: 'https://serper.dev/',\n },\n tavily: {\n description: '面向 AI 的网页搜索,当前使用基础搜索。',\n pricing: '每月免费 1,000 积分,基础搜索 1 积分/次;按量付费 $0.008/积分($8/千次)。',\n pricingUrl: 'https://docs.tavily.com/documentation/api-credits',\n },\n}\n\nexport function searchProviderInfo(provider: ProviderView): ProviderInfo {\n const known = Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : undefined\n return {\n description: known?.description ?? provider.description,\n pricing: provider.pricing ?? known?.pricing ?? (provider.billing === 'none' ? '此后端不收取搜索费用。'\n : provider.billing === 'model-and-tools' ? '按模型用量和工具调用计费;免费额度及单价请查看供应商说明。'\n : '按 API 调用计费;免费额度及单价请查看供应商说明。'),\n pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl,\n }\n}\n","import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Execution and source vocabulary belong to ctx.web. */\nexport interface ProviderDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface. The page must not delete it. */\n readonly credentialShared?: boolean\n /** Optional notice naming the settings surface that owns a shared credential. */\n readonly credentialHint?: string\n readonly billing: 'request' | 'model-and-tools' | 'none'\n /** Optional provider-supplied billing summary. */\n readonly pricing?: string\n /** Public HTTPS page explaining provider charges. */\n readonly pricingUrl?: string\n /** Public HTTPS page where the user can create an API key. */\n readonly signupUrl?: string\n}\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { useEffect, useId, useRef, useState } from 'react'\nimport { PROVIDER_PRICING_CHECKED, searchProviderInfo } from './provider-info.ts'\nimport {\n WEB_SEARCH_RPC_CHANNEL, pickActiveSearch, resolveSearchOrder,\n type ProviderView, type RpcResult, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport const name = 'dsh-web-search-manager-client'\nexport const inject = ['slots', 'connection', 'remote', 'remote.credentials'] as const\n\ninterface Credentials {\n describe(refs: string[]): Promise<RpcResult<Record<string, { configured: boolean; writable: boolean; source?: string }>>>\n set(ref: string, value: string): Promise<RpcResult<unknown>>\n unset(ref: string): Promise<RpcResult<unknown>>\n}\ninterface Client {\n connection: { rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> } }\n remote: { credentials: Credentials }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; key: string }, render: unknown): () => void\n }\n}\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\nfunction editable(settings: WebSettings): Omit<WebSettings, 'revision'> {\n const { revision: _revision, ...rest } = settings\n return rest\n}\n\nexport function isProviderOn(status: WebStatus, provider: ProviderView): boolean {\n const enabled = provider.kind === 'search' ? status.searchActive : status.fetchActive\n const selected = status.settings[provider.kind === 'search' ? 'searchProvider' : 'fetchProvider']\n return enabled && selected === provider.id\n}\n\nexport function searchBackends(status: WebStatus): ProviderView[] {\n const all = status.providers.filter(provider => provider.kind === 'search')\n const order = resolveSearchOrder(\n all.map(provider => provider.id),\n status.settings,\n id => Boolean(all.find(provider => provider.id === id)?.credentialRef),\n )\n return order.flatMap(id => all.find(provider => provider.id === id) ?? [])\n}\n\nexport function catalogSearchBackends(status: WebStatus): ProviderView[] {\n const enabled = searchBackends(status)\n const rest = status.providers.filter(provider => provider.kind === 'search' && !enabled.some(row => row.id === provider.id))\n return [...enabled, ...rest]\n}\n\nexport function selectedSearchBackend(status: WebStatus): ProviderView | undefined {\n const backends = searchBackends(status)\n const id = pickActiveSearch(backends.map(provider => provider.id), id => Boolean(backends.find(provider => provider.id === id)?.configured))\n return backends.find(provider => provider.id === id)\n}\n\nfunction ExternalLink({ url, label, children }: { url: string; label: string; children: string }) {\n return <a href={url} target=\"_blank\" rel=\"noreferrer noopener\" aria-label={label}\n onClick={event => {\n const bridge = (globalThis as { dshWindow?: { openExternal?(url: string): void } }).dshWindow\n if (bridge?.openExternal) {\n event.preventDefault()\n bridge.openExternal(url)\n }\n }}>{children}</a>\n}\n\nexport function canEnableSearch(\n order: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return order.some(id => {\n const provider = providers.find(item => item.kind === 'search' && item.id === id)\n if (!provider) return false\n if (provider.configured) return true\n const typed = (keys[provider.id] ?? '').trim()\n return Boolean(provider.credentialRef && typed && writable[provider.credentialRef] !== false)\n })\n}\n\nexport function nextSearchEnabled(\n currentlyEnabled: boolean,\n nextOrder: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable)\n}\n\nfunction ConfirmButton(props: { label: string; confirmLabel: string; disabled?: boolean; onConfirm(): void }) {\n const [armed, setArmed] = useState(false)\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n useEffect(() => () => clearTimeout(timer.current), [])\n function disarm() {\n clearTimeout(timer.current)\n setArmed(false)\n }\n return <button type=\"button\" className=\"web-search-danger\" disabled={props.disabled}\n onClick={() => {\n if (!armed) {\n setArmed(true)\n timer.current = setTimeout(() => setArmed(false), 3000)\n return\n }\n disarm()\n props.onConfirm()\n }}\n onBlur={disarm}>{armed ? props.confirmLabel : props.label}</button>\n}\n\nexport function NetworkSearchSettings({ client }: { client: Client }) {\n const tabsId = useId()\n const [tab, setTab] = useState<'providers' | 'limits'>('providers')\n const [status, setStatus] = useState<WebStatus>()\n const [draft, setDraft] = useState<WebSettings>()\n const [keys, setKeys] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [dragSource, setDragSource] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n const [sortAnnouncement, setSortAnnouncement] = useState('')\n const [note, setNote] = useState('')\n const [error, setError] = useState('')\n const [writable, setWritable] = useState<Record<string, boolean>>({})\n\n async function call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload) as RpcResult<T>)\n }\n async function load() {\n const next = await call<WebStatus>('status')\n setStatus(next); setDraft(next.settings)\n const refs = next.providers.flatMap(provider => provider.credentialRef ? [provider.credentialRef] : [])\n if (refs.length) {\n const credentials = unwrap(await client.remote.credentials.describe(refs))\n setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])))\n }\n return next\n }\n useEffect(() => { void load().catch(() => setError('无法读取网络搜索设置,请检查 Host 连接后重试。')) }, [client])\n\n async function action(run: () => Promise<void>) {\n setBusy(true); setNote(''); setError('')\n try { await run() }\n catch (cause) { setError(cause instanceof Error ? cause.message : '操作失败。'); await load().catch(() => {}) }\n finally { setBusy(false) }\n }\n async function update(settings: Omit<WebSettings, 'revision'>, expectedRevision: number) {\n const next = await call<WebStatus>('update', { settings, expectedRevision })\n setStatus(next); setDraft(next.settings)\n return next\n }\n\n async function persist(on: boolean, order: string[]) {\n if (!status || !draft) return\n for (const provider of status.providers) {\n if (provider.kind !== 'search') continue\n const snapshot = keys[provider.id] ?? ''\n const typed = snapshot.trim()\n if (!provider.credentialRef || !typed) continue\n if (writable[provider.credentialRef] === false) throw new Error('当前凭据为只读,无法写入。')\n unwrap(await client.remote.credentials.set(provider.credentialRef, typed))\n setKeys(current => current[provider.id] === snapshot ? { ...current, [provider.id]: '' } : current)\n }\n const bounded = (key: (typeof limits)[number][0]) => {\n const row = limits.find(item => item[0] === key)!\n return Math.min(row[3], Math.max(row[2], Number.isFinite(draft[key]) ? draft[key] : row[2]))\n }\n await update({\n ...editable(status.settings),\n maxResults: bounded('maxResults'), maxQueries: bounded('maxQueries'),\n timeoutMs: bounded('timeoutMs'), maxFetchChars: bounded('maxFetchChars'),\n endpoints: draft.endpoints, searchOrder: order,\n searchEnabled: on, fetchProvider: 'http', fetchEnabled: on,\n }, status.settings.revision)\n await load()\n }\n\n async function removeKey(provider: ProviderView) {\n if (!provider.credentialRef || provider.credentialShared || !status) return\n unwrap(await client.remote.credentials.unset(provider.credentialRef))\n setKeys(current => ({ ...current, [provider.id]: '' }))\n await load()\n setNote('已删除 Key。')\n }\n\n if (!status || !draft) {\n return <section className=\"web-search-settings\">\n <p role=\"status\">{error || '正在读取设置…'}</p>\n {error ? <button type=\"button\" onClick={() => void action(async () => { await load() })}>重新连接</button> : null}\n </section>\n }\n\n const enabled = searchBackends(status)\n const backends = catalogSearchBackends(status)\n const selected = selectedSearchBackend(status)\n const on = status.settings.searchEnabled\n const order = enabled.map(provider => provider.id)\n const canEnable = canEnableSearch(order, status.providers, keys, writable)\n const limits = [\n ['maxResults', '每条查询的结果上限', 1, 20],\n ['maxQueries', '每次工具调用的查询上限', 1, 5],\n ['timeoutMs', '请求超时(毫秒)', 1000, 120000],\n ['maxFetchChars', '网页正文字符上限', 1000, 200000],\n ] as const\n\n function move(id: string, nextIndex: number) {\n const index = order.indexOf(id)\n if (busy || index < 0 || nextIndex < 0 || nextIndex >= order.length || index === nextIndex) return\n const next = [...order]\n const [item] = next.splice(index, 1)\n next.splice(nextIndex, 0, item)\n void action(async () => {\n await persist(on, next)\n setSortAnnouncement((backends.find(provider => provider.id === id)?.label ?? id) + ' 已移至第 ' + (nextIndex + 1) + ' 位')\n })\n }\n\n function endDrag() {\n setDragSource(null)\n setDropTarget(null)\n }\n\n return <section className=\"web-search-settings\" data-testid=\"web-search-settings\">\n <div className=\"web-search-tabs\" role=\"tablist\" aria-label=\"网络搜索设置\" onKeyDown={event => {\n if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return\n const buttons = [...event.currentTarget.querySelectorAll<HTMLButtonElement>('[role=\"tab\"]')]\n const index = buttons.indexOf(event.target as HTMLButtonElement)\n if (index < 0) return\n event.preventDefault()\n const next = event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1\n : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length\n buttons[next]?.focus()\n buttons[next]?.click()\n }}>\n {([['providers', '搜索服务'], ['limits', '请求限制']] as const).map(([key, label]) => (\n <button key={key} type=\"button\" role=\"tab\" id={tabsId + '-' + key + '-tab'}\n aria-controls={tabsId + '-' + key + '-panel'} aria-selected={tab === key}\n tabIndex={tab === key ? 0 : -1} onClick={() => setTab(key)}>{label}</button>\n ))}\n </div>\n <span className=\"web-search-sr-only\" role=\"status\" aria-live=\"polite\">{sortAnnouncement}</span>\n {error && <p role=\"alert\" className=\"web-search-error\">{error}</p>}\n {note && <p role=\"status\">{note}</p>}\n {status.storageFailed && <p role=\"alert\">配置保存异常,当前运行已暂停网络访问。请重新保存。</p>}\n <div role=\"tabpanel\" id={tabsId + '-providers-panel'} aria-labelledby={tabsId + '-providers-tab'}\n hidden={tab !== 'providers'} tabIndex={0}>\n {backends.length === 0 ? <p className=\"web-search-empty\">还没有可用的搜索后端。</p> : <article\n className=\"web-search-card\"\n data-testid=\"web-search-tool\"\n data-on={on ? 'true' : 'false'}>\n <header>\n <div>\n <h3>联网搜索</h3>\n {!canEnable && <p className=\"web-search-meta\">请选择搜索服务并配置 Key。</p>}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${on ? ' is-on' : ''}`}\n aria-checked={on}\n aria-label={on ? '关闭联网搜索' : '启用联网搜索'}\n disabled={busy || (!on && !canEnable)}\n onClick={() => void action(() => persist(!on, order))}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </header>\n <ol className=\"web-search-rank\" aria-label=\"搜索后端\">\n {backends.map(provider => {\n const participating = order.includes(provider.id)\n const rank = order.indexOf(provider.id)\n const active = status.searchActive && selected?.id === provider.id\n const info = searchProviderInfo(provider)\n const needsKey = Boolean(provider.credentialRef)\n const shared = Boolean(provider.credentialShared)\n const showKey = needsKey && participating && (!shared || !provider.configured)\n const canSort = participating && order.length > 1\n return <li key={provider.id} className={active ? 'is-active' : undefined} data-testid={`web-search-rank-${provider.id}`}\n data-dragging={dragSource === provider.id || undefined}\n data-drop={dropTarget === provider.id && dragSource ? (order.indexOf(dragSource) < rank ? 'after' : 'before') : undefined}\n onDragOver={event => {\n if (busy || !canSort || !dragSource || dragSource === provider.id) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(provider.id)\n }}\n onDragLeave={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropTarget(null)\n }}\n onDrop={event => {\n if (busy || !canSort || !dragSource) return\n event.preventDefault()\n move(dragSource, rank)\n endDrag()\n }}>\n {canSort ? <button type=\"button\" className=\"web-search-drag-handle\"\n draggable={!busy} aria-disabled={busy}\n aria-label={'拖动排序 ' + provider.label}\n title=\"拖动排序,或用上下方向键调整\"\n onDragStart={event => {\n if (busy) { event.preventDefault(); return }\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('application/x-dsh-search-provider', provider.id)\n const row = event.currentTarget.closest('li')\n if (row) event.dataTransfer.setDragImage(row, 16, 16)\n setDragSource(provider.id)\n setSortAnnouncement('')\n }}\n onDragEnd={endDrag}\n onKeyDown={event => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(provider.id, rank + (event.key === 'ArrowUp' ? -1 : 1))\n }}>\n <svg width=\"16\" height=\"20\" viewBox=\"0 0 16 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"5\" cy=\"5\" r=\"1.5\" /><circle cx=\"11\" cy=\"5\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"10\" r=\"1.5\" /><circle cx=\"11\" cy=\"10\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"15\" r=\"1.5\" /><circle cx=\"11\" cy=\"15\" r=\"1.5\" />\n </svg>\n </button> : <span className=\"web-search-rank-index\">{participating ? rank + 1 : '—'}</span>}\n <div className=\"web-search-provider\">\n <div className=\"web-search-provider-heading\">\n <strong>{provider.label}</strong>\n {active && <span className=\"web-search-meta\">当前使用</span>}\n {provider.signupUrl && <ExternalLink url={provider.signupUrl} label={provider.label + ' 去注册'}>去注册</ExternalLink>}\n </div>\n <div className=\"web-search-provider-info\">\n <p>{info.description}</p>\n <details>\n <summary>费用说明</summary>\n <p>{info.pricing}{info.pricingUrl && <> <ExternalLink url={info.pricingUrl}\n label={provider.label + ' 费用说明'}>{provider.id === 'ddg' ? '官网' : '费用说明'}</ExternalLink></>}</p>\n </details>\n </div>\n {participating && !provider.configured && <p className=\"web-search-meta\">{needsKey ? '待配置 Key' : '暂不可用'}</p>}\n {shared && participating && <small>{provider.credentialHint ?? '与其他设置共用凭据'}</small>}\n {showKey ? <label>API Key\n <input type=\"password\" autoComplete=\"off\" spellCheck={false}\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n value={keys[provider.id] ?? ''}\n placeholder={provider.configured ? '已配置,留空不替换' : '请输入 API Key'}\n onChange={event => setKeys(current => ({ ...current, [provider.id]: event.target.value }))} />\n </label> : null}\n {needsKey && !shared && provider.configured ? <ConfirmButton\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n label=\"删除 Key\" confirmLabel=\"确认删除 Key?\"\n onConfirm={() => void action(() => removeKey(provider))} /> : null}\n </div>\n <div className=\"web-search-rank-move\">\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${participating ? ' is-on' : ''}`}\n aria-checked={participating}\n aria-label={`${participating ? '关闭' : '开启'} ${provider.label}`}\n disabled={busy}\n onClick={() => {\n const next = participating ? order.filter(id => id !== provider.id) : [...order, provider.id]\n void action(() => persist(nextSearchEnabled(status.settings.searchEnabled, next, status.providers, keys, writable), next))\n }}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </div>\n </li>\n })}\n </ol>\n <p className=\"web-search-meta\">价格核对于 {PROVIDER_PRICING_CHECKED},实际额度与费用以供应商账户为准。</p>\n {on ? <div className=\"web-search-card-actions\">\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n const result = await call<{ sources: number }>('test')\n await load()\n setNote(`连接正常,返回 ${result.sources} 条来源。`)\n })}>测试连接(可能计费)</button>\n </div> : null}\n </article>}\n </div>\n <div role=\"tabpanel\" id={tabsId + '-limits-panel'} aria-labelledby={tabsId + '-limits-tab'}\n hidden={tab !== 'limits'} tabIndex={0}>\n <fieldset className=\"web-search-limits\" disabled={busy} aria-label=\"请求限制\">\n {limits.map(([key, label, min, max]) => (\n <label key={key}>{label}\n <input type=\"number\" min={min} max={max} step={1} value={draft[key]}\n onChange={event => setDraft({ ...draft, [key]: Number(event.target.value) })} />\n </label>\n ))}\n </fieldset>\n </div>\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n setNote('已保存。')\n })}>保存</button>\n </section>\n}\n\nconst styles = `\n.web-search-settings{max-width:760px;display:grid;gap:24px;color:inherit;font:400 var(--font-size-2,14px)/1.5 var(--default-font-family,system-ui,sans-serif)}\n.web-search-settings p{margin:0;line-height:1.5}\n.web-search-settings small,.web-search-meta,.web-search-empty{font-size:var(--font-size-1,13px);color:var(--gray-11,inherit)}\n.web-search-error{color:var(--red-11,#b42318)}\n.web-search-card{display:grid;gap:12px;padding:16px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent));border-radius:12px}\n.web-search-card header{display:flex;justify-content:space-between;gap:12px;align-items:center;padding-bottom:8px}\n.web-search-card h3{margin:0;font-size:var(--font-size-3,16px);font-weight:600}\n.web-search-provider{display:grid;gap:8px;min-width:0}\n.web-search-provider-info{display:grid;gap:3px;font-size:var(--font-size-1,13px);color:var(--gray-11,inherit);overflow-wrap:anywhere}\n.web-search-provider-heading{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;min-height:24px}\n.web-search-provider-heading strong{font-weight:500}\n.web-search-card label{display:grid;gap:6px}\n.web-search-settings input{box-sizing:border-box;width:100%;min-width:0;padding:8px 10px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 22%,transparent));border-radius:8px;background:var(--color-surface,transparent);color:inherit;font:inherit;transition:border-color 150ms ease,box-shadow 150ms ease}\n.web-search-settings input:focus{border-color:var(--accent-9,#3b82f6);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent-9,#3b82f6) 25%,transparent)}\n.web-search-settings button:not([role=\"switch\"]){min-height:34px;padding:6px 12px;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;background:transparent;color:inherit;cursor:pointer;font:inherit;justify-self:start;transition:background-color 150ms ease,color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,transform 150ms ease}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):hover:not(:disabled){background:var(--gray-3,color-mix(in srgb,currentColor 6%,transparent));border-color:color-mix(in srgb,currentColor 35%,transparent)}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):active:not(:disabled){transform:scale(.97)}\n.web-search-danger:hover:not(:disabled){border-color:var(--red-11,#b42318);color:var(--red-11,#b42318);background:color-mix(in srgb,var(--red-11,#b42318) 8%,transparent)}\n.web-search-settings button:disabled{opacity:.45;cursor:not-allowed}\n.web-search-tabs{display:flex;gap:20px;border-bottom:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-tabs button[role=\"tab\"]{padding:8px 0;border:0;border-bottom:2px solid transparent;border-radius:0;color:var(--gray-11,inherit)}\n.web-search-tabs button[role=\"tab\"]:hover{color:inherit}\n.web-search-tabs button[aria-selected=\"true\"]{border-bottom-color:var(--accent-9,#3b82f6);color:var(--accent-11,inherit);font-weight:600}\n.web-search-settings [role=\"tabpanel\"]{border-radius:12px}\n.web-search-settings .web-search-switch{all:unset;box-sizing:border-box;position:relative;display:inline-block;width:36px;height:20px;flex:none;border-radius:999px;background:var(--gray-7,color-mix(in srgb,currentColor 28%,transparent));cursor:pointer}\n.web-search-switch.is-on{background:var(--accent-9,#3b82f6)}\n.web-search-switch:disabled{cursor:not-allowed;opacity:.7}\n.web-search-switch-thumb{position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:999px;background:#fff;transition:transform 150ms ease}\n.web-search-switch.is-on .web-search-switch-thumb{transform:translateX(16px)}\n.web-search-settings :focus-visible{outline:2px solid var(--accent-9,currentColor);outline-offset:3px}\n.web-search-card-actions{display:flex;flex-wrap:wrap;gap:8px}\n.web-search-rank{margin:0;padding:0;list-style:none}\n.web-search-rank li{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto;gap:10px;align-items:start;padding:14px 0;border-top:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-settings button.web-search-drag-handle{display:grid;place-items:center;width:24px;height:24px;min-height:24px;padding:0;border:0;cursor:grab;color:inherit;opacity:.65}\n.web-search-settings button.web-search-drag-handle:hover{opacity:1;background:var(--gray-3,color-mix(in srgb,currentColor 8%,transparent))}\n.web-search-settings button.web-search-drag-handle:active{cursor:grabbing}\n.web-search-settings button.web-search-drag-handle[aria-disabled=\"true\"]{cursor:wait;opacity:.4}\n.web-search-rank li[data-dragging]{opacity:.45}\n.web-search-rank li[data-drop]::after{content:'';position:absolute;left:0;right:0;height:2px;background:var(--accent-9,#3b82f6);pointer-events:none}\n.web-search-rank li[data-drop=\"before\"]::after{top:-1px}\n.web-search-rank li[data-drop=\"after\"]::after{bottom:-1px}\n.web-search-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n.web-search-rank-index{opacity:.6;font-variant-numeric:tabular-nums;line-height:24px}\n.web-search-rank-move{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-end;padding-top:2px}\n.web-search-settings a{color:var(--accent-11,currentColor);font-size:var(--font-size-1,13px);text-underline-offset:3px}\n.web-search-limits{margin:0;border:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 20px}\n.web-search-limits label{display:grid;gap:6px;font-size:var(--font-size-2,14px)}\n@media(max-width:560px){.web-search-rank-move{flex-direction:column;align-items:flex-end}.web-search-limits{grid-template-columns:minmax(0,1fr)}}\n@media(prefers-reduced-motion:reduce){.web-search-switch-thumb{transition:none}.web-search-settings button:not([role=\"switch\"]),.web-search-settings input{transition:none}}\n`\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-web-search-manager'); style.textContent = styles\n style.dataset.dshWebSearch = ''; document.head.appendChild(style)\n return () => style.remove()\n }, 'web-search-manager.styles')\n ctx.effect(() => client.slots.inject('plugins.bundle.config', () => client.slots.register({\n name: 'plugins.bundle.config', key: '@klarkxy/dsh-web-search-manager',\n }, () => <NetworkSearchSettings client={client} />)), 'web-search-manager.settings')\n}\n"],"mappings":";;;;AAGA,MAAa,2BAA2B;AAQxC,MAAM,eAA6C;CACjD,KAAK;EACH,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,OAAO;EACL,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,OAAO;EACL,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,qBAAqB;EACnB,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,KAAK;EACH,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,WAAW;EACT,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,QAAQ;EACN,aAAa;EACb,SAAS;EACT,YAAY;CACd;CACA,QAAQ;EACN,aAAa;EACb,SAAS;EACT,YAAY;CACd;AACF;AAEA,SAAgB,mBAAmB,UAAsC;CACvE,MAAM,QAAQ,OAAO,OAAO,cAAc,SAAS,EAAE,IAAI,aAAa,SAAS,MAAM,KAAA;CACrF,OAAO;EACL,aAAa,OAAO,eAAe,SAAS;EAC5C,SAAS,SAAS,WAAW,OAAO,YAAY,SAAS,YAAY,SAAS,gBAC1E,SAAS,YAAY,oBAAoB,kCACvC;EACN,YAAY,SAAS,cAAc,OAAO,cAAc,SAAS;CACnE;AACF;;;AC7DA,MAAa,yBAAyB;AAgEtC,SAAgB,mBACd,UACA,UACA,QACU;CACV,OAAO,CAAC,GAAG,IAAI,IAAI,SAAS,YAAY,QAAO,OAAM,SAAS,SAAS,EAAE,CAAC,CAAC,CAAC;AAC9E;AAEA,SAAgB,iBAAiB,OAA0B,YAA6C;CACtG,OAAO,MAAM,KAAK,UAAU,KAAK;AACnC;;;ACpEA,MAAa,OAAO;AACpB,MAAa,SAAS;CAAC;CAAS;CAAc;CAAU;AAAoB;AAgB5E,SAAS,OAAU,QAAyB;CAC1C,IAAI,CAAC,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO,MAAM,OAAO;CACpD,OAAO,OAAO;AAChB;AACA,SAAS,SAAS,UAAsD;CACtE,MAAM,EAAE,UAAU,WAAW,GAAG,SAAS;CACzC,OAAO;AACT;AAEA,SAAgB,aAAa,QAAmB,UAAiC;CAC/E,MAAM,UAAU,SAAS,SAAS,WAAW,OAAO,eAAe,OAAO;CAC1E,MAAM,WAAW,OAAO,SAAS,SAAS,SAAS,WAAW,mBAAmB;CACjF,OAAO,WAAW,aAAa,SAAS;AAC1C;AAEA,SAAgB,eAAe,QAAmC;CAChE,MAAM,MAAM,OAAO,UAAU,QAAO,aAAY,SAAS,SAAS,QAAQ;CAM1E,OALc,mBACZ,IAAI,KAAI,aAAY,SAAS,EAAE,GAC/B,OAAO,WACP,OAAM,QAAQ,IAAI,MAAK,aAAY,SAAS,OAAO,EAAE,CAAC,EAAE,aAAa,CAE5D,CAAC,CAAC,SAAQ,OAAM,IAAI,MAAK,aAAY,SAAS,OAAO,EAAE,KAAK,CAAC,CAAC;AAC3E;AAEA,SAAgB,sBAAsB,QAAmC;CACvE,MAAM,UAAU,eAAe,MAAM;CACrC,MAAM,OAAO,OAAO,UAAU,QAAO,aAAY,SAAS,SAAS,YAAY,CAAC,QAAQ,MAAK,QAAO,IAAI,OAAO,SAAS,EAAE,CAAC;CAC3H,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI;AAC7B;AAEA,SAAgB,sBAAsB,QAA6C;CACjF,MAAM,WAAW,eAAe,MAAM;CACtC,MAAM,KAAK,iBAAiB,SAAS,KAAI,aAAY,SAAS,EAAE,IAAG,OAAM,QAAQ,SAAS,MAAK,aAAY,SAAS,OAAO,EAAE,CAAC,EAAE,UAAU,CAAC;CAC3I,OAAO,SAAS,MAAK,aAAY,SAAS,OAAO,EAAE;AACrD;AAEA,SAAS,aAAa,EAAE,KAAK,OAAO,YAA8D;CAChG,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;EAAG,MAAM;EAAK,QAAO;EAAS,KAAI;EAAsB,cAAY;EACzE,UAAS,UAAS;GAChB,MAAM,SAAU,WAAoE;GACpF,IAAI,QAAQ,cAAc;IACxB,MAAM,eAAe;IACrB,OAAO,aAAa,GAAG;GACzB;EACF;EAAI;CAAY,CAAA;AACpB;AAEA,SAAgB,gBACd,OACA,WACA,MACA,UACS;CACT,OAAO,MAAM,MAAK,OAAM;EACtB,MAAM,WAAW,UAAU,MAAK,SAAQ,KAAK,SAAS,YAAY,KAAK,OAAO,EAAE;EAChF,IAAI,CAAC,UAAU,OAAO;EACtB,IAAI,SAAS,YAAY,OAAO;EAChC,MAAM,SAAS,KAAK,SAAS,OAAO,GAAA,CAAI,KAAK;EAC7C,OAAO,QAAQ,SAAS,iBAAiB,SAAS,SAAS,SAAS,mBAAmB,KAAK;CAC9F,CAAC;AACH;AAEA,SAAgB,kBACd,kBACA,WACA,WACA,MACA,UACS;CACT,OAAO,oBAAoB,gBAAgB,WAAW,WAAW,MAAM,QAAQ;AACjF;AAEA,SAAS,cAAc,OAAuF;CAC5G,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,KAAK;CACxC,MAAM,SAAA,GAAQC,MAAAA,OAAAA,CAAsC,KAAA,CAAS;CAC7D,CAAA,GAAA,MAAA,UAAA,aAAsB,aAAa,MAAM,OAAO,GAAG,CAAC,CAAC;CACrD,SAAS,SAAS;EAChB,aAAa,MAAM,OAAO;EAC1B,SAAS,KAAK;CAChB;CACA,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;EAAQ,MAAK;EAAS,WAAU;EAAoB,UAAU,MAAM;EACzE,eAAe;GACb,IAAI,CAAC,OAAO;IACV,SAAS,IAAI;IACb,MAAM,UAAU,iBAAiB,SAAS,KAAK,GAAG,GAAI;IACtD;GACF;GACA,OAAO;GACP,MAAM,UAAU;EAClB;EACA,QAAQ;EAAS,UAAA,QAAQ,MAAM,eAAe,MAAM;CAAc,CAAA;AACtE;AAEA,SAAgB,sBAAsB,EAAE,UAA8B;CACpE,MAAM,UAAA,GAASC,MAAAA,MAAAA,CAAM;CACrB,MAAM,CAAC,KAAK,WAAA,GAAUF,MAAAA,SAAAA,CAAiC,WAAW;CAClE,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAoB;CAChD,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAsB;CAChD,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAiC,CAAC,CAAC;CAC3D,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAS,KAAK;CACtC,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAwB,IAAI;CAChE,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAwB,IAAI;CAChE,MAAM,CAAC,kBAAkB,wBAAA,GAAuBA,MAAAA,SAAAA,CAAS,EAAE;CAC3D,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAS,EAAE;CACnC,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,EAAE;CACrC,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAkC,CAAC,CAAC;CAEpE,eAAe,KAAQ,UAAkB,UAAmB,CAAC,GAAe;EAC1E,OAAO,OAAO,MAAM,OAAO,WAAW,IAAI,KAAK,wBAAwB,UAAU,OAAO,CAAiB;CAC3G;CACA,eAAe,OAAO;EACpB,MAAM,OAAO,MAAM,KAAgB,QAAQ;EAC3C,UAAU,IAAI;EAAG,SAAS,KAAK,QAAQ;EACvC,MAAM,OAAO,KAAK,UAAU,SAAQ,aAAY,SAAS,gBAAgB,CAAC,SAAS,aAAa,IAAI,CAAC,CAAC;EACtG,IAAI,KAAK,QAAQ;GACf,MAAM,cAAc,OAAO,MAAM,OAAO,OAAO,YAAY,SAAS,IAAI,CAAC;GACzE,YAAY,OAAO,YAAY,OAAO,QAAQ,WAAW,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC;EAC1G;EACA,OAAO;CACT;CACA,CAAA,GAAA,MAAA,UAAA,OAAgB;EAAE,KAAU,CAAC,CAAC,YAAY,SAAS,4BAA4B,CAAC;CAAE,GAAG,CAAC,MAAM,CAAC;CAE7F,eAAe,OAAO,KAA0B;EAC9C,QAAQ,IAAI;EAAG,QAAQ,EAAE;EAAG,SAAS,EAAE;EACvC,IAAI;GAAE,MAAM,IAAI;EAAE,SACX,OAAO;GAAE,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO;GAAG,MAAM,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;EAAE,UACjG;GAAE,QAAQ,KAAK;EAAE;CAC3B;CACA,eAAe,OAAO,UAAyC,kBAA0B;EACvF,MAAM,OAAO,MAAM,KAAgB,UAAU;GAAE;GAAU;EAAiB,CAAC;EAC3E,UAAU,IAAI;EAAG,SAAS,KAAK,QAAQ;EACvC,OAAO;CACT;CAEA,eAAe,QAAQ,IAAa,OAAiB;EACnD,IAAI,CAAC,UAAU,CAAC,OAAO;EACvB,KAAK,MAAM,YAAY,OAAO,WAAW;GACvC,IAAI,SAAS,SAAS,UAAU;GAChC,MAAM,WAAW,KAAK,SAAS,OAAO;GACtC,MAAM,QAAQ,SAAS,KAAK;GAC5B,IAAI,CAAC,SAAS,iBAAiB,CAAC,OAAO;GACvC,IAAI,SAAS,SAAS,mBAAmB,OAAO,MAAM,IAAI,MAAM,eAAe;GAC/E,OAAO,MAAM,OAAO,OAAO,YAAY,IAAI,SAAS,eAAe,KAAK,CAAC;GACzE,SAAQ,YAAW,QAAQ,SAAS,QAAQ,WAAW;IAAE,GAAG;KAAU,SAAS,KAAK;GAAG,IAAI,OAAO;EACpG;EACA,MAAM,WAAW,QAAoC;GACnD,MAAM,MAAM,OAAO,MAAK,SAAQ,KAAK,OAAO,GAAG;GAC/C,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,SAAS,MAAM,IAAI,IAAI,MAAM,OAAO,IAAI,EAAE,CAAC;EAC7F;EACA,MAAM,OAAO;GACX,GAAG,SAAS,OAAO,QAAQ;GAC3B,YAAY,QAAQ,YAAY;GAAG,YAAY,QAAQ,YAAY;GACnE,WAAW,QAAQ,WAAW;GAAG,eAAe,QAAQ,eAAe;GACvE,WAAW,MAAM;GAAW,aAAa;GACzC,eAAe;GAAI,eAAe;GAAQ,cAAc;EAC1D,GAAG,OAAO,SAAS,QAAQ;EAC3B,MAAM,KAAK;CACb;CAEA,eAAe,UAAU,UAAwB;EAC/C,IAAI,CAAC,SAAS,iBAAiB,SAAS,oBAAoB,CAAC,QAAQ;EACrE,OAAO,MAAM,OAAO,OAAO,YAAY,MAAM,SAAS,aAAa,CAAC;EACpE,SAAQ,aAAY;GAAE,GAAG;IAAU,SAAS,KAAK;EAAG,EAAE;EACtD,MAAM,KAAK;EACX,QAAQ,UAAU;CACpB;CAEA,IAAI,CAAC,UAAU,CAAC,OACd,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;EAAS,WAAU;EAAnB,UAAA,CACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;GAAG,MAAK;GAAU,UAAA,SAAS;EAAa,CAAA,GACvC,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GAAQ,MAAK;GAAS,eAAe,KAAK,OAAO,YAAY;IAAE,MAAM,KAAK;GAAE,CAAC;GAAG,UAAA;EAAY,CAAA,IAAI,IAClG;;CAGX,MAAM,UAAU,eAAe,MAAM;CACrC,MAAM,WAAW,sBAAsB,MAAM;CAC7C,MAAM,WAAW,sBAAsB,MAAM;CAC7C,MAAM,KAAK,OAAO,SAAS;CAC3B,MAAM,QAAQ,QAAQ,KAAI,aAAY,SAAS,EAAE;CACjD,MAAM,YAAY,gBAAgB,OAAO,OAAO,WAAW,MAAM,QAAQ;CACzE,MAAM,SAAS;EACb;GAAC;GAAc;GAAa;GAAG;EAAE;EACjC;GAAC;GAAc;GAAe;GAAG;EAAC;EAClC;GAAC;GAAa;GAAY;GAAM;EAAM;EACtC;GAAC;GAAiB;GAAY;GAAM;EAAM;CAC5C;CAEA,SAAS,KAAK,IAAY,WAAmB;EAC3C,MAAM,QAAQ,MAAM,QAAQ,EAAE;EAC9B,IAAI,QAAQ,QAAQ,KAAK,YAAY,KAAK,aAAa,MAAM,UAAU,UAAU,WAAW;EAC5F,MAAM,OAAO,CAAC,GAAG,KAAK;EACtB,MAAM,CAAC,QAAQ,KAAK,OAAO,OAAO,CAAC;EACnC,KAAK,OAAO,WAAW,GAAG,IAAI;EAC9B,OAAY,YAAY;GACtB,MAAM,QAAQ,IAAI,IAAI;GACtB,qBAAqB,SAAS,MAAK,aAAY,SAAS,OAAO,EAAE,CAAC,EAAE,SAAS,MAAM,YAAY,YAAY,KAAK,IAAI;EACtH,CAAC;CACH;CAEA,SAAS,UAAU;EACjB,cAAc,IAAI;EAClB,cAAc,IAAI;CACpB;CAEA,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;EAAS,WAAU;EAAsB,eAAY;EAArD,UAAA;GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAU;IAAkB,MAAK;IAAU,cAAW;IAAS,YAAW,UAAS;KACtF,IAAI,CAAC;MAAC;MAAa;MAAc;MAAQ;KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,GAAG;KACrE,MAAM,UAAU,CAAC,GAAG,MAAM,cAAc,iBAAoC,gBAAc,CAAC;KAC3F,MAAM,QAAQ,QAAQ,QAAQ,MAAM,MAA2B;KAC/D,IAAI,QAAQ,GAAG;KACf,MAAM,eAAe;KACrB,MAAM,OAAO,MAAM,QAAQ,SAAS,IAAI,MAAM,QAAQ,QAAQ,QAAQ,SAAS,KAC1E,SAAS,MAAM,QAAQ,eAAe,IAAI,MAAM,QAAQ,UAAU,QAAQ;KAC/E,QAAQ,KAAK,EAAE,MAAM;KACrB,QAAQ,KAAK,EAAE,MAAM;IACvB;IACI,UAAA,CAAC,CAAC,aAAa,MAAM,GAAG,CAAC,UAAU,MAAM,CAAC,CAAC,CAAW,KAAK,CAAC,KAAK,WACjE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KAAkB,MAAK;KAAS,MAAK;KAAM,IAAI,SAAS,MAAM,MAAM;KAClE,iBAAe,SAAS,MAAM,MAAM;KAAU,iBAAe,QAAQ;KACrE,UAAU,QAAQ,MAAM,IAAI;KAAI,eAAe,OAAO,GAAG;KAAI,UAAA;IAAc,GAFhE,GAEgE,CAC9E;GACE,CAAA;GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAAM,WAAU;IAAqB,MAAK;IAAS,aAAU;IAAU,UAAA;GAAuB,CAAA;GAC7F,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,MAAK;IAAQ,WAAU;IAAoB,UAAA;GAAS,CAAA;GAChE,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,MAAK;IAAU,UAAA;GAAQ,CAAA;GAClC,OAAO,iBAAiB,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,MAAK;IAAQ,UAAA;GAA4B,CAAA;GACrE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,MAAK;IAAW,IAAI,SAAS;IAAoB,mBAAiB,SAAS;IAC9E,QAAQ,QAAQ;IAAa,UAAU;IACtC,UAAA,SAAS,WAAW,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;KAAG,WAAU;KAAmB,UAAA;IAAc,CAAA,IAAI,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;KACzE,WAAU;KACV,eAAY;KACZ,WAAS,KAAK,SAAS;KAHkD,UAAA;MAIzE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,EAAA,UAAI,OAAQ,CAAA,GACX,CAAC,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;OAAG,WAAU;OAAkB,UAAA;MAAkB,CAAA,CAC7D,EAAA,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OACE,MAAK;OACL,MAAK;OACL,WAAW,oBAAoB,KAAK,WAAW;OAC/C,gBAAc;OACd,cAAY,KAAK,WAAW;OAC5B,UAAU,QAAS,CAAC,MAAM,CAAC;OAC3B,eAAe,KAAK,aAAa,QAAQ,CAAC,IAAI,KAAK,CAAC;OACpD,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAA0B,eAAY;OAAQ,CAAA;MACxD,CAAA,CACF,EAAA,CAAA;MACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;OAAI,WAAU;OAAkB,cAAW;OACxC,UAAA,SAAS,KAAI,aAAY;QACxB,MAAM,gBAAgB,MAAM,SAAS,SAAS,EAAE;QAChD,MAAM,OAAO,MAAM,QAAQ,SAAS,EAAE;QACtC,MAAM,SAAS,OAAO,gBAAgB,UAAU,OAAO,SAAS;QAChE,MAAM,OAAO,mBAAmB,QAAQ;QACxC,MAAM,WAAW,QAAQ,SAAS,aAAa;QAC/C,MAAM,SAAS,QAAQ,SAAS,gBAAgB;QAChD,MAAM,UAAU,YAAY,kBAAkB,CAAC,UAAU,CAAC,SAAS;QACnE,MAAM,UAAU,iBAAiB,MAAM,SAAS;QAChD,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;SAAsB,WAAW,SAAS,cAAc,KAAA;SAAW,eAAa,mBAAmB,SAAS;SACjH,iBAAe,eAAe,SAAS,MAAM,KAAA;SAC7C,aAAW,eAAe,SAAS,MAAM,aAAc,MAAM,QAAQ,UAAU,IAAI,OAAO,UAAU,WAAY,KAAA;SAChH,aAAY,UAAS;UACnB,IAAI,QAAQ,CAAC,WAAW,CAAC,cAAc,eAAe,SAAS,IAAI;UACnE,MAAM,eAAe;UACrB,MAAM,aAAa,aAAa;UAChC,cAAc,SAAS,EAAE;SAC3B;SACA,cAAa,UAAS;UACpB,IAAI,CAAC,MAAM,cAAc,SAAS,MAAM,aAA4B,GAAG,cAAc,IAAI;SAC3F;SACA,SAAQ,UAAS;UACf,IAAI,QAAQ,CAAC,WAAW,CAAC,YAAY;UACrC,MAAM,eAAe;UACrB,KAAK,YAAY,IAAI;UACrB,QAAQ;SACV;SAjBK,UAAA;UAkBJ,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;WAAQ,MAAK;WAAS,WAAU;WACzC,WAAW,CAAC;WAAM,iBAAe;WACjC,cAAY,UAAU,SAAS;WAC/B,OAAM;WACN,cAAa,UAAS;YACpB,IAAI,MAAM;aAAE,MAAM,eAAe;aAAG;YAAO;YAC3C,MAAM,aAAa,gBAAgB;YACnC,MAAM,aAAa,QAAQ,qCAAqC,SAAS,EAAE;YAC3E,MAAM,MAAM,MAAM,cAAc,QAAQ,IAAI;YAC5C,IAAI,KAAK,MAAM,aAAa,aAAa,KAAK,IAAI,EAAE;YACpD,cAAc,SAAS,EAAE;YACzB,oBAAoB,EAAE;WACxB;WACA,WAAW;WACX,YAAW,UAAS;YAClB,IAAI,MAAM,QAAQ,aAAa,MAAM,QAAQ,aAAa;YAC1D,MAAM,eAAe;YACrB,KAAK,SAAS,IAAI,QAAQ,MAAM,QAAQ,YAAY,KAAK,EAAE;WAC7D;WACA,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;YAAK,OAAM;YAAK,QAAO;YAAK,SAAQ;YAAY,MAAK;YAAe,eAAY;YAAhF,UAAA;aACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;cAAQ,IAAG;cAAI,IAAG;cAAI,GAAE;aAAO,CAAA;aAAC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;cAAQ,IAAG;cAAK,IAAG;cAAI,GAAE;aAAO,CAAA;aAChE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;cAAQ,IAAG;cAAI,IAAG;cAAK,GAAE;aAAO,CAAA;aAAC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;cAAQ,IAAG;cAAK,IAAG;cAAK,GAAE;aAAO,CAAA;aAClE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;cAAQ,IAAG;cAAI,IAAG;cAAK,GAAE;aAAO,CAAA;aAAC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;cAAQ,IAAG;cAAK,IAAG;cAAK,GAAE;aAAO,CAAA;YAC/D;;UACC,CAAA,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAU;WAAyB,UAAA,gBAAgB,OAAO,IAAI;UAAU,CAAA;UAC1F,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;WAAK,WAAU;WAAf,UAAA;YACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;aAAK,WAAU;aAAf,UAAA;cACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD,EAAA,UAAS,SAAS,MAAc,CAAA;cAC/B,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;eAAM,WAAU;eAAkB,UAAA;cAAU,CAAA;cACtD,SAAS,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD;eAAc,KAAK,SAAS;eAAW,OAAO,SAAS,QAAQ;eAAQ,UAAA;cAAiB,CAAA;aAC5G;;YACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;aAAK,WAAU;aAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UAAI,KAAK,YAAe,CAAA,GACxB,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD,EAAA,UAAS,OAAa,CAAA,GACtB,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD,EAAA,UAAA,CAAI,KAAK,SAAS,KAAK,cAAc,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CAAE,KAAC,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD;cAAc,KAAK,KAAK;cAC9D,OAAO,SAAS,QAAQ;cAAU,UAAA,SAAS,OAAO,QAAQ,OAAO;aAAqB,CAAA,CAAG,EAAA,CAAA,CAAK,EAAA,CAAA,CACzF,EAAA,CAAA,CACN;;YACJ,iBAAiB,CAAC,SAAS,cAAc,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;aAAG,WAAU;aAAmB,UAAA,WAAW,YAAY;YAAU,CAAA;YAC1G,UAAU,iBAAiB,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD,EAAA,UAAQ,SAAS,kBAAkB,YAAmB,CAAA;YACjF,UAAU,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD,EAAA,UAAA,CAAO,WAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;aAAO,MAAK;aAAW,cAAa;aAAM,YAAY;aACpD,UAAU,QAAQ,SAAS,SAAS,iBAAiB,QAAQ;aAC7D,OAAO,KAAK,SAAS,OAAO;aAC5B,aAAa,SAAS,aAAa,cAAc;aACjD,WAAU,UAAS,SAAQ,aAAY;cAAE,GAAG;eAAU,SAAS,KAAK,MAAM,OAAO;aAAM,EAAE;YAAI,CAAA,CAC1F,EAAA,CAAA,IAAI;YACV,YAAY,CAAC,UAAU,SAAS,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,eAAD;aAC5C,UAAU,QAAQ,SAAS,SAAS,iBAAiB,QAAQ;aAC7D,OAAM;aAAS,cAAa;aAC5B,iBAAiB,KAAK,aAAa,UAAU,QAAQ,CAAC;YAAI,CAAA,IAAI;WAC7D;;UACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;WAAK,WAAU;WACb,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;YACE,MAAK;YACL,MAAK;YACL,WAAW,oBAAoB,gBAAgB,WAAW;YAC1D,gBAAc;YACd,cAAY,GAAG,gBAAgB,OAAO,KAAK,GAAG,SAAS;YACvD,UAAU;YACV,eAAe;aACb,MAAM,OAAO,gBAAgB,MAAM,QAAO,OAAM,OAAO,SAAS,EAAE,IAAI,CAAC,GAAG,OAAO,SAAS,EAAE;aAC5F,aAAkB,QAAQ,kBAAkB,OAAO,SAAS,eAAe,MAAM,OAAO,WAAW,MAAM,QAAQ,GAAG,IAAI,CAAC;YAC3H;YACA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;aAAM,WAAU;aAA0B,eAAY;YAAQ,CAAA;WACxD,CAAA;UACL,CAAA;SACH;QAtFY,GAAA,SAAS,EAsFrB;OACN,CAAC;MACC,CAAA;MACJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD;OAAG,WAAU;OAAb,UAAA;QAA+B;QAAO;QAAyB;OAAoB;;MAClF,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OACnB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,UAAU;QAAM,eAAe,KAAK,OAAO,YAAY;SAC3E,MAAM,QAAQ,OAAO,SAAS,eAAe,KAAK;SAClD,MAAM,SAAS,MAAM,KAA0B,MAAM;SACrD,MAAM,KAAK;SACX,QAAQ,WAAW,OAAO,QAAQ,MAAM;QAC1C,CAAC;QAAG,UAAA;OAAkB,CAAA;MACnB,CAAA,IAAI;KACF;;GACN,CAAA;GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,MAAK;IAAW,IAAI,SAAS;IAAiB,mBAAiB,SAAS;IAC3E,QAAQ,QAAQ;IAAU,UAAU;IACpC,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;KAAU,WAAU;KAAoB,UAAU;KAAM,cAAW;KAChE,UAAA,OAAO,KAAK,CAAC,KAAK,OAAO,KAAK,SAC7B,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD,EAAA,UAAA,CAAkB,OAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MAAO,MAAK;MAAc;MAAU;MAAK,MAAM;MAAG,OAAO,MAAM;MAC7D,WAAU,UAAS,SAAS;OAAE,GAAG;QAAQ,MAAM,OAAO,MAAM,OAAO,KAAK;MAAE,CAAC;KAAI,CAAA,CAC5E,EAAA,GAHK,GAGL,CACR;IACO,CAAA;GACP,CAAA;GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IAAQ,MAAK;IAAS,UAAU;IAAM,eAAe,KAAK,OAAO,YAAY;KAC3E,MAAM,QAAQ,OAAO,SAAS,eAAe,KAAK;KAClD,QAAQ,MAAM;IAChB,CAAC;IAAG,UAAA;GAAU,CAAA;EACP;;AACX;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDf,SAAgB,MAAM,KAAoB;CACxC,MAAM,SAAS;CACf,IAAI,aAAa;EACf,IAAI,OAAO,aAAa,aAAa,aAAa,CAAC;EACnD,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,aAAa,eAAe,iCAAiC;EAAG,MAAM,cAAc;EAC1F,MAAM,QAAQ,eAAe;EAAI,SAAS,KAAK,YAAY,KAAK;EAChE,aAAa,MAAM,OAAO;CAC5B,GAAG,2BAA2B;CAC9B,IAAI,aAAa,OAAO,MAAM,OAAO,+BAA+B,OAAO,MAAM,SAAS;EACxF,MAAM;EAAyB,KAAK;CACtC,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAD,EAA+B,OAAS,CAAA,CAAC,CAAC,GAAG,6BAA6B;AACrF"}
|
package/lib/client.js
CHANGED
|
@@ -676,11 +676,11 @@ function NetworkSearchSettings({ client }) {
|
|
|
676
676
|
}),
|
|
677
677
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
678
678
|
className: "web-search-provider-info",
|
|
679
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: info.description }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [info.pricing, info.pricingUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExternalLink, {
|
|
679
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: info.description }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: "费用说明" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [info.pricing, info.pricingUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExternalLink, {
|
|
680
680
|
url: info.pricingUrl,
|
|
681
681
|
label: provider.label + " 费用说明",
|
|
682
682
|
children: provider.id === "ddg" ? "官网" : "费用说明"
|
|
683
|
-
})] })] })]
|
|
683
|
+
})] })] })] })]
|
|
684
684
|
}),
|
|
685
685
|
participating && !provider.configured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
686
686
|
className: "web-search-meta",
|
|
@@ -852,11 +852,9 @@ function apply(ctx) {
|
|
|
852
852
|
document.head.appendChild(style);
|
|
853
853
|
return () => style.remove();
|
|
854
854
|
}, "web-search-manager.styles");
|
|
855
|
-
ctx.effect(() => client.slots.inject("
|
|
856
|
-
name: "
|
|
857
|
-
|
|
858
|
-
order: 70,
|
|
859
|
-
label: "网络搜索"
|
|
855
|
+
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
856
|
+
name: "plugins.bundle.config",
|
|
857
|
+
key: "@klarkxy/dsh-web-search-manager"
|
|
860
858
|
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSearchSettings, { client }))), "web-search-manager.settings");
|
|
861
859
|
}
|
|
862
860
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -4,8 +4,95 @@ import { WebError } from "@deepseek-ai/dsh-web";
|
|
|
4
4
|
import { DEEPSEEK_DEFAULT_API_VERSION, DEEPSEEK_DEFAULT_BASE_URL, DEEPSEEK_DEFAULT_MAX_TOKENS, DEEPSEEK_DEFAULT_MAX_USES, DEEPSEEK_DEFAULT_MODEL, DeepSeekSearchProvider } from "@deepseek-ai/dsh-web-search-deepseek";
|
|
5
5
|
import { ExaSearchProvider } from "@deepseek-ai/dsh-web-search-exa";
|
|
6
6
|
import { HttpFetchProvider } from "@deepseek-ai/dsh-web-fetch-http";
|
|
7
|
+
import { isIP } from "node:net";
|
|
7
8
|
import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
8
9
|
import { z } from "zod";
|
|
10
|
+
//#region src/jina-fetch.ts
|
|
11
|
+
/** Do not send obvious local addresses, credentials, or nested Reader URLs to a third party. */
|
|
12
|
+
function readerUrl(value) {
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL(value);
|
|
15
|
+
const host = url.hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "").toLowerCase();
|
|
16
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || isIP(host) || !host.includes(".") || host === "r.jina.ai" || /\.(?:localhost|local|internal|lan|home|test|invalid)$/.test(host)) return void 0;
|
|
17
|
+
return `https://r.jina.ai/${url.href}`;
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function object(value) {
|
|
23
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
24
|
+
}
|
|
25
|
+
/** Accept Reader's text envelope or negotiated JSON, but not error/empty responses. */
|
|
26
|
+
function readerContent(result) {
|
|
27
|
+
if (result.statusCode < 200 || result.statusCode >= 300 || result.body.kind !== "text") return void 0;
|
|
28
|
+
const content = result.body.content;
|
|
29
|
+
if (content.trimStart().startsWith("{")) try {
|
|
30
|
+
const envelope = object(JSON.parse(content));
|
|
31
|
+
if (!envelope || envelope.code !== void 0 && envelope.code !== 200) return void 0;
|
|
32
|
+
const page = object(envelope.data) ?? envelope;
|
|
33
|
+
return typeof page.content === "string" && page.content.trim() && !page.warning ? page.content : void 0;
|
|
34
|
+
} catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const marker = /^Markdown Content:[\t ]*\r?$/m.exec(content);
|
|
38
|
+
if (!marker || /^Warning:/im.test(content.slice(0, marker.index))) return void 0;
|
|
39
|
+
return content.slice(marker.index + marker[0].length).trim() ? content : void 0;
|
|
40
|
+
}
|
|
41
|
+
/** Bound the Reader stage even if a provider fails to honour its cooperative signal. */
|
|
42
|
+
async function readWithTimeout(read, timeoutMs, parent) {
|
|
43
|
+
parent?.throwIfAborted();
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const signal = parent ? AbortSignal.any([parent, controller.signal]) : controller.signal;
|
|
46
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
47
|
+
let off = () => {};
|
|
48
|
+
const aborted = new Promise((_, reject) => {
|
|
49
|
+
const onAbort = () => reject(signal.reason);
|
|
50
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
51
|
+
off = () => signal.removeEventListener("abort", onAbort);
|
|
52
|
+
});
|
|
53
|
+
try {
|
|
54
|
+
return await Promise.race([Promise.resolve().then(() => {
|
|
55
|
+
signal.throwIfAborted();
|
|
56
|
+
return read(signal);
|
|
57
|
+
}), aborted]);
|
|
58
|
+
} finally {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
off();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** One managed fetch route: anonymous Jina Reader first, existing safe HTTP transport second. */
|
|
64
|
+
var JinaFirstFetchProvider = class {
|
|
65
|
+
id = "http";
|
|
66
|
+
options;
|
|
67
|
+
constructor(options) {
|
|
68
|
+
if (!Number.isInteger(options.readerTimeoutMs) || options.readerTimeoutMs < 1 || options.readerTimeoutMs > 2147483647) throw new Error("Invalid Reader timeout");
|
|
69
|
+
this.options = options;
|
|
70
|
+
}
|
|
71
|
+
available() {
|
|
72
|
+
return this.options.direct.available();
|
|
73
|
+
}
|
|
74
|
+
async fetch(request, signal) {
|
|
75
|
+
signal?.throwIfAborted();
|
|
76
|
+
const url = readerUrl(request.url);
|
|
77
|
+
if (url && this.options.reader.available()) try {
|
|
78
|
+
const result = await readWithTimeout((combined) => this.options.reader.fetch({ url }, combined), this.options.readerTimeoutMs, signal);
|
|
79
|
+
signal?.throwIfAborted();
|
|
80
|
+
const content = readerContent(result);
|
|
81
|
+
if (content !== void 0) return {
|
|
82
|
+
...result,
|
|
83
|
+
url: request.url,
|
|
84
|
+
body: {
|
|
85
|
+
kind: "text",
|
|
86
|
+
content
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
} catch {
|
|
90
|
+
signal?.throwIfAborted();
|
|
91
|
+
}
|
|
92
|
+
signal?.throwIfAborted();
|
|
93
|
+
return this.options.direct.fetch(request, signal);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
9
96
|
const SEARCH_ENGINE_PAGE = "https://lite.duckduckgo.com/lite/";
|
|
10
97
|
const MAX_RESPONSE_BYTES$2 = 2e6;
|
|
11
98
|
const USER_AGENT = "Mozilla/5.0 (compatible; dsh-editor-search/1.0)";
|
|
@@ -531,16 +618,28 @@ function registerBuiltins(manager) {
|
|
|
531
618
|
}));
|
|
532
619
|
const offHttp = manager.registerFetchProvider({
|
|
533
620
|
id: "http",
|
|
534
|
-
label: "
|
|
621
|
+
label: "网页读取",
|
|
535
622
|
billing: "none",
|
|
536
|
-
description: "
|
|
537
|
-
}, (options) =>
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
623
|
+
description: "优先通过匿名 Jina Reader 读取,失败后本机 HTTP 直连;无需 Key。"
|
|
624
|
+
}, (options) => {
|
|
625
|
+
const limits = {
|
|
626
|
+
maxResponseBytes: 5e6,
|
|
627
|
+
maxBodyChars: options.maxFetchChars,
|
|
628
|
+
timeoutMs: options.timeoutMs,
|
|
629
|
+
maxRedirects: 5,
|
|
630
|
+
userAgent: "dsh-editor/managed-web"
|
|
631
|
+
};
|
|
632
|
+
const readerTimeoutMs = Math.min(15e3, Math.floor(options.timeoutMs / 2));
|
|
633
|
+
return new JinaFirstFetchProvider({
|
|
634
|
+
reader: new HttpFetchProvider({
|
|
635
|
+
...limits,
|
|
636
|
+
maxBodyChars: limits.maxResponseBytes,
|
|
637
|
+
timeoutMs: readerTimeoutMs
|
|
638
|
+
}),
|
|
639
|
+
direct: new HttpFetchProvider(limits),
|
|
640
|
+
readerTimeoutMs
|
|
641
|
+
});
|
|
642
|
+
});
|
|
544
643
|
return () => {
|
|
545
644
|
offHttp();
|
|
546
645
|
offTavily();
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["MAX_RESPONSE_BYTES","MAX_RESPONSE_BYTES","record","readJSON","readJSON","record"],"sources":["../src/search-engine.ts","../src/http-json.ts","../src/rest-search.ts","../src/tavily.ts","../src/builtins.ts","../src/host-rpc.ts","../src/manager.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\n\n/** Keyless `ctx.web` search backend. The model still calls official `web_search`. */\nexport const SEARCH_ENGINE_ID = 'ddg'\nexport const SEARCH_ENGINE_PAGE = 'https://lite.duckduckgo.com/lite/'\nconst MAX_RESPONSE_BYTES = 2_000_000\nconst USER_AGENT = 'Mozilla/5.0 (compatible; dsh-editor-search/1.0)'\n\nexport function searchEngineUrl(query: string, page = SEARCH_ENGINE_PAGE): string {\n const url = new URL(page)\n if (url.protocol !== 'https:' || url.username || url.password) throw new Error('Invalid search page')\n url.searchParams.set('q', query)\n return url.href\n}\n\nfunction decodeEntities(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, ' ')\n}\n\nfunction stripTags(value: string): string {\n return value.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ')\n}\n\nexport function unwrapSearchHref(href: string, pageUrl: string): string | undefined {\n let raw = decodeEntities(href.trim())\n let url: URL\n try { url = new URL(raw, pageUrl) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n const host = url.hostname\n const isDdg = host === 'duckduckgo.com' || host.endsWith('.duckduckgo.com')\n if (isDdg && (url.pathname === '/l/' || url.pathname === '/l')) {\n const nested = url.searchParams.get('uddg') ?? url.searchParams.get('u')\n if (nested) {\n try { url = new URL(nested) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n }\n }\n const resultHost = url.hostname.replace(/^www\\./, '')\n if (resultHost === 'duckduckgo.com' || resultHost.endsWith('.duckduckgo.com')) return\n return url.href\n}\n\nexport function parseSearchEngineHtml(html: string, pageUrl = SEARCH_ENGINE_PAGE): WebSearchSource[] {\n const sources: WebSearchSource[] = []\n const seen = new Set<string>()\n const re = /<a\\b[^>]*href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')[^>]*>([\\s\\S]*?)<\\/a>/gi\n let match: RegExpExecArray | null\n while ((match = re.exec(html))) {\n const url = unwrapSearchHref(match[1] ?? match[2] ?? '', pageUrl)\n if (!url || seen.has(url)) continue\n seen.add(url)\n const title = decodeEntities(stripTags(match[3] ?? '')).trim().slice(0, 1000)\n sources.push(title ? { url, title } : { url })\n }\n return sources\n}\n\nasync function readText(response: Response, signal?: AbortSignal): Promise<string> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return new TextDecoder().decode(bytes)\n}\n\n/** Adapter for `ctx.web.search`; `dsh-tool-web` still owns the model-facing `web_search` tool. */\nexport class SearchEngineProvider implements WebSearchProvider {\n readonly id = SEARCH_ENGINE_ID\n constructor(private readonly options: { fetch?: typeof fetch } = {}) {}\n available(): boolean { return true }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const url = searchEngineUrl(request.query.trim())\n const response = await (this.options.fetch ?? fetch)(url, {\n method: 'GET', redirect: 'follow', signal,\n headers: { accept: 'text/html', 'user-agent': USER_AGENT },\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索页请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = parseSearchEngineHtml(await readText(response, signal), response.url || SEARCH_ENGINE_PAGE)\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法读取公开搜索页。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import type { WebSearchSource } from '@deepseek-ai/dsh-web'\n\nexport const MAX_RESPONSE_BYTES = 2_000_000\n\nexport function record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nexport async function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n}\n\nexport function httpsEndpoint(base: string, path = ''): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}${path}`\n}\n\nexport function sourceFrom(raw: unknown, urlKey: string, titleKey?: string, snippetKey?: string): WebSearchSource | undefined {\n const row = record(raw)\n if (typeof row?.[urlKey] !== 'string') return\n let url: URL\n try { url = new URL(row[urlKey] as string) } catch { return }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) return\n const title = titleKey && typeof row[titleKey] === 'string' ? (row[titleKey] as string).slice(0, 1000) : undefined\n const snippet = snippetKey && typeof row[snippetKey] === 'string' ? (row[snippetKey] as string).slice(0, 8000) : undefined\n return { url: url.href, ...(title ? { title } : {}), ...(snippet ? { snippet } : {}) }\n}\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nimport { httpsEndpoint, readJSON, record, sourceFrom } from './http-json.ts'\n\nexport interface RestSearchOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\n\ntype RestSpec = {\n id: string\n fallback: string\n path: string\n method: 'GET' | 'POST'\n headers(apiKey: string): Record<string, string>\n query?: (request: { query: string; count: number }) => Record<string, string>\n body?: (request: { query: string; count: number }) => unknown\n parse(payload: unknown): WebSearchSource[]\n}\n\nfunction createRestSearch(spec: RestSpec): new (options: RestSearchOptions) => WebSearchProvider {\n return class implements WebSearchProvider {\n readonly id = spec.id\n constructor(private readonly options: RestSearchOptions) {}\n available(): boolean {\n try { httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError(`${spec.id} 配置不可用。`, 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n const query = request.query.trim()\n try {\n signal?.throwIfAborted()\n const url = new URL(httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path))\n for (const [key, value] of Object.entries(spec.query?.({ query, count }) ?? {})) url.searchParams.set(key, value)\n const response = await (this.options.fetch ?? fetch)(url, {\n method: spec.method, redirect: 'error', signal,\n headers: { accept: 'application/json', ...spec.headers(this.options.apiKey) },\n ...(spec.body ? { body: JSON.stringify(spec.body({ query, count })) } : {}),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = spec.parse(await readJSON(response, signal))\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法完成搜索请求。', 'WEB_PROVIDER_ERROR')\n }\n }\n }\n}\n\nexport const BraveSearchProvider = createRestSearch({\n id: 'brave', fallback: 'https://api.search.brave.com', path: '/res/v1/web/search', method: 'GET',\n headers: apiKey => ({ 'x-subscription-token': apiKey }),\n query: ({ query, count }) => ({ q: query, count: String(count) }),\n parse(payload) {\n const results = record(record(payload)?.web)?.results\n return Array.isArray(results) ? results.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n\nexport const BochaSearchProvider = createRestSearch({\n id: 'bocha', fallback: 'https://api.bochaai.com', path: '/v1/web-search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, count, freshness: 'noLimit', summary: false }),\n parse(payload) {\n const pages = record(record(record(payload)?.data)?.webPages)?.value\n ?? record(record(payload)?.webPages)?.value\n return Array.isArray(pages) ? pages.flatMap(row => sourceFrom(row, 'url', 'name', 'snippet') ?? []) : []\n },\n})\n\nexport const SerperSearchProvider = createRestSearch({\n id: 'serper', fallback: 'https://google.serper.dev', path: '/search', method: 'POST',\n headers: apiKey => ({ 'x-api-key': apiKey, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ q: query, num: count }),\n parse(payload) {\n const organic = record(payload)?.organic\n return Array.isArray(organic) ? organic.flatMap(row => sourceFrom(row, 'link', 'title', 'snippet') ?? []) : []\n },\n})\n\nexport const FirecrawlSearchProvider = createRestSearch({\n id: 'firecrawl', fallback: 'https://api.firecrawl.dev', path: '/v1/search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, limit: count }),\n parse(payload) {\n const data = record(payload)?.data\n return Array.isArray(data) ? data.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nexport interface TavilyOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\nconst MAX_RESPONSE_BYTES = 2_000_000\nfunction endpoint(base: string): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}/search`\n}\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\nasync function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n}\n/** Direct REST adapter. No auxiliary LLM, retries, anonymous fallback or auto-depth. */\nexport class TavilySearchProvider implements WebSearchProvider {\n readonly id = 'tavily'\n constructor(private readonly options: TavilyOptions) {}\n available(): boolean {\n try { endpoint(this.options.baseURL ?? 'https://api.tavily.com'); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError('Tavily 配置不可用。', 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const response = await (this.options.fetch ?? fetch)(endpoint(this.options.baseURL ?? 'https://api.tavily.com'), {\n method: 'POST', redirect: 'error', signal,\n headers: { authorization: `Bearer ${this.options.apiKey}`, 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n query: request.query.trim(), max_results: count, search_depth: 'basic', topic: 'general',\n auto_parameters: false, include_answer: false, include_raw_content: false, include_images: false,\n }),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`Tavily 请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const payload = record(await readJSON(response, signal))\n if (!Array.isArray(payload?.results)) throw new Error('Invalid response shape')\n const sources: WebSearchSource[] = []\n for (const raw of payload.results) {\n const row = record(raw)\n if (typeof row?.url !== 'string') continue\n let url: URL\n try { url = new URL(row.url) } catch { continue }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) continue\n sources.push({\n url: url.href,\n ...(typeof row.title === 'string' ? { title: row.title.slice(0, 1000) } : {}),\n ...(typeof row.content === 'string' ? { snippet: row.content.slice(0, 8000) } : {}),\n ...(typeof row.published_date === 'string' && Number.isFinite(Date.parse(row.published_date))\n ? { publishedAt: new Date(row.published_date).toISOString() } : {}),\n })\n }\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('Tavily 搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('Tavily 返回无效响应或网络连接失败。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import {\n DEEPSEEK_DEFAULT_API_VERSION,\n DEEPSEEK_DEFAULT_BASE_URL,\n DEEPSEEK_DEFAULT_MAX_TOKENS,\n DEEPSEEK_DEFAULT_MAX_USES,\n DEEPSEEK_DEFAULT_MODEL,\n DeepSeekSearchProvider,\n} from '@deepseek-ai/dsh-web-search-deepseek'\nimport { ExaSearchProvider } from '@deepseek-ai/dsh-web-search-exa'\nimport { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http'\nimport { SEARCH_ENGINE_ID, SearchEngineProvider } from './search-engine.ts'\nimport { BochaSearchProvider, BraveSearchProvider, FirecrawlSearchProvider, SerperSearchProvider } from './rest-search.ts'\nimport { TavilySearchProvider } from './tavily.ts'\nimport type { WebSearchManager } from './manager.ts'\n\nexport function registerBuiltins(manager: WebSearchManager): () => void {\n const offEngine = manager.registerSearchProvider({\n id: SEARCH_ENGINE_ID, label: 'DuckDuckGo', billing: 'none',\n description: '无需注册。默认后端。',\n }, () => new SearchEngineProvider())\n const offDeepSeek = manager.registerSearchProvider({\n id: 'deepseek-official', label: 'DeepSeek 搜索',\n description: '使用已配置的 DeepSeek API Key。可能产生额外搜索费用。',\n defaultBaseURL: DEEPSEEK_DEFAULT_BASE_URL, credentialRef: 'DEEPSEEK_API_KEY',\n credentialShared: true, credentialHint: '与模型设置共用 Key', billing: 'model-and-tools',\n signupUrl: 'https://platform.deepseek.com/api_keys',\n }, options => new DeepSeekSearchProvider(() => ({\n apiKey: options.apiKey, baseURL: options.baseURL ?? DEEPSEEK_DEFAULT_BASE_URL,\n model: DEEPSEEK_DEFAULT_MODEL, apiVersion: DEEPSEEK_DEFAULT_API_VERSION,\n maxTokens: DEEPSEEK_DEFAULT_MAX_TOKENS, maxUses: DEEPSEEK_DEFAULT_MAX_USES,\n })))\n const offExa = manager.registerSearchProvider({\n id: 'exa', label: 'Exa', description: '独立 Search API。',\n defaultBaseURL: 'https://api.exa.ai', credentialRef: 'DSH_EDITOR_WEB_EXA_API_KEY', billing: 'request',\n signupUrl: 'https://dashboard.exa.ai/api-keys',\n }, options => new ExaSearchProvider({\n apiKey: options.apiKey ?? '', baseURL: options.baseURL ?? 'https://api.exa.ai',\n searchType: 'auto', highlightsPerResult: 1,\n }))\n const offBrave = manager.registerSearchProvider({\n id: 'brave', label: 'Brave', description: 'Brave Search API。',\n defaultBaseURL: 'https://api.search.brave.com', credentialRef: 'DSH_EDITOR_WEB_BRAVE_API_KEY', billing: 'request',\n signupUrl: 'https://api.search.brave.com',\n }, options => new BraveSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offBocha = manager.registerSearchProvider({\n id: 'bocha', label: '博查', description: '中文网页搜索 API。',\n defaultBaseURL: 'https://api.bochaai.com', credentialRef: 'DSH_EDITOR_WEB_BOCHA_API_KEY', billing: 'request',\n signupUrl: 'https://open.bochaai.com',\n }, options => new BochaSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offSerper = manager.registerSearchProvider({\n id: 'serper', label: 'Serper', description: 'Google 结果的 Search API。',\n defaultBaseURL: 'https://google.serper.dev', credentialRef: 'DSH_EDITOR_WEB_SERPER_API_KEY', billing: 'request',\n signupUrl: 'https://serper.dev',\n }, options => new SerperSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offFirecrawl = manager.registerSearchProvider({\n id: 'firecrawl', label: 'Firecrawl', description: '网页搜索与提取 API。',\n defaultBaseURL: 'https://api.firecrawl.dev', credentialRef: 'DSH_EDITOR_WEB_FIRECRAWL_API_KEY', billing: 'request',\n signupUrl: 'https://www.firecrawl.dev',\n }, options => new FirecrawlSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offTavily = manager.registerSearchProvider({\n id: 'tavily', label: 'Tavily', defaultBaseURL: 'https://api.tavily.com',\n description: 'Search API;固定 basic 检索,不自动升级搜索深度。',\n credentialRef: 'DSH_EDITOR_WEB_TAVILY_API_KEY', billing: 'request',\n signupUrl: 'https://app.tavily.com',\n }, options => new TavilySearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offHttp = manager.registerFetchProvider({\n id: 'http', label: 'HTTP 读取', billing: 'none',\n description: '本机直连公开网页,无需 Key,不产生搜索费用。',\n }, options => new HttpFetchProvider({\n maxResponseBytes: 5_000_000, maxBodyChars: options.maxFetchChars,\n timeoutMs: options.timeoutMs, maxRedirects: 5, userAgent: 'dsh-editor/managed-web',\n }))\n return () => {\n offHttp(); offTavily(); offFirecrawl(); offSerper(); offBocha(); offBrave(); offExa(); offDeepSeek(); offEngine()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { RpcResult } from './contracts.ts'\nexport interface HostRpcContext {\n webServer: { register(route: { kind: 'prefix'; path: string; handler(req: IncomingMessage, res: ServerResponse): void | Promise<void> }): () => void }\n connection: { requestRejection?: (request: IncomingMessage) => number | undefined }\n}\nexport function registerHostRpc(ctx: HostRpcContext, channel: string,\n call: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<RpcResult>): () => void {\n return ctx.webServer.register({ kind: 'prefix', path: channel, handler: async (req, res) => {\n // Require the host's origin/auth policy. Missing policy must not expose settings.\n if (!ctx.connection.requestRejection) { res.writeHead(503); res.end(); return }\n const rejection = ctx.connection.requestRejection(req)\n if (rejection !== undefined) { res.writeHead(rejection); res.end(); return }\n const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname\n const endpoint = pathname.startsWith(`${channel}/`) ? pathname.slice(channel.length + 1) : ''\n if (req.method !== 'POST' || !/^[a-z.]+$/.test(endpoint)) { res.writeHead(404); res.end(); return }\n if (String(req.headers['content-type'] ?? '').split(';')[0]?.trim() !== 'application/json') {\n res.writeHead(415); res.end(); return\n }\n const controller = new AbortController()\n res.on('close', () => { if (!res.writableEnded) controller.abort() })\n try {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buffer.length\n if (size > 64 * 1024) { res.writeHead(413, { connection: 'close' }); res.end(); req.destroy(); return }\n chunks.push(buffer)\n }\n let request: Record<string, unknown>\n try {\n const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error()\n request = parsed as Record<string, unknown>\n } catch { res.writeHead(400); res.end(); return }\n if (request.type !== 'client-request' || request.method !== endpoint || typeof request.rpcId !== 'string') {\n res.writeHead(400); res.end(); return\n }\n const result = await call(endpoint, request.payload, controller.signal)\n if (!res.destroyed) {\n res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n res.end(JSON.stringify({ type: 'server-response', rpcId: request.rpcId, result }))\n }\n } catch {\n if (!res.destroyed) { res.writeHead(500); res.end('network settings request failed') }\n }\n } })\n}\n","import { WebError, type WebFetchProvider, type WebSearchProvider } from '@deepseek-ai/dsh-web'\nimport {\n defaultSettings, migrateSearchOrder, pickActiveSearch, providerKey, resolveSearchOrder, validateBaseURL,\n type FetchProviderFactory, type ProviderDescriptor, type ProviderKind, type ProviderOptions,\n type SearchProviderFactory, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport interface WebRegistry {\n registerSearchProvider(provider: WebSearchProvider): () => void\n registerFetchProvider(provider: WebFetchProvider): () => void\n}\nexport interface ManagerOptions {\n web: WebRegistry\n initial?: Omit<WebSettings, 'searchOrder'> & { searchOrder?: string[] }\n resolveCredential(ref: string): Promise<string | undefined>\n save(settings: WebSettings): Promise<void>\n}\ntype Entry = {\n descriptor: ProviderDescriptor; kind: ProviderKind; configured: boolean\n calls: number; failures: number; unregister(): void; active: Set<AbortController>\n}\nfunction fail(code: string, message: string): never { throw new WebError(message, code) }\nfunction bounded(value: number, min: number, max: number, label: string): number {\n if (!Number.isInteger(value) || value < min || value > max) fail('WEB_INVALID_CONFIG', `${label}须为 ${min} 至 ${max} 的整数。`)\n return value\n}\n/** Enforce cancellation even when an extension ignores its cooperative signal. */\nasync function abortable<T>(work: () => Promise<T>, signal: AbortSignal): Promise<T> {\n signal.throwIfAborted()\n let off = () => {}\n const aborted = new Promise<never>((_, reject) => {\n const onAbort = () => reject(new WebError('网络请求已取消或超时。', 'WEB_ABORTED'))\n signal.addEventListener('abort', onAbort, { once: true })\n off = () => signal.removeEventListener('abort', onAbort)\n })\n try {\n return await Promise.race([Promise.resolve().then(() => { signal.throwIfAborted(); return work() }), aborted])\n } finally { off() }\n}\n\n/** Manages consent and provider lifecycle. Selection and execution stay in ctx.web. */\nexport class WebSearchManager {\n private settings: WebSettings\n private entries = new Map<string, Entry>()\n private listeners = new Set<() => void>()\n private pending = Promise.resolve()\n private epoch = 0\n private suspended = false\n private disposed = false\n private storageFailed = false\n\n constructor(private readonly options: ManagerOptions) {\n const initial = structuredClone(options.initial ?? defaultSettings())\n this.settings = { ...defaultSettings(), ...initial, searchOrder: migrateSearchOrder(initial) }\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n private notify(): void {\n for (const listener of this.listeners) {\n try { listener() } catch { /* observer failure cannot authorize a request */ }\n }\n }\n private knownSearchIds(): string[] {\n return [...this.entries.values()].filter(entry => entry.kind === 'search').map(entry => entry.descriptor.id)\n }\n private keyed(id: string): boolean {\n return Boolean(this.entries.get(providerKey('search', id))?.descriptor.credentialRef)\n }\n private configured(id: string): boolean {\n return Boolean(this.entries.get(providerKey('search', id))?.configured)\n }\n private searchOrder(settings: WebSettings = this.settings): string[] {\n return resolveSearchOrder(this.knownSearchIds(), settings, id => this.keyed(id))\n }\n private activeSearchId(settings: WebSettings = this.settings): string {\n return pickActiveSearch(this.searchOrder(settings), id => this.configured(id))\n }\n private selected(entry: Entry): boolean {\n if (this.disposed || this.suspended || !entry.configured) return false\n if (entry.kind === 'fetch') {\n return this.settings.fetchEnabled\n && this.settings.fetchProvider === entry.descriptor.id\n && this.entries.get(providerKey('fetch', entry.descriptor.id)) === entry\n }\n return this.settings.searchEnabled && this.activeSearchId() === entry.descriptor.id\n }\n status(): WebStatus {\n const entries = [...this.entries.values()]\n return {\n settings: structuredClone(this.settings), storageFailed: this.storageFailed,\n searchActive: entries.some(entry => entry.kind === 'search' && this.selected(entry)),\n fetchActive: entries.some(entry => entry.kind === 'fetch' && this.selected(entry)),\n providers: entries.map(entry => ({\n ...entry.descriptor, kind: entry.kind, configured: entry.configured,\n baseURL: this.settings.endpoints[providerKey(entry.kind, entry.descriptor.id)] ?? entry.descriptor.defaultBaseURL,\n calls: entry.calls, failures: entry.failures,\n })).sort((a, b) => providerKey(a.kind, a.id).localeCompare(providerKey(b.kind, b.id))),\n }\n }\n async refresh(): Promise<WebStatus> {\n const epoch = this.epoch\n await Promise.all([...this.entries.entries()].map(async ([key, entry]) => {\n let configured = !entry.descriptor.credentialRef\n if (entry.descriptor.credentialRef) {\n try { configured = Boolean((await this.options.resolveCredential(entry.descriptor.credentialRef))?.trim()) }\n catch { configured = false }\n }\n if (this.disposed || epoch !== this.epoch || this.entries.get(key) !== entry) return\n if (entry.configured !== configured) {\n entry.configured = configured\n if (!configured) this.abort(entry)\n }\n }))\n this.notify()\n return this.status()\n }\n private abort(entry?: Entry): void {\n for (const target of entry ? [entry] : this.entries.values()) {\n for (const controller of target.active) controller.abort()\n }\n }\n private add(kind: ProviderKind, descriptor: ProviderDescriptor): Entry {\n if (this.disposed) fail('WEB_DISABLED', '网络搜索插件已停止。')\n const key = providerKey(kind, descriptor.id)\n if (!/^[a-z][a-z0-9-]{0,63}$/.test(descriptor.id) || !descriptor.label.trim()) fail('WEB_INVALID_PROVIDER', '供应商标识或名称无效。')\n if (descriptor.credentialRef && !/^[A-Z][A-Z0-9_]{1,127}$/.test(descriptor.credentialRef)) fail('WEB_INVALID_PROVIDER', '供应商凭据引用无效。')\n if (descriptor.defaultBaseURL) validateBaseURL(descriptor.defaultBaseURL)\n for (const [value, label] of [[descriptor.signupUrl, '注册地址'], [descriptor.pricingUrl, '费用说明地址']] as const) {\n if (!value) continue\n try {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password) fail('WEB_INVALID_PROVIDER', `${label}须为 HTTPS。`)\n } catch { fail('WEB_INVALID_PROVIDER', `${label}须为 HTTPS。`) }\n }\n if (this.entries.has(key)) fail('WEB_DUPLICATE_PROVIDER', '供应商标识已注册。')\n const entry: Entry = {\n descriptor: Object.freeze({ ...descriptor }), kind, configured: !descriptor.credentialRef,\n calls: 0, failures: 0, unregister() {}, active: new Set(),\n }\n this.entries.set(key, entry)\n return entry\n }\n private registration(entry: Entry, register: () => () => void): () => void {\n const key = providerKey(entry.kind, entry.descriptor.id)\n try { entry.unregister = register() }\n catch (error) { this.entries.delete(key); throw error }\n void this.refresh()\n let removed = false\n return () => {\n if (removed) return\n removed = true\n this.abort(entry)\n this.entries.delete(key)\n entry.unregister()\n this.notify()\n }\n }\n registerSearchProvider(descriptor: ProviderDescriptor, factory: SearchProviderFactory): () => void {\n const entry = this.add('search', descriptor)\n return this.registration(entry, () => this.options.web.registerSearchProvider({\n id: descriptor.id, available: () => this.selected(entry),\n search: (request, signal) => {\n if (typeof request.query !== 'string' || !request.query.trim() || request.query.length > 4000) fail('WEB_INVALID_REQUEST', '查询须为 1 至 4000 字符。')\n const count = Math.min(bounded(request.maxResults ?? this.settings.maxResults, 1, 100, '结果数'), this.settings.maxResults)\n return this.execute(entry, signal, async (options, combined) => {\n const provider = factory(options)\n if (!provider.available()) fail('WEB_PROVIDER_ERROR', '供应商配置不可用。')\n const result = await provider.search({ query: request.query.trim(), maxResults: count }, combined)\n const sources = result.sources.slice(0, count).filter(source => {\n try { const url = new URL(source.url); return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password } catch { return false }\n }).map(source => ({ ...source, title: source.title?.slice(0, 1000), snippet: source.snippet?.slice(0, 8000) }))\n return { ...result, content: result.content?.slice(0, 40_000), sources,\n truncated: result.truncated || result.sources.length > count }\n })\n },\n }))\n }\n registerFetchProvider(descriptor: ProviderDescriptor, factory: FetchProviderFactory): () => void {\n const entry = this.add('fetch', descriptor)\n return this.registration(entry, () => this.options.web.registerFetchProvider({\n id: descriptor.id, available: () => this.selected(entry),\n fetch: (request, signal) => this.execute(entry, signal, async (options, combined) => {\n const provider = factory(options)\n if (!provider.available()) fail('WEB_PROVIDER_ERROR', '供应商配置不可用。')\n const result = await provider.fetch(request, combined)\n const content = result.body.content.slice(0, options.maxFetchChars)\n return { ...result, body: { ...result.body, content },\n truncated: result.truncated || content.length !== result.body.content.length }\n }),\n }))\n }\n private async execute<T>(entry: Entry, signal: AbortSignal | undefined,\n run: (options: ProviderOptions, signal: AbortSignal) => Promise<T>): Promise<T> {\n if (!this.selected(entry)) fail('WEB_DISABLED', '请在设置 → 网络搜索中配置凭据并主动启用。')\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.settings.timeoutMs)\n const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal\n const epoch = this.epoch\n entry.active.add(controller)\n let started = false\n try {\n const ref = entry.descriptor.credentialRef\n const apiKey = ref ? await abortable(() => this.options.resolveCredential(ref), combined) : undefined\n if (ref && !apiKey?.trim()) {\n entry.configured = false\n this.abort(entry)\n this.notify()\n fail('WEB_CREDENTIAL_MISSING', '搜索凭据已删除或不可用,请重新配置。')\n }\n if (epoch !== this.epoch || !this.selected(entry)) fail('WEB_DISABLED', '网络访问设置已改变,请重新发起请求。')\n const options = {\n apiKey, baseURL: this.settings.endpoints[providerKey(entry.kind, entry.descriptor.id)] ?? entry.descriptor.defaultBaseURL,\n timeoutMs: this.settings.timeoutMs, maxFetchChars: this.settings.maxFetchChars,\n }\n started = true\n entry.calls += 1\n const result = await abortable(() => run(options, combined), combined)\n if (epoch !== this.epoch || !this.selected(entry)) fail('WEB_ABORTED', '网络请求已取消。')\n return result\n } catch (error) {\n if (started) entry.failures += 1\n const code = error instanceof WebError ? error.code : undefined\n if (code === 'WEB_CREDENTIAL_MISSING') throw new WebError('搜索凭据不可用,请重新配置。', 'WEB_CREDENTIAL_MISSING')\n if (code === 'WEB_DISABLED') throw new WebError('网络搜索未启用或设置已改变。', 'WEB_DISABLED')\n if (combined.aborted || code === 'WEB_ABORTED') throw new WebError('网络请求已取消或超时。', 'WEB_ABORTED')\n // Do not forward raw provider responses, token-bearing errors, or causes.\n throw new WebError('网络供应商请求失败,请检查凭据、额度和连接。未切换到其他供应商。', 'WEB_PROVIDER_ERROR')\n } finally {\n clearTimeout(timeout)\n entry.active.delete(controller)\n }\n }\n update(next: Omit<WebSettings, 'revision'>, expectedRevision: number): Promise<WebStatus> {\n const task = this.pending.then(async () => {\n if (this.disposed) fail('WEB_DISABLED', '网络搜索插件已停止。')\n if (expectedRevision !== this.settings.revision) fail('WEB_CONFIG_CONFLICT', '设置已被其他页面修改,请刷新后重试。')\n const proposed: WebSettings = { ...structuredClone(next), revision: expectedRevision + 1 }\n bounded(proposed.maxResults, 1, 20, '结果上限')\n bounded(proposed.maxQueries, 1, 5, '查询上限')\n bounded(proposed.timeoutMs, 1000, 120_000, '超时毫秒数')\n bounded(proposed.maxFetchChars, 1000, 200_000, '正文长度')\n for (const [key, value] of Object.entries(proposed.endpoints)) {\n const entry = this.entries.get(key)\n if (entry && !entry.descriptor.defaultBaseURL) fail('WEB_INVALID_CONFIG', '供应商不支持自定义地址。')\n try { proposed.endpoints[key] = validateBaseURL(value) }\n catch { fail('WEB_INVALID_CONFIG', '供应商地址须为无凭据、无查询参数的 HTTPS 地址。') }\n }\n await this.refresh()\n proposed.searchOrder = this.searchOrder(proposed)\n proposed.searchProvider = this.activeSearchId(proposed)\n if (proposed.searchEnabled && !proposed.searchProvider) {\n fail('WEB_CREDENTIAL_MISSING', '请先选择已安装的供应商并填写有效凭据。')\n }\n if (proposed.searchEnabled && this.entries.get(providerKey('fetch', 'http'))) {\n proposed.fetchEnabled = true\n proposed.fetchProvider = 'http'\n }\n if (!proposed.searchEnabled) proposed.fetchEnabled = false\n if (proposed.fetchEnabled) {\n const entry = this.entries.get(providerKey('fetch', proposed.fetchProvider))\n if (!entry) proposed.fetchEnabled = false\n else if (!entry.configured) fail('WEB_CREDENTIAL_MISSING', '请先选择已安装的供应商并填写有效凭据。')\n }\n this.suspended = true\n this.epoch += 1\n this.abort()\n this.notify()\n try { await this.options.save(proposed) }\n catch {\n this.storageFailed = true\n this.notify()\n fail('WEB_CONFIG_SAVE_FAILED', '设置保存失败。为避免意外联网,本次运行已暂停网络访问。')\n }\n this.settings = proposed\n this.storageFailed = false\n this.suspended = false\n this.notify()\n return this.status()\n })\n this.pending = task.then(() => {}, () => {})\n return task\n }\n async dispose(): Promise<void> {\n this.disposed = true\n this.epoch += 1\n this.abort()\n this.notify()\n for (const entry of this.entries.values()) entry.unregister()\n this.entries.clear()\n this.listeners.clear()\n await this.pending\n }\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport { migrateSearchOrder } from './contracts.ts'\nexport const settingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n searchEnabled: z.boolean(), fetchEnabled: z.boolean(),\n searchProvider: z.string().max(64), fetchProvider: z.string().max(64),\n searchOrder: z.array(z.string().min(1).max(64)).max(32),\n maxResults: z.number().int().min(1).max(20), maxQueries: z.number().int().min(1).max(5),\n timeoutMs: z.number().int().min(1000).max(120_000), maxFetchChars: z.number().int().min(1000).max(200_000),\n endpoints: z.record(z.string().regex(/^(search|fetch):[a-z][a-z0-9-]{0,63}$/), z.string().max(2048))\n .refine(value => Object.keys(value).length <= 50),\n}).strict()\nexport const updateSchema = z.object({\n expectedRevision: z.number().int().nonnegative(), settings: settingsSchema.omit({ revision: true }),\n}).strict()\nexport const storedSettingsSchema = settingsSchema.extend({\n searchOrder: settingsSchema.shape.searchOrder.optional(),\n}).transform(settings => ({ ...settings, searchOrder: migrateSearchOrder(settings) }))\nexport const webSettingsDomain = defineDomain({\n name: 'dsh_editor_web_search', version: 1,\n tables: { settings: domainTable<string, z.output<typeof storedSettingsSchema>>(storedSettingsSchema) },\n})\n","import type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@deepseek-ai/dsh-credentials'\nimport { WebError } from '@deepseek-ai/dsh-web'\nimport { WEB_SEARCH_RPC_CHANNEL, type RpcResult } from './contracts.ts'\nimport { registerBuiltins } from './builtins.ts'\nimport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nimport { WebSearchManager } from './manager.ts'\nimport { updateSchema, webSettingsDomain } from './storage.ts'\nexport { WebSearchManager } from './manager.ts'\nexport type { ProviderDescriptor, ProviderOptions, SearchProviderFactory, FetchProviderFactory } from './contracts.ts'\nexport const name = '@klarkxy/dsh-web-search-manager'\nexport const inject = ['web', 'credentials', 'storageDomain', 'connection', 'webServer'] as const\n\ndeclare module '@deepseek-ai/cordis' { interface Context { webSearchManager: WebSearchManager } }\ntype Host = Context & HostRpcContext & {\n credentials: { resolve(ref: ReturnType<typeof credentialRef>): Promise<{ value: string } | undefined> }\n}\nexport async function apply(ctx: Context): Promise<void> {\n const host = ctx as Host\n const domain = await ctx.storageDomain.open(webSettingsDomain)\n const table = domain.table('settings')\n const manager = new WebSearchManager({\n web: ctx.web, initial: table.get('global'),\n resolveCredential: async ref => (await host.credentials.resolve(credentialRef(ref)))?.value,\n save: settings => table.put('global', settings),\n })\n ctx.effect(() => async () => { await manager.dispose(); await domain.close() }, 'web-search-manager.dispose')\n ctx.provide('webSearchManager', manager)\n ctx.effect(() => registerBuiltins(manager), 'web-search-manager.providers')\n ctx.effect(() => registerHostRpc(host, WEB_SEARCH_RPC_CHANNEL, async (endpoint, payload, signal): Promise<RpcResult> => {\n try {\n signal.throwIfAborted()\n if (endpoint === 'status') return { ok: true, value: await manager.refresh() }\n if (endpoint === 'update') {\n const parsed = updateSchema.safeParse(payload)\n if (!parsed.success) return { ok: false, error: { code: 'WEB_INVALID_CONFIG', message: '网络搜索设置格式无效。', details: {} } }\n return { ok: true, value: await manager.update(parsed.data.settings, parsed.data.expectedRevision) }\n }\n if (endpoint === 'test') {\n // Testing is explicit, uses the same authorized registry, and sends no manuscript text.\n const result = await ctx.web.search({ query: 'IANA example domains', maxResults: 1 }, signal)\n return { ok: true, value: { sources: result.sources.length } }\n }\n return { ok: false, error: { code: 'WEB_INVALID_REQUEST', message: '未知操作。', details: {} } }\n } catch (error) {\n return { ok: false, error: {\n code: error instanceof WebError ? error.code : 'WEB_REQUEST_FAILED',\n message: error instanceof WebError ? error.message : '网络设置操作失败,请刷新后重试。', details: {},\n } }\n }\n }), 'web-search-manager.rpc')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["MAX_RESPONSE_BYTES","MAX_RESPONSE_BYTES","record","readJSON","readJSON","record"],"sources":["../src/jina-fetch.ts","../src/search-engine.ts","../src/http-json.ts","../src/rest-search.ts","../src/tavily.ts","../src/builtins.ts","../src/host-rpc.ts","../src/manager.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import { isIP } from 'node:net'\nimport type { WebFetchProvider, WebFetchRequest, WebFetchResult } from '@deepseek-ai/dsh-web'\n\nexport interface JinaFirstFetchOptions {\n readonly reader: WebFetchProvider\n readonly direct: WebFetchProvider\n readonly readerTimeoutMs: number\n}\n\n/** Do not send obvious local addresses, credentials, or nested Reader URLs to a third party. */\nfunction readerUrl(value: string): string | undefined {\n try {\n const url = new URL(value)\n const host = url.hostname.replace(/^\\[|\\]$/g, '').replace(/\\.$/, '').toLowerCase()\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password\n || isIP(host) || !host.includes('.') || host === 'r.jina.ai'\n || /\\.(?:localhost|local|internal|lan|home|test|invalid)$/.test(host)) return undefined\n return `https://r.jina.ai/${url.href}`\n } catch { return undefined }\n}\n\nfunction object(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown> : undefined\n}\n\n/** Accept Reader's text envelope or negotiated JSON, but not error/empty responses. */\nfunction readerContent(result: WebFetchResult): string | undefined {\n if (result.statusCode < 200 || result.statusCode >= 300 || result.body.kind !== 'text') return undefined\n const content = result.body.content\n if (content.trimStart().startsWith('{')) {\n try {\n const envelope = object(JSON.parse(content))\n if (!envelope || (envelope.code !== undefined && envelope.code !== 200)) return undefined\n const page = object(envelope.data) ?? envelope\n return typeof page.content === 'string' && page.content.trim() && !page.warning\n ? page.content : undefined\n } catch { return undefined }\n }\n const marker = /^Markdown Content:[\\t ]*\\r?$/m.exec(content)\n if (!marker || /^Warning:/im.test(content.slice(0, marker.index))) return undefined\n return content.slice(marker.index + marker[0].length).trim() ? content : undefined\n}\n\n/** Bound the Reader stage even if a provider fails to honour its cooperative signal. */\nasync function readWithTimeout(\n read: (signal: AbortSignal) => Promise<WebFetchResult>, timeoutMs: number, parent?: AbortSignal,\n): Promise<WebFetchResult> {\n parent?.throwIfAborted()\n const controller = new AbortController()\n const signal = parent ? AbortSignal.any([parent, controller.signal]) : controller.signal\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n let off = () => {}\n const aborted = new Promise<never>((_, reject) => {\n const onAbort = () => reject(signal.reason)\n signal.addEventListener('abort', onAbort, { once: true })\n off = () => signal.removeEventListener('abort', onAbort)\n })\n try {\n return await Promise.race([\n Promise.resolve().then(() => { signal.throwIfAborted(); return read(signal) }),\n aborted,\n ])\n } finally {\n clearTimeout(timer)\n off()\n }\n}\n\n/** One managed fetch route: anonymous Jina Reader first, existing safe HTTP transport second. */\nexport class JinaFirstFetchProvider implements WebFetchProvider {\n // Preserve stored settings, environment overrides, and the managed registry identity.\n readonly id = 'http'\n private readonly options: JinaFirstFetchOptions\n\n constructor(options: JinaFirstFetchOptions) {\n if (!Number.isInteger(options.readerTimeoutMs) || options.readerTimeoutMs < 1\n || options.readerTimeoutMs > 2_147_483_647) throw new Error('Invalid Reader timeout')\n this.options = options\n }\n\n available(): boolean { return this.options.direct.available() }\n\n async fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult> {\n signal?.throwIfAborted()\n const url = readerUrl(request.url)\n if (url && this.options.reader.available()) {\n try {\n // Only the URL is forwarded. No account, API key, cookie, or caller headers are added.\n const result = await readWithTimeout(\n combined => this.options.reader.fetch({ url }, combined), this.options.readerTimeoutMs, signal,\n )\n signal?.throwIfAborted()\n const content = readerContent(result)\n if (content !== undefined) return { ...result, url: request.url, body: { kind: 'text', content } }\n } catch {\n // A Reader timeout/failure permits fallback; caller cancellation or the total deadline does not.\n signal?.throwIfAborted()\n }\n }\n signal?.throwIfAborted()\n // Keep the original request, cancellation/deadline, URL policy, redirects, and error semantics.\n return this.options.direct.fetch(request, signal)\n }\n}\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\n\n/** Keyless `ctx.web` search backend. The model still calls official `web_search`. */\nexport const SEARCH_ENGINE_ID = 'ddg'\nexport const SEARCH_ENGINE_PAGE = 'https://lite.duckduckgo.com/lite/'\nconst MAX_RESPONSE_BYTES = 2_000_000\nconst USER_AGENT = 'Mozilla/5.0 (compatible; dsh-editor-search/1.0)'\n\nexport function searchEngineUrl(query: string, page = SEARCH_ENGINE_PAGE): string {\n const url = new URL(page)\n if (url.protocol !== 'https:' || url.username || url.password) throw new Error('Invalid search page')\n url.searchParams.set('q', query)\n return url.href\n}\n\nfunction decodeEntities(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, ' ')\n}\n\nfunction stripTags(value: string): string {\n return value.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ')\n}\n\nexport function unwrapSearchHref(href: string, pageUrl: string): string | undefined {\n let raw = decodeEntities(href.trim())\n let url: URL\n try { url = new URL(raw, pageUrl) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n const host = url.hostname\n const isDdg = host === 'duckduckgo.com' || host.endsWith('.duckduckgo.com')\n if (isDdg && (url.pathname === '/l/' || url.pathname === '/l')) {\n const nested = url.searchParams.get('uddg') ?? url.searchParams.get('u')\n if (nested) {\n try { url = new URL(nested) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n }\n }\n const resultHost = url.hostname.replace(/^www\\./, '')\n if (resultHost === 'duckduckgo.com' || resultHost.endsWith('.duckduckgo.com')) return\n return url.href\n}\n\nexport function parseSearchEngineHtml(html: string, pageUrl = SEARCH_ENGINE_PAGE): WebSearchSource[] {\n const sources: WebSearchSource[] = []\n const seen = new Set<string>()\n const re = /<a\\b[^>]*href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')[^>]*>([\\s\\S]*?)<\\/a>/gi\n let match: RegExpExecArray | null\n while ((match = re.exec(html))) {\n const url = unwrapSearchHref(match[1] ?? match[2] ?? '', pageUrl)\n if (!url || seen.has(url)) continue\n seen.add(url)\n const title = decodeEntities(stripTags(match[3] ?? '')).trim().slice(0, 1000)\n sources.push(title ? { url, title } : { url })\n }\n return sources\n}\n\nasync function readText(response: Response, signal?: AbortSignal): Promise<string> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return new TextDecoder().decode(bytes)\n}\n\n/** Adapter for `ctx.web.search`; `dsh-tool-web` still owns the model-facing `web_search` tool. */\nexport class SearchEngineProvider implements WebSearchProvider {\n readonly id = SEARCH_ENGINE_ID\n constructor(private readonly options: { fetch?: typeof fetch } = {}) {}\n available(): boolean { return true }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const url = searchEngineUrl(request.query.trim())\n const response = await (this.options.fetch ?? fetch)(url, {\n method: 'GET', redirect: 'follow', signal,\n headers: { accept: 'text/html', 'user-agent': USER_AGENT },\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索页请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = parseSearchEngineHtml(await readText(response, signal), response.url || SEARCH_ENGINE_PAGE)\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法读取公开搜索页。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import type { WebSearchSource } from '@deepseek-ai/dsh-web'\n\nexport const MAX_RESPONSE_BYTES = 2_000_000\n\nexport function record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nexport async function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n}\n\nexport function httpsEndpoint(base: string, path = ''): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}${path}`\n}\n\nexport function sourceFrom(raw: unknown, urlKey: string, titleKey?: string, snippetKey?: string): WebSearchSource | undefined {\n const row = record(raw)\n if (typeof row?.[urlKey] !== 'string') return\n let url: URL\n try { url = new URL(row[urlKey] as string) } catch { return }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) return\n const title = titleKey && typeof row[titleKey] === 'string' ? (row[titleKey] as string).slice(0, 1000) : undefined\n const snippet = snippetKey && typeof row[snippetKey] === 'string' ? (row[snippetKey] as string).slice(0, 8000) : undefined\n return { url: url.href, ...(title ? { title } : {}), ...(snippet ? { snippet } : {}) }\n}\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nimport { httpsEndpoint, readJSON, record, sourceFrom } from './http-json.ts'\n\nexport interface RestSearchOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\n\ntype RestSpec = {\n id: string\n fallback: string\n path: string\n method: 'GET' | 'POST'\n headers(apiKey: string): Record<string, string>\n query?: (request: { query: string; count: number }) => Record<string, string>\n body?: (request: { query: string; count: number }) => unknown\n parse(payload: unknown): WebSearchSource[]\n}\n\nfunction createRestSearch(spec: RestSpec): new (options: RestSearchOptions) => WebSearchProvider {\n return class implements WebSearchProvider {\n readonly id = spec.id\n constructor(private readonly options: RestSearchOptions) {}\n available(): boolean {\n try { httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError(`${spec.id} 配置不可用。`, 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n const query = request.query.trim()\n try {\n signal?.throwIfAborted()\n const url = new URL(httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path))\n for (const [key, value] of Object.entries(spec.query?.({ query, count }) ?? {})) url.searchParams.set(key, value)\n const response = await (this.options.fetch ?? fetch)(url, {\n method: spec.method, redirect: 'error', signal,\n headers: { accept: 'application/json', ...spec.headers(this.options.apiKey) },\n ...(spec.body ? { body: JSON.stringify(spec.body({ query, count })) } : {}),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = spec.parse(await readJSON(response, signal))\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法完成搜索请求。', 'WEB_PROVIDER_ERROR')\n }\n }\n }\n}\n\nexport const BraveSearchProvider = createRestSearch({\n id: 'brave', fallback: 'https://api.search.brave.com', path: '/res/v1/web/search', method: 'GET',\n headers: apiKey => ({ 'x-subscription-token': apiKey }),\n query: ({ query, count }) => ({ q: query, count: String(count) }),\n parse(payload) {\n const results = record(record(payload)?.web)?.results\n return Array.isArray(results) ? results.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n\nexport const BochaSearchProvider = createRestSearch({\n id: 'bocha', fallback: 'https://api.bochaai.com', path: '/v1/web-search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, count, freshness: 'noLimit', summary: false }),\n parse(payload) {\n const pages = record(record(record(payload)?.data)?.webPages)?.value\n ?? record(record(payload)?.webPages)?.value\n return Array.isArray(pages) ? pages.flatMap(row => sourceFrom(row, 'url', 'name', 'snippet') ?? []) : []\n },\n})\n\nexport const SerperSearchProvider = createRestSearch({\n id: 'serper', fallback: 'https://google.serper.dev', path: '/search', method: 'POST',\n headers: apiKey => ({ 'x-api-key': apiKey, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ q: query, num: count }),\n parse(payload) {\n const organic = record(payload)?.organic\n return Array.isArray(organic) ? organic.flatMap(row => sourceFrom(row, 'link', 'title', 'snippet') ?? []) : []\n },\n})\n\nexport const FirecrawlSearchProvider = createRestSearch({\n id: 'firecrawl', fallback: 'https://api.firecrawl.dev', path: '/v1/search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, limit: count }),\n parse(payload) {\n const data = record(payload)?.data\n return Array.isArray(data) ? data.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nexport interface TavilyOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\nconst MAX_RESPONSE_BYTES = 2_000_000\nfunction endpoint(base: string): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}/search`\n}\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\nasync function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n}\n/** Direct REST adapter. No auxiliary LLM, retries, anonymous fallback or auto-depth. */\nexport class TavilySearchProvider implements WebSearchProvider {\n readonly id = 'tavily'\n constructor(private readonly options: TavilyOptions) {}\n available(): boolean {\n try { endpoint(this.options.baseURL ?? 'https://api.tavily.com'); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError('Tavily 配置不可用。', 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const response = await (this.options.fetch ?? fetch)(endpoint(this.options.baseURL ?? 'https://api.tavily.com'), {\n method: 'POST', redirect: 'error', signal,\n headers: { authorization: `Bearer ${this.options.apiKey}`, 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n query: request.query.trim(), max_results: count, search_depth: 'basic', topic: 'general',\n auto_parameters: false, include_answer: false, include_raw_content: false, include_images: false,\n }),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`Tavily 请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const payload = record(await readJSON(response, signal))\n if (!Array.isArray(payload?.results)) throw new Error('Invalid response shape')\n const sources: WebSearchSource[] = []\n for (const raw of payload.results) {\n const row = record(raw)\n if (typeof row?.url !== 'string') continue\n let url: URL\n try { url = new URL(row.url) } catch { continue }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) continue\n sources.push({\n url: url.href,\n ...(typeof row.title === 'string' ? { title: row.title.slice(0, 1000) } : {}),\n ...(typeof row.content === 'string' ? { snippet: row.content.slice(0, 8000) } : {}),\n ...(typeof row.published_date === 'string' && Number.isFinite(Date.parse(row.published_date))\n ? { publishedAt: new Date(row.published_date).toISOString() } : {}),\n })\n }\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('Tavily 搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('Tavily 返回无效响应或网络连接失败。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import {\n DEEPSEEK_DEFAULT_API_VERSION,\n DEEPSEEK_DEFAULT_BASE_URL,\n DEEPSEEK_DEFAULT_MAX_TOKENS,\n DEEPSEEK_DEFAULT_MAX_USES,\n DEEPSEEK_DEFAULT_MODEL,\n DeepSeekSearchProvider,\n} from '@deepseek-ai/dsh-web-search-deepseek'\nimport { ExaSearchProvider } from '@deepseek-ai/dsh-web-search-exa'\nimport { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http'\nimport { JinaFirstFetchProvider } from './jina-fetch.ts'\nimport { SEARCH_ENGINE_ID, SearchEngineProvider } from './search-engine.ts'\nimport { BochaSearchProvider, BraveSearchProvider, FirecrawlSearchProvider, SerperSearchProvider } from './rest-search.ts'\nimport { TavilySearchProvider } from './tavily.ts'\nimport type { WebSearchManager } from './manager.ts'\n\nexport function registerBuiltins(manager: WebSearchManager): () => void {\n const offEngine = manager.registerSearchProvider({\n id: SEARCH_ENGINE_ID, label: 'DuckDuckGo', billing: 'none',\n description: '无需注册。默认后端。',\n }, () => new SearchEngineProvider())\n const offDeepSeek = manager.registerSearchProvider({\n id: 'deepseek-official', label: 'DeepSeek 搜索',\n description: '使用已配置的 DeepSeek API Key。可能产生额外搜索费用。',\n defaultBaseURL: DEEPSEEK_DEFAULT_BASE_URL, credentialRef: 'DEEPSEEK_API_KEY',\n credentialShared: true, credentialHint: '与模型设置共用 Key', billing: 'model-and-tools',\n signupUrl: 'https://platform.deepseek.com/api_keys',\n }, options => new DeepSeekSearchProvider(() => ({\n apiKey: options.apiKey, baseURL: options.baseURL ?? DEEPSEEK_DEFAULT_BASE_URL,\n model: DEEPSEEK_DEFAULT_MODEL, apiVersion: DEEPSEEK_DEFAULT_API_VERSION,\n maxTokens: DEEPSEEK_DEFAULT_MAX_TOKENS, maxUses: DEEPSEEK_DEFAULT_MAX_USES,\n })))\n const offExa = manager.registerSearchProvider({\n id: 'exa', label: 'Exa', description: '独立 Search API。',\n defaultBaseURL: 'https://api.exa.ai', credentialRef: 'DSH_EDITOR_WEB_EXA_API_KEY', billing: 'request',\n signupUrl: 'https://dashboard.exa.ai/api-keys',\n }, options => new ExaSearchProvider({\n apiKey: options.apiKey ?? '', baseURL: options.baseURL ?? 'https://api.exa.ai',\n searchType: 'auto', highlightsPerResult: 1,\n }))\n const offBrave = manager.registerSearchProvider({\n id: 'brave', label: 'Brave', description: 'Brave Search API。',\n defaultBaseURL: 'https://api.search.brave.com', credentialRef: 'DSH_EDITOR_WEB_BRAVE_API_KEY', billing: 'request',\n signupUrl: 'https://api.search.brave.com',\n }, options => new BraveSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offBocha = manager.registerSearchProvider({\n id: 'bocha', label: '博查', description: '中文网页搜索 API。',\n defaultBaseURL: 'https://api.bochaai.com', credentialRef: 'DSH_EDITOR_WEB_BOCHA_API_KEY', billing: 'request',\n signupUrl: 'https://open.bochaai.com',\n }, options => new BochaSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offSerper = manager.registerSearchProvider({\n id: 'serper', label: 'Serper', description: 'Google 结果的 Search API。',\n defaultBaseURL: 'https://google.serper.dev', credentialRef: 'DSH_EDITOR_WEB_SERPER_API_KEY', billing: 'request',\n signupUrl: 'https://serper.dev',\n }, options => new SerperSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offFirecrawl = manager.registerSearchProvider({\n id: 'firecrawl', label: 'Firecrawl', description: '网页搜索与提取 API。',\n defaultBaseURL: 'https://api.firecrawl.dev', credentialRef: 'DSH_EDITOR_WEB_FIRECRAWL_API_KEY', billing: 'request',\n signupUrl: 'https://www.firecrawl.dev',\n }, options => new FirecrawlSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offTavily = manager.registerSearchProvider({\n id: 'tavily', label: 'Tavily', defaultBaseURL: 'https://api.tavily.com',\n description: 'Search API;固定 basic 检索,不自动升级搜索深度。',\n credentialRef: 'DSH_EDITOR_WEB_TAVILY_API_KEY', billing: 'request',\n signupUrl: 'https://app.tavily.com',\n }, options => new TavilySearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offHttp = manager.registerFetchProvider({\n id: 'http', label: '网页读取', billing: 'none',\n description: '优先通过匿名 Jina Reader 读取,失败后本机 HTTP 直连;无需 Key。',\n }, options => {\n const limits = {\n maxResponseBytes: 5_000_000, maxBodyChars: options.maxFetchChars,\n timeoutMs: options.timeoutMs, maxRedirects: 5, userAgent: 'dsh-editor/managed-web',\n }\n const readerTimeoutMs = Math.min(15_000, Math.floor(options.timeoutMs / 2))\n return new JinaFirstFetchProvider({\n // Decode a complete JSON envelope before the manager applies maxFetchChars to its content.\n reader: new HttpFetchProvider({ ...limits, maxBodyChars: limits.maxResponseBytes, timeoutMs: readerTimeoutMs }),\n direct: new HttpFetchProvider(limits),\n readerTimeoutMs,\n })\n })\n return () => {\n offHttp(); offTavily(); offFirecrawl(); offSerper(); offBocha(); offBrave(); offExa(); offDeepSeek(); offEngine()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { RpcResult } from './contracts.ts'\nexport interface HostRpcContext {\n webServer: { register(route: { kind: 'prefix'; path: string; handler(req: IncomingMessage, res: ServerResponse): void | Promise<void> }): () => void }\n connection: { requestRejection?: (request: IncomingMessage) => number | undefined }\n}\nexport function registerHostRpc(ctx: HostRpcContext, channel: string,\n call: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<RpcResult>): () => void {\n return ctx.webServer.register({ kind: 'prefix', path: channel, handler: async (req, res) => {\n // Require the host's origin/auth policy. Missing policy must not expose settings.\n if (!ctx.connection.requestRejection) { res.writeHead(503); res.end(); return }\n const rejection = ctx.connection.requestRejection(req)\n if (rejection !== undefined) { res.writeHead(rejection); res.end(); return }\n const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname\n const endpoint = pathname.startsWith(`${channel}/`) ? pathname.slice(channel.length + 1) : ''\n if (req.method !== 'POST' || !/^[a-z.]+$/.test(endpoint)) { res.writeHead(404); res.end(); return }\n if (String(req.headers['content-type'] ?? '').split(';')[0]?.trim() !== 'application/json') {\n res.writeHead(415); res.end(); return\n }\n const controller = new AbortController()\n res.on('close', () => { if (!res.writableEnded) controller.abort() })\n try {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buffer.length\n if (size > 64 * 1024) { res.writeHead(413, { connection: 'close' }); res.end(); req.destroy(); return }\n chunks.push(buffer)\n }\n let request: Record<string, unknown>\n try {\n const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error()\n request = parsed as Record<string, unknown>\n } catch { res.writeHead(400); res.end(); return }\n if (request.type !== 'client-request' || request.method !== endpoint || typeof request.rpcId !== 'string') {\n res.writeHead(400); res.end(); return\n }\n const result = await call(endpoint, request.payload, controller.signal)\n if (!res.destroyed) {\n res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n res.end(JSON.stringify({ type: 'server-response', rpcId: request.rpcId, result }))\n }\n } catch {\n if (!res.destroyed) { res.writeHead(500); res.end('network settings request failed') }\n }\n } })\n}\n","import { WebError, type WebFetchProvider, type WebSearchProvider } from '@deepseek-ai/dsh-web'\nimport {\n defaultSettings, migrateSearchOrder, pickActiveSearch, providerKey, resolveSearchOrder, validateBaseURL,\n type FetchProviderFactory, type ProviderDescriptor, type ProviderKind, type ProviderOptions,\n type SearchProviderFactory, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport interface WebRegistry {\n registerSearchProvider(provider: WebSearchProvider): () => void\n registerFetchProvider(provider: WebFetchProvider): () => void\n}\nexport interface ManagerOptions {\n web: WebRegistry\n initial?: Omit<WebSettings, 'searchOrder'> & { searchOrder?: string[] }\n resolveCredential(ref: string): Promise<string | undefined>\n save(settings: WebSettings): Promise<void>\n}\ntype Entry = {\n descriptor: ProviderDescriptor; kind: ProviderKind; configured: boolean\n calls: number; failures: number; unregister(): void; active: Set<AbortController>\n}\nfunction fail(code: string, message: string): never { throw new WebError(message, code) }\nfunction bounded(value: number, min: number, max: number, label: string): number {\n if (!Number.isInteger(value) || value < min || value > max) fail('WEB_INVALID_CONFIG', `${label}须为 ${min} 至 ${max} 的整数。`)\n return value\n}\n/** Enforce cancellation even when an extension ignores its cooperative signal. */\nasync function abortable<T>(work: () => Promise<T>, signal: AbortSignal): Promise<T> {\n signal.throwIfAborted()\n let off = () => {}\n const aborted = new Promise<never>((_, reject) => {\n const onAbort = () => reject(new WebError('网络请求已取消或超时。', 'WEB_ABORTED'))\n signal.addEventListener('abort', onAbort, { once: true })\n off = () => signal.removeEventListener('abort', onAbort)\n })\n try {\n return await Promise.race([Promise.resolve().then(() => { signal.throwIfAborted(); return work() }), aborted])\n } finally { off() }\n}\n\n/** Manages consent and provider lifecycle. Selection and execution stay in ctx.web. */\nexport class WebSearchManager {\n private settings: WebSettings\n private entries = new Map<string, Entry>()\n private listeners = new Set<() => void>()\n private pending = Promise.resolve()\n private epoch = 0\n private suspended = false\n private disposed = false\n private storageFailed = false\n\n constructor(private readonly options: ManagerOptions) {\n const initial = structuredClone(options.initial ?? defaultSettings())\n this.settings = { ...defaultSettings(), ...initial, searchOrder: migrateSearchOrder(initial) }\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n private notify(): void {\n for (const listener of this.listeners) {\n try { listener() } catch { /* observer failure cannot authorize a request */ }\n }\n }\n private knownSearchIds(): string[] {\n return [...this.entries.values()].filter(entry => entry.kind === 'search').map(entry => entry.descriptor.id)\n }\n private keyed(id: string): boolean {\n return Boolean(this.entries.get(providerKey('search', id))?.descriptor.credentialRef)\n }\n private configured(id: string): boolean {\n return Boolean(this.entries.get(providerKey('search', id))?.configured)\n }\n private searchOrder(settings: WebSettings = this.settings): string[] {\n return resolveSearchOrder(this.knownSearchIds(), settings, id => this.keyed(id))\n }\n private activeSearchId(settings: WebSettings = this.settings): string {\n return pickActiveSearch(this.searchOrder(settings), id => this.configured(id))\n }\n private selected(entry: Entry): boolean {\n if (this.disposed || this.suspended || !entry.configured) return false\n if (entry.kind === 'fetch') {\n return this.settings.fetchEnabled\n && this.settings.fetchProvider === entry.descriptor.id\n && this.entries.get(providerKey('fetch', entry.descriptor.id)) === entry\n }\n return this.settings.searchEnabled && this.activeSearchId() === entry.descriptor.id\n }\n status(): WebStatus {\n const entries = [...this.entries.values()]\n return {\n settings: structuredClone(this.settings), storageFailed: this.storageFailed,\n searchActive: entries.some(entry => entry.kind === 'search' && this.selected(entry)),\n fetchActive: entries.some(entry => entry.kind === 'fetch' && this.selected(entry)),\n providers: entries.map(entry => ({\n ...entry.descriptor, kind: entry.kind, configured: entry.configured,\n baseURL: this.settings.endpoints[providerKey(entry.kind, entry.descriptor.id)] ?? entry.descriptor.defaultBaseURL,\n calls: entry.calls, failures: entry.failures,\n })).sort((a, b) => providerKey(a.kind, a.id).localeCompare(providerKey(b.kind, b.id))),\n }\n }\n async refresh(): Promise<WebStatus> {\n const epoch = this.epoch\n await Promise.all([...this.entries.entries()].map(async ([key, entry]) => {\n let configured = !entry.descriptor.credentialRef\n if (entry.descriptor.credentialRef) {\n try { configured = Boolean((await this.options.resolveCredential(entry.descriptor.credentialRef))?.trim()) }\n catch { configured = false }\n }\n if (this.disposed || epoch !== this.epoch || this.entries.get(key) !== entry) return\n if (entry.configured !== configured) {\n entry.configured = configured\n if (!configured) this.abort(entry)\n }\n }))\n this.notify()\n return this.status()\n }\n private abort(entry?: Entry): void {\n for (const target of entry ? [entry] : this.entries.values()) {\n for (const controller of target.active) controller.abort()\n }\n }\n private add(kind: ProviderKind, descriptor: ProviderDescriptor): Entry {\n if (this.disposed) fail('WEB_DISABLED', '网络搜索插件已停止。')\n const key = providerKey(kind, descriptor.id)\n if (!/^[a-z][a-z0-9-]{0,63}$/.test(descriptor.id) || !descriptor.label.trim()) fail('WEB_INVALID_PROVIDER', '供应商标识或名称无效。')\n if (descriptor.credentialRef && !/^[A-Z][A-Z0-9_]{1,127}$/.test(descriptor.credentialRef)) fail('WEB_INVALID_PROVIDER', '供应商凭据引用无效。')\n if (descriptor.defaultBaseURL) validateBaseURL(descriptor.defaultBaseURL)\n for (const [value, label] of [[descriptor.signupUrl, '注册地址'], [descriptor.pricingUrl, '费用说明地址']] as const) {\n if (!value) continue\n try {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password) fail('WEB_INVALID_PROVIDER', `${label}须为 HTTPS。`)\n } catch { fail('WEB_INVALID_PROVIDER', `${label}须为 HTTPS。`) }\n }\n if (this.entries.has(key)) fail('WEB_DUPLICATE_PROVIDER', '供应商标识已注册。')\n const entry: Entry = {\n descriptor: Object.freeze({ ...descriptor }), kind, configured: !descriptor.credentialRef,\n calls: 0, failures: 0, unregister() {}, active: new Set(),\n }\n this.entries.set(key, entry)\n return entry\n }\n private registration(entry: Entry, register: () => () => void): () => void {\n const key = providerKey(entry.kind, entry.descriptor.id)\n try { entry.unregister = register() }\n catch (error) { this.entries.delete(key); throw error }\n void this.refresh()\n let removed = false\n return () => {\n if (removed) return\n removed = true\n this.abort(entry)\n this.entries.delete(key)\n entry.unregister()\n this.notify()\n }\n }\n registerSearchProvider(descriptor: ProviderDescriptor, factory: SearchProviderFactory): () => void {\n const entry = this.add('search', descriptor)\n return this.registration(entry, () => this.options.web.registerSearchProvider({\n id: descriptor.id, available: () => this.selected(entry),\n search: (request, signal) => {\n if (typeof request.query !== 'string' || !request.query.trim() || request.query.length > 4000) fail('WEB_INVALID_REQUEST', '查询须为 1 至 4000 字符。')\n const count = Math.min(bounded(request.maxResults ?? this.settings.maxResults, 1, 100, '结果数'), this.settings.maxResults)\n return this.execute(entry, signal, async (options, combined) => {\n const provider = factory(options)\n if (!provider.available()) fail('WEB_PROVIDER_ERROR', '供应商配置不可用。')\n const result = await provider.search({ query: request.query.trim(), maxResults: count }, combined)\n const sources = result.sources.slice(0, count).filter(source => {\n try { const url = new URL(source.url); return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password } catch { return false }\n }).map(source => ({ ...source, title: source.title?.slice(0, 1000), snippet: source.snippet?.slice(0, 8000) }))\n return { ...result, content: result.content?.slice(0, 40_000), sources,\n truncated: result.truncated || result.sources.length > count }\n })\n },\n }))\n }\n registerFetchProvider(descriptor: ProviderDescriptor, factory: FetchProviderFactory): () => void {\n const entry = this.add('fetch', descriptor)\n return this.registration(entry, () => this.options.web.registerFetchProvider({\n id: descriptor.id, available: () => this.selected(entry),\n fetch: (request, signal) => this.execute(entry, signal, async (options, combined) => {\n const provider = factory(options)\n if (!provider.available()) fail('WEB_PROVIDER_ERROR', '供应商配置不可用。')\n const result = await provider.fetch(request, combined)\n const content = result.body.content.slice(0, options.maxFetchChars)\n return { ...result, body: { ...result.body, content },\n truncated: result.truncated || content.length !== result.body.content.length }\n }),\n }))\n }\n private async execute<T>(entry: Entry, signal: AbortSignal | undefined,\n run: (options: ProviderOptions, signal: AbortSignal) => Promise<T>): Promise<T> {\n if (!this.selected(entry)) fail('WEB_DISABLED', '请在设置 → 网络搜索中配置凭据并主动启用。')\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.settings.timeoutMs)\n const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal\n const epoch = this.epoch\n entry.active.add(controller)\n let started = false\n try {\n const ref = entry.descriptor.credentialRef\n const apiKey = ref ? await abortable(() => this.options.resolveCredential(ref), combined) : undefined\n if (ref && !apiKey?.trim()) {\n entry.configured = false\n this.abort(entry)\n this.notify()\n fail('WEB_CREDENTIAL_MISSING', '搜索凭据已删除或不可用,请重新配置。')\n }\n if (epoch !== this.epoch || !this.selected(entry)) fail('WEB_DISABLED', '网络访问设置已改变,请重新发起请求。')\n const options = {\n apiKey, baseURL: this.settings.endpoints[providerKey(entry.kind, entry.descriptor.id)] ?? entry.descriptor.defaultBaseURL,\n timeoutMs: this.settings.timeoutMs, maxFetchChars: this.settings.maxFetchChars,\n }\n started = true\n entry.calls += 1\n const result = await abortable(() => run(options, combined), combined)\n if (epoch !== this.epoch || !this.selected(entry)) fail('WEB_ABORTED', '网络请求已取消。')\n return result\n } catch (error) {\n if (started) entry.failures += 1\n const code = error instanceof WebError ? error.code : undefined\n if (code === 'WEB_CREDENTIAL_MISSING') throw new WebError('搜索凭据不可用,请重新配置。', 'WEB_CREDENTIAL_MISSING')\n if (code === 'WEB_DISABLED') throw new WebError('网络搜索未启用或设置已改变。', 'WEB_DISABLED')\n if (combined.aborted || code === 'WEB_ABORTED') throw new WebError('网络请求已取消或超时。', 'WEB_ABORTED')\n // Do not forward raw provider responses, token-bearing errors, or causes.\n throw new WebError('网络供应商请求失败,请检查凭据、额度和连接。未切换到其他供应商。', 'WEB_PROVIDER_ERROR')\n } finally {\n clearTimeout(timeout)\n entry.active.delete(controller)\n }\n }\n update(next: Omit<WebSettings, 'revision'>, expectedRevision: number): Promise<WebStatus> {\n const task = this.pending.then(async () => {\n if (this.disposed) fail('WEB_DISABLED', '网络搜索插件已停止。')\n if (expectedRevision !== this.settings.revision) fail('WEB_CONFIG_CONFLICT', '设置已被其他页面修改,请刷新后重试。')\n const proposed: WebSettings = { ...structuredClone(next), revision: expectedRevision + 1 }\n bounded(proposed.maxResults, 1, 20, '结果上限')\n bounded(proposed.maxQueries, 1, 5, '查询上限')\n bounded(proposed.timeoutMs, 1000, 120_000, '超时毫秒数')\n bounded(proposed.maxFetchChars, 1000, 200_000, '正文长度')\n for (const [key, value] of Object.entries(proposed.endpoints)) {\n const entry = this.entries.get(key)\n if (entry && !entry.descriptor.defaultBaseURL) fail('WEB_INVALID_CONFIG', '供应商不支持自定义地址。')\n try { proposed.endpoints[key] = validateBaseURL(value) }\n catch { fail('WEB_INVALID_CONFIG', '供应商地址须为无凭据、无查询参数的 HTTPS 地址。') }\n }\n await this.refresh()\n proposed.searchOrder = this.searchOrder(proposed)\n proposed.searchProvider = this.activeSearchId(proposed)\n if (proposed.searchEnabled && !proposed.searchProvider) {\n fail('WEB_CREDENTIAL_MISSING', '请先选择已安装的供应商并填写有效凭据。')\n }\n if (proposed.searchEnabled && this.entries.get(providerKey('fetch', 'http'))) {\n proposed.fetchEnabled = true\n proposed.fetchProvider = 'http'\n }\n if (!proposed.searchEnabled) proposed.fetchEnabled = false\n if (proposed.fetchEnabled) {\n const entry = this.entries.get(providerKey('fetch', proposed.fetchProvider))\n if (!entry) proposed.fetchEnabled = false\n else if (!entry.configured) fail('WEB_CREDENTIAL_MISSING', '请先选择已安装的供应商并填写有效凭据。')\n }\n this.suspended = true\n this.epoch += 1\n this.abort()\n this.notify()\n try { await this.options.save(proposed) }\n catch {\n this.storageFailed = true\n this.notify()\n fail('WEB_CONFIG_SAVE_FAILED', '设置保存失败。为避免意外联网,本次运行已暂停网络访问。')\n }\n this.settings = proposed\n this.storageFailed = false\n this.suspended = false\n this.notify()\n return this.status()\n })\n this.pending = task.then(() => {}, () => {})\n return task\n }\n async dispose(): Promise<void> {\n this.disposed = true\n this.epoch += 1\n this.abort()\n this.notify()\n for (const entry of this.entries.values()) entry.unregister()\n this.entries.clear()\n this.listeners.clear()\n await this.pending\n }\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport { migrateSearchOrder } from './contracts.ts'\nexport const settingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n searchEnabled: z.boolean(), fetchEnabled: z.boolean(),\n searchProvider: z.string().max(64), fetchProvider: z.string().max(64),\n searchOrder: z.array(z.string().min(1).max(64)).max(32),\n maxResults: z.number().int().min(1).max(20), maxQueries: z.number().int().min(1).max(5),\n timeoutMs: z.number().int().min(1000).max(120_000), maxFetchChars: z.number().int().min(1000).max(200_000),\n endpoints: z.record(z.string().regex(/^(search|fetch):[a-z][a-z0-9-]{0,63}$/), z.string().max(2048))\n .refine(value => Object.keys(value).length <= 50),\n}).strict()\nexport const updateSchema = z.object({\n expectedRevision: z.number().int().nonnegative(), settings: settingsSchema.omit({ revision: true }),\n}).strict()\nexport const storedSettingsSchema = settingsSchema.extend({\n searchOrder: settingsSchema.shape.searchOrder.optional(),\n}).transform(settings => ({ ...settings, searchOrder: migrateSearchOrder(settings) }))\nexport const webSettingsDomain = defineDomain({\n name: 'dsh_editor_web_search', version: 1,\n tables: { settings: domainTable<string, z.output<typeof storedSettingsSchema>>(storedSettingsSchema) },\n})\n","import type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@deepseek-ai/dsh-credentials'\nimport { WebError } from '@deepseek-ai/dsh-web'\nimport { WEB_SEARCH_RPC_CHANNEL, type RpcResult } from './contracts.ts'\nimport { registerBuiltins } from './builtins.ts'\nimport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nimport { WebSearchManager } from './manager.ts'\nimport { updateSchema, webSettingsDomain } from './storage.ts'\nexport { WebSearchManager } from './manager.ts'\nexport type { ProviderDescriptor, ProviderOptions, SearchProviderFactory, FetchProviderFactory } from './contracts.ts'\nexport const name = '@klarkxy/dsh-web-search-manager'\nexport const inject = ['web', 'credentials', 'storageDomain', 'connection', 'webServer'] as const\n\ndeclare module '@deepseek-ai/cordis' { interface Context { webSearchManager: WebSearchManager } }\ntype Host = Context & HostRpcContext & {\n credentials: { resolve(ref: ReturnType<typeof credentialRef>): Promise<{ value: string } | undefined> }\n}\nexport async function apply(ctx: Context): Promise<void> {\n const host = ctx as Host\n const domain = await ctx.storageDomain.open(webSettingsDomain)\n const table = domain.table('settings')\n const manager = new WebSearchManager({\n web: ctx.web, initial: table.get('global'),\n resolveCredential: async ref => (await host.credentials.resolve(credentialRef(ref)))?.value,\n save: settings => table.put('global', settings),\n })\n ctx.effect(() => async () => { await manager.dispose(); await domain.close() }, 'web-search-manager.dispose')\n ctx.provide('webSearchManager', manager)\n ctx.effect(() => registerBuiltins(manager), 'web-search-manager.providers')\n ctx.effect(() => registerHostRpc(host, WEB_SEARCH_RPC_CHANNEL, async (endpoint, payload, signal): Promise<RpcResult> => {\n try {\n signal.throwIfAborted()\n if (endpoint === 'status') return { ok: true, value: await manager.refresh() }\n if (endpoint === 'update') {\n const parsed = updateSchema.safeParse(payload)\n if (!parsed.success) return { ok: false, error: { code: 'WEB_INVALID_CONFIG', message: '网络搜索设置格式无效。', details: {} } }\n return { ok: true, value: await manager.update(parsed.data.settings, parsed.data.expectedRevision) }\n }\n if (endpoint === 'test') {\n // Testing is explicit, uses the same authorized registry, and sends no manuscript text.\n const result = await ctx.web.search({ query: 'IANA example domains', maxResults: 1 }, signal)\n return { ok: true, value: { sources: result.sources.length } }\n }\n return { ok: false, error: { code: 'WEB_INVALID_REQUEST', message: '未知操作。', details: {} } }\n } catch (error) {\n return { ok: false, error: {\n code: error instanceof WebError ? error.code : 'WEB_REQUEST_FAILED',\n message: error instanceof WebError ? error.message : '网络设置操作失败,请刷新后重试。', details: {},\n } }\n }\n }), 'web-search-manager.rpc')\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klarkxy/dsh-web-search-manager",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "Web Search: manage web search providers and public-page fetching from one settings page.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"./package.json": "./package.json"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
|
+
"icon.svg",
|
|
34
35
|
"lib",
|
|
35
36
|
"cordis.patch.yml",
|
|
36
37
|
"README.md",
|
|
@@ -59,31 +60,59 @@
|
|
|
59
60
|
"entries": [
|
|
60
61
|
{
|
|
61
62
|
"id": "web-search-manager",
|
|
62
|
-
"title": "
|
|
63
|
-
"description": "
|
|
63
|
+
"title": "网页搜索",
|
|
64
|
+
"description": "在一个设置页管理搜索服务商和公开页面抓取",
|
|
64
65
|
"feature": "web-search",
|
|
65
66
|
"service": "webSearchManager"
|
|
66
67
|
}
|
|
67
68
|
]
|
|
68
69
|
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "tsdown && node ../../scripts/editor-plugins/wrap-client.mjs @klarkxy/dsh-web-search-manager",
|
|
72
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
73
|
+
"test": "vitest run --root ../.. --config vitest.editor-plugins.config.ts --maxWorkers=2 plugins/dsh-web-search-manager/src"
|
|
74
|
+
},
|
|
69
75
|
"dependencies": {
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/dsh-web-search-deepseek": "0.1.7-rc.2",
|
|
72
|
-
"@deepseek-ai/dsh-web-search-exa": "0.1.7-rc.2",
|
|
73
|
-
"@deepseek-ai/dsh-web-fetch-http": "0.1.7-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-web-search-exa": ">=0.1.7-rc.2",
|
|
74
77
|
"zod": "^4.1.5"
|
|
75
78
|
},
|
|
76
79
|
"peerDependencies": {
|
|
77
80
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
78
|
-
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
|
|
79
|
-
"@deepseek-ai/dsh-
|
|
80
|
-
"@deepseek-ai/dsh-tool-web": "0.1.7-rc.2"
|
|
81
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.7-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-storage-domain": ">=0.1.7-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-tool-web": ">=0.1.7-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-web": ">=0.1.7-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-web-fetch-http": ">=0.1.7-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-web-search-deepseek": ">=0.1.7-rc.2"
|
|
87
|
+
},
|
|
88
|
+
"peerDependenciesMeta": {
|
|
89
|
+
"@deepseek-ai/dsh-credentials": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@deepseek-ai/dsh-storage-domain": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@deepseek-ai/dsh-tool-web": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"@deepseek-ai/dsh-web": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"@deepseek-ai/dsh-web-fetch-http": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@deepseek-ai/dsh-web-search-deepseek": {
|
|
105
|
+
"optional": true
|
|
106
|
+
}
|
|
81
107
|
},
|
|
82
108
|
"devDependencies": {
|
|
83
109
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
84
110
|
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
|
|
85
|
-
"@deepseek-ai/dsh-
|
|
111
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.7-rc.2",
|
|
86
112
|
"@deepseek-ai/dsh-tool-web": "0.1.7-rc.2",
|
|
113
|
+
"@deepseek-ai/dsh-web": "0.1.7-rc.2",
|
|
114
|
+
"@deepseek-ai/dsh-web-fetch-http": "0.1.7-rc.2",
|
|
115
|
+
"@deepseek-ai/dsh-web-search-deepseek": "0.1.7-rc.2",
|
|
87
116
|
"react": "^18.2.0",
|
|
88
117
|
"@types/react": "18.3.31"
|
|
89
118
|
},
|
|
@@ -100,23 +129,18 @@
|
|
|
100
129
|
],
|
|
101
130
|
"repository": {
|
|
102
131
|
"type": "git",
|
|
103
|
-
"url": "git+https://github.com/klarkxy/dsh-
|
|
104
|
-
"directory": "
|
|
132
|
+
"url": "git+https://github.com/klarkxy/dsh-plugins.git",
|
|
133
|
+
"directory": "plugins/dsh-web-search-manager"
|
|
105
134
|
},
|
|
106
|
-
"homepage": "https://github.
|
|
135
|
+
"homepage": "https://klarkxy.github.io/dsh-plugins/plugins/web-search-manager/",
|
|
107
136
|
"bugs": {
|
|
108
|
-
"url": "https://github.com/klarkxy/dsh-
|
|
137
|
+
"url": "https://github.com/klarkxy/dsh-plugins/issues"
|
|
109
138
|
},
|
|
110
139
|
"publishConfig": {
|
|
111
140
|
"access": "public",
|
|
112
141
|
"registry": "https://registry.npmjs.org/"
|
|
113
142
|
},
|
|
114
143
|
"dshRelease": {
|
|
115
|
-
"contentHash": "
|
|
116
|
-
},
|
|
117
|
-
"scripts": {
|
|
118
|
-
"build": "tsdown && node ../../scripts/wrap-client.mjs @klarkxy/dsh-web-search-manager",
|
|
119
|
-
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
120
|
-
"test": "vitest run --root ../.. packages/dsh-web-search-manager/src"
|
|
144
|
+
"contentHash": "b7f8dbf516ec9c16ffe95c43858689ad9e4925cd040c97a876400edd0ceb8209"
|
|
121
145
|
}
|
|
122
|
-
}
|
|
146
|
+
}
|