@klarkxy/dsh-web-search-manager 0.1.8 → 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 CHANGED
@@ -39,7 +39,11 @@ Built-in search backends:
39
39
  - **Firecrawl**: a web search and extraction API; requires `DSH_EDITOR_WEB_FIRECRAWL_API_KEY`.
40
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
41
 
42
- Enabling search also enables public-page fetching through the built-in HTTP fetch provider, which reads pages directly from your machine: no key and no search charges. Request limits cap each query's results (`maxResults`), queries per tool call (`maxQueries`), request timeout (`timeoutMs`), and fetched page characters (`maxFetchChars`). A connection test sends a fixed query, not manuscript content, and may incur a provider charge.
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.
43
47
 
44
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.
45
49
 
@@ -39,7 +39,11 @@ dsh plugin --profile web add @klarkxy/dsh-web-search-manager
39
39
  - **Firecrawl**:网页搜索与提取 API,需要 `DSH_EDITOR_WEB_FIRECRAWL_API_KEY`。
40
40
  - **Tavily**:Search API,固定 basic 检索深度,不自动升级;需要 `DSH_EDITOR_WEB_TAVILY_API_KEY`。适配器已并入本插件,原独立的 `dsh-web-search-tavily` 包已退役。
41
41
 
42
- 开启搜索也会启用公开网页读取,由内置 HTTP 读取后端直接从本机读取页面,无需密钥、不产生搜索费用。请求限制包括每条查询的结果上限(`maxResults`)、每次工具调用的查询上限(`maxQueries`)、请求超时(`timeoutMs`)和网页正文字符上限(`maxFetchChars`)。连接测试发送固定查询,不发送作品内容,可能产生一次调用费用。
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 字节上限约束。连接测试发送固定查询,不发送作品内容,可能产生一次调用费用。
43
47
 
44
48
  加入 tools 入口后,模型获得 `@deepseek-ai/dsh-tool-web` 的官方 `web_search` 与 `web_fetch` 工具,仅在搜索与网页读取开启时挂载。
45
49
 
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: "HTTP 读取",
621
+ label: "网页读取",
535
622
  billing: "none",
536
- description: "本机直连公开网页,无需 Key,不产生搜索费用。"
537
- }, (options) => new HttpFetchProvider({
538
- maxResponseBytes: 5e6,
539
- maxBodyChars: options.maxFetchChars,
540
- timeoutMs: options.timeoutMs,
541
- maxRedirects: 5,
542
- userAgent: "dsh-editor/managed-web"
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(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&nbsp;/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(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&nbsp;/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,6 +1,6 @@
1
1
  {
2
2
  "name": "@klarkxy/dsh-web-search-manager",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
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",
@@ -141,6 +141,6 @@
141
141
  "registry": "https://registry.npmjs.org/"
142
142
  },
143
143
  "dshRelease": {
144
- "contentHash": "bf0d3a6c045677e009bf8cec234ef966392afc94b90a5b2e538426b7f1cc5d06"
144
+ "contentHash": "b7f8dbf516ec9c16ffe95c43858689ad9e4925cd040c97a876400edd0ceb8209"
145
145
  }
146
146
  }