@klarkxy/dsh-web-search-manager 0.1.6 → 0.1.7
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 +2 -4
- package/docs/README.zh-CN.md +2 -4
- package/lib/client.inner.cjs +6 -10
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +6 -10
- package/lib/contracts.d.ts +7 -1
- package/lib/contracts.js.map +1 -1
- package/lib/index.js +9 -5
- package/lib/index.js.map +1 -1
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Configure DSH web search providers and page fetching from one settings page.
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
Requires Node.js ≥22 and DSH `0.1.7-
|
|
9
|
+
Requires Node.js ≥22 and DSH `0.1.7-rc.2`. No repository build is needed.
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
12
|
dsh plugin --profile web add @klarkxy/dsh-web-search-manager
|
|
@@ -26,8 +26,6 @@ DuckDuckGo is enabled by default and requires no key. Enable another provider an
|
|
|
26
26
|
|
|
27
27
|
The first ready provider by priority is used. Missing credentials are skipped; failed requests do not automatically switch providers. Enabling search also enables public-page fetching. A connection test sends a fixed query, not manuscript content, and may incur a provider charge.
|
|
28
28
|
|
|
29
|
-
With [@klarkxy/dsh-zhihu](https://www.npmjs.com/package/@klarkxy/dsh-zhihu) installed, **Zhihu global search** shares the Access Secret from Zhihu settings.
|
|
30
|
-
|
|
31
29
|
Only configure trusted HTTPS endpoints: API keys are sent to them. Credentials use DSH storage; protection at rest depends on its configured backend. Usage counters are call attempts, not billing statements or a spending cap.
|
|
32
30
|
|
|
33
31
|
Disabling a managed provider cancels its requests, but does not sandbox HTTP calls made directly by other plugins. Search results are external material and cannot authorize changes to manuscripts.
|
|
@@ -36,7 +34,7 @@ If deployment environment overrides conflict with the settings selection, remove
|
|
|
36
34
|
|
|
37
35
|
## Extend or develop
|
|
38
36
|
|
|
39
|
-
Use DSH's existing `ctx.web` service. For a provider extension, see [manager contracts](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/contracts.ts), [built-in registration](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/builtins.ts), and the [Tavily adapter](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/tavily.ts). Forward cancellation signals; `available()` must check local state without a network request.
|
|
37
|
+
Use DSH's existing `ctx.web` service. For a provider extension, see [manager contracts](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/contracts.ts), [built-in registration](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/builtins.ts), and the [Tavily adapter](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/tavily.ts). Forward cancellation signals; `available()` must check local state without a network request. Provider extensions can supply `pricing`, an HTTPS `pricingUrl`, and `credentialHint` for a shared credential in their registration metadata.
|
|
40
38
|
|
|
41
39
|
From the repository root:
|
|
42
40
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
|
-
需要 Node.js ≥22、DSH `0.1.7-
|
|
9
|
+
需要 Node.js ≥22、DSH `0.1.7-rc.2`,无需构建本仓库。
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
12
|
dsh plugin --profile web add @klarkxy/dsh-web-search-manager
|
|
@@ -26,8 +26,6 @@ DSH Editor 已内置此插件、模型工具及 Tavily 适配器。
|
|
|
26
26
|
|
|
27
27
|
按优先级使用第一个就绪后端,缺密钥的跳过,失败不自动换家。开启搜索也会启用公开网页读取。连接测试发送固定查询,不发送作品内容,可能产生一次调用费用。
|
|
28
28
|
|
|
29
|
-
安装 [@klarkxy/dsh-zhihu](https://www.npmjs.com/package/@klarkxy/dsh-zhihu) 后,「知乎全网搜索」共用知乎设置中的 Access Secret。
|
|
30
|
-
|
|
31
29
|
只配置可信 HTTPS 端点,密钥会发送到该地址。凭据由 DSH 保存,落盘保护取决于其后端配置。用量计数是调用尝试,不等于账单或消费上限。
|
|
32
30
|
|
|
33
31
|
关闭受管后端会取消其请求,但不能阻止其它插件自行联网。搜索结果是外部资料,不授权修改稿件。
|
|
@@ -36,7 +34,7 @@ DSH Editor 已内置此插件、模型工具及 Tavily 适配器。
|
|
|
36
34
|
|
|
37
35
|
## 扩展与开发
|
|
38
36
|
|
|
39
|
-
复用 DSH 的 `ctx.web`。扩展供应商可参照[管理接口](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/contracts.ts)、[内置注册](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/builtins.ts)和 [Tavily 适配器](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/tavily.ts)。须传递取消信号;`available()`
|
|
37
|
+
复用 DSH 的 `ctx.web`。扩展供应商可参照[管理接口](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/contracts.ts)、[内置注册](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/builtins.ts)和 [Tavily 适配器](https://github.com/klarkxy/dsh-editor/blob/main/packages/dsh-web-search-manager/src/tavily.ts)。须传递取消信号;`available()` 只检查本地状态,不发送网络请求。扩展后端可在注册描述中提供 `pricing`、HTTPS `pricingUrl`;共用凭据时可提供 `credentialHint`,由后端维护自己的设置页文案。
|
|
40
38
|
|
|
41
39
|
在仓库根运行:
|
|
42
40
|
|
package/lib/client.inner.cjs
CHANGED
|
@@ -9,11 +9,6 @@ const providerInfo = {
|
|
|
9
9
|
pricing: "免费使用,不产生搜索 API 费用。",
|
|
10
10
|
pricingUrl: "https://duckduckgo.com/"
|
|
11
11
|
},
|
|
12
|
-
"zhihu-global": {
|
|
13
|
-
description: "融合知乎问答与全网内容,返回可溯源的搜索结果。",
|
|
14
|
-
pricing: "注册可获 5,000 次/天试用额度;超额价格需向平台咨询。",
|
|
15
|
-
pricingUrl: "https://developer.zhihu.com/"
|
|
16
|
-
},
|
|
17
12
|
bocha: {
|
|
18
13
|
description: "面向中文内容的网页搜索,返回标题、链接和摘要。",
|
|
19
14
|
pricing: "按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。",
|
|
@@ -51,10 +46,11 @@ const providerInfo = {
|
|
|
51
46
|
}
|
|
52
47
|
};
|
|
53
48
|
function searchProviderInfo(provider) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
const known = Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : void 0;
|
|
50
|
+
return {
|
|
51
|
+
description: known?.description ?? provider.description,
|
|
52
|
+
pricing: provider.pricing ?? known?.pricing ?? (provider.billing === "none" ? "此后端不收取搜索费用。" : provider.billing === "model-and-tools" ? "按模型用量和工具调用计费;免费额度及单价请查看供应商说明。" : "按 API 调用计费;免费额度及单价请查看供应商说明。"),
|
|
53
|
+
pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl
|
|
58
54
|
};
|
|
59
55
|
}
|
|
60
56
|
//#endregion
|
|
@@ -523,7 +519,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
523
519
|
className: "web-search-meta",
|
|
524
520
|
children: needsKey ? "待配置 Key" : "暂不可用"
|
|
525
521
|
}),
|
|
526
|
-
shared && participating && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: provider.
|
|
522
|
+
shared && participating && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: provider.credentialHint ?? "与其他设置共用凭据" }),
|
|
527
523
|
showKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: ["API Key", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
528
524
|
type: "password",
|
|
529
525
|
autoComplete: "off",
|
package/lib/client.inner.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.inner.cjs","names":["useState","useRef","useId"],"sources":["../src/provider-info.ts","../src/contracts.ts","../src/client.tsx"],"sourcesContent":["import type { ProviderView } from './contracts.ts'\n\n// Public prices checked on 2026-09-21; links remain available for current account terms.\nexport const PROVIDER_PRICING_CHECKED = '2026-09-21'\n\ninterface ProviderInfo {\n description: string\n pricing: string\n pricingUrl?: string\n}\n\nconst providerInfo: Record<string, ProviderInfo> = {\n ddg: {\n description: '搜索 DuckDuckGo 公开网页,无需注册或 API Key。',\n pricing: '免费使用,不产生搜索 API 费用。',\n pricingUrl: 'https://duckduckgo.com/',\n },\n 'zhihu-global': {\n description: '融合知乎问答与全网内容,返回可溯源的搜索结果。',\n pricing: '注册可获 5,000 次/天试用额度;超额价格需向平台咨询。',\n pricingUrl: 'https://developer.zhihu.com/',\n },\n bocha: {\n description: '面向中文内容的网页搜索,返回标题、链接和摘要。',\n pricing: '按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。',\n pricingUrl: 'https://aq6ky2b8nql.feishu.cn/wiki/JYSbwzdPIiFnz4kDYPXcHSDrnZb',\n },\n brave: {\n description: '使用 Brave 独立网页索引,搜索网页、新闻等内容。',\n pricing: '每月赠 $5(约 1,000 次);超出 $5/千次,激活需绑卡。',\n pricingUrl: 'https://brave.com/search/api/',\n },\n 'deepseek-official': {\n description: '由 DeepSeek 模型调用内置搜索,复用模型 API Key。',\n pricing: '默认 Flash 按 tokens 计费:每百万输入 ¥1–2(缓存命中 ¥0.02–0.04)、输出 ¥4–8,随峰谷时段变化;赠送余额和搜索工具费用以平台为准。',\n pricingUrl: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/',\n },\n exa: {\n description: '面向 AI 的网页搜索,提供相关网页与内容片段。',\n pricing: '注册赠 $20,此后每月赠 $10;$7/千次(每次最多 10 条),超过 10 条每多一条加 $1/千次。',\n pricingUrl: 'https://exa.ai/pricing',\n },\n firecrawl: {\n description: '搜索网页,并可配合网页内容提取。',\n pricing: '每月免费 1,000 积分,搜索每 10 条结果扣 2 积分;Hobby 含 5,000 积分/月,年付折合 $16/月,额外 $5/千积分。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n serper: {\n description: '获取 Google 的网页搜索结果。',\n pricing: '注册赠 2,500 次(一次性);充值 $50/5万次($1/千次),付费额度有效期 6 个月。',\n pricingUrl: 'https://serper.dev/',\n },\n tavily: {\n description: '面向 AI 的网页搜索,当前使用基础搜索。',\n pricing: '每月免费 1,000 积分,基础搜索 1 积分/次;按量付费 $0.008/积分($8/千次)。',\n pricingUrl: 'https://docs.tavily.com/documentation/api-credits',\n },\n}\n\nexport function searchProviderInfo(provider: ProviderView): ProviderInfo {\n return (Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : undefined) ?? {\n description: provider.description,\n pricing: provider.billing === 'none' ? '此后端不收取搜索费用。'\n : provider.billing === 'model-and-tools' ? '按模型用量和工具调用计费;免费额度及单价请查看供应商说明。'\n : '按 API 调用计费;免费额度及单价请查看供应商说明。',\n pricingUrl: provider.signupUrl,\n }\n}\n","import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Execution and source vocabulary belong to ctx.web. */\nexport interface ProviderDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface (e.g. the chat model Key). The page must not delete it. */\n readonly credentialShared?: boolean\n readonly billing: 'request' | 'model-and-tools' | 'none'\n /** Public HTTPS page where the user can create an API key. */\n readonly signupUrl?: string\n}\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { useEffect, useId, useRef, useState } from 'react'\nimport { PROVIDER_PRICING_CHECKED, searchProviderInfo } from './provider-info.ts'\nimport {\n WEB_SEARCH_RPC_CHANNEL, pickActiveSearch, resolveSearchOrder,\n type ProviderView, type RpcResult, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport const name = 'dsh-web-search-manager-client'\nexport const inject = ['slots', 'connection', 'remote', 'remote.credentials'] as const\n\ninterface Credentials {\n describe(refs: string[]): Promise<RpcResult<Record<string, { configured: boolean; writable: boolean; source?: string }>>>\n set(ref: string, value: string): Promise<RpcResult<unknown>>\n unset(ref: string): Promise<RpcResult<unknown>>\n}\ninterface Client {\n connection: { rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> } }\n remote: { credentials: Credentials }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; id: string; label: string; order: number }, render: unknown): () => void\n }\n}\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\nfunction editable(settings: WebSettings): Omit<WebSettings, 'revision'> {\n const { revision: _revision, ...rest } = settings\n return rest\n}\n\nexport function isProviderOn(status: WebStatus, provider: ProviderView): boolean {\n const enabled = provider.kind === 'search' ? status.searchActive : status.fetchActive\n const selected = status.settings[provider.kind === 'search' ? 'searchProvider' : 'fetchProvider']\n return enabled && selected === provider.id\n}\n\nexport function searchBackends(status: WebStatus): ProviderView[] {\n const all = status.providers.filter(provider => provider.kind === 'search')\n const order = resolveSearchOrder(\n all.map(provider => provider.id),\n status.settings,\n id => Boolean(all.find(provider => provider.id === id)?.credentialRef),\n )\n return order.flatMap(id => all.find(provider => provider.id === id) ?? [])\n}\n\nexport function catalogSearchBackends(status: WebStatus): ProviderView[] {\n const enabled = searchBackends(status)\n const rest = status.providers.filter(provider => provider.kind === 'search' && !enabled.some(row => row.id === provider.id))\n return [...enabled, ...rest]\n}\n\nexport function selectedSearchBackend(status: WebStatus): ProviderView | undefined {\n const backends = searchBackends(status)\n const id = pickActiveSearch(backends.map(provider => provider.id), id => Boolean(backends.find(provider => provider.id === id)?.configured))\n return backends.find(provider => provider.id === id)\n}\n\nfunction ExternalLink({ url, label, children }: { url: string; label: string; children: string }) {\n return <a href={url} target=\"_blank\" rel=\"noreferrer noopener\" aria-label={label}\n onClick={event => {\n const bridge = (globalThis as { dshWindow?: { openExternal?(url: string): void } }).dshWindow\n if (bridge?.openExternal) {\n event.preventDefault()\n bridge.openExternal(url)\n }\n }}>{children}</a>\n}\n\nexport function canEnableSearch(\n order: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return order.some(id => {\n const provider = providers.find(item => item.kind === 'search' && item.id === id)\n if (!provider) return false\n if (provider.configured) return true\n const typed = (keys[provider.id] ?? '').trim()\n return Boolean(provider.credentialRef && typed && writable[provider.credentialRef] !== false)\n })\n}\n\nexport function nextSearchEnabled(\n currentlyEnabled: boolean,\n nextOrder: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable)\n}\n\nfunction ConfirmButton(props: { label: string; confirmLabel: string; disabled?: boolean; onConfirm(): void }) {\n const [armed, setArmed] = useState(false)\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n useEffect(() => () => clearTimeout(timer.current), [])\n function disarm() {\n clearTimeout(timer.current)\n setArmed(false)\n }\n return <button type=\"button\" className=\"web-search-danger\" disabled={props.disabled}\n onClick={() => {\n if (!armed) {\n setArmed(true)\n timer.current = setTimeout(() => setArmed(false), 3000)\n return\n }\n disarm()\n props.onConfirm()\n }}\n onBlur={disarm}>{armed ? props.confirmLabel : props.label}</button>\n}\n\nexport function NetworkSearchSettings({ client }: { client: Client }) {\n const tabsId = useId()\n const [tab, setTab] = useState<'providers' | 'limits'>('providers')\n const [status, setStatus] = useState<WebStatus>()\n const [draft, setDraft] = useState<WebSettings>()\n const [keys, setKeys] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [dragSource, setDragSource] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n const [sortAnnouncement, setSortAnnouncement] = useState('')\n const [note, setNote] = useState('')\n const [error, setError] = useState('')\n const [writable, setWritable] = useState<Record<string, boolean>>({})\n\n async function call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload) as RpcResult<T>)\n }\n async function load() {\n const next = await call<WebStatus>('status')\n setStatus(next); setDraft(next.settings)\n const refs = next.providers.flatMap(provider => provider.credentialRef ? [provider.credentialRef] : [])\n if (refs.length) {\n const credentials = unwrap(await client.remote.credentials.describe(refs))\n setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])))\n }\n return next\n }\n useEffect(() => { void load().catch(() => setError('无法读取网络搜索设置,请检查 Host 连接后重试。')) }, [client])\n\n async function action(run: () => Promise<void>) {\n setBusy(true); setNote(''); setError('')\n try { await run() }\n catch (cause) { setError(cause instanceof Error ? cause.message : '操作失败。'); await load().catch(() => {}) }\n finally { setBusy(false) }\n }\n async function update(settings: Omit<WebSettings, 'revision'>, expectedRevision: number) {\n const next = await call<WebStatus>('update', { settings, expectedRevision })\n setStatus(next); setDraft(next.settings)\n return next\n }\n\n async function persist(on: boolean, order: string[]) {\n if (!status || !draft) return\n for (const provider of status.providers) {\n if (provider.kind !== 'search') continue\n const snapshot = keys[provider.id] ?? ''\n const typed = snapshot.trim()\n if (!provider.credentialRef || !typed) continue\n if (writable[provider.credentialRef] === false) throw new Error('当前凭据为只读,无法写入。')\n unwrap(await client.remote.credentials.set(provider.credentialRef, typed))\n setKeys(current => current[provider.id] === snapshot ? { ...current, [provider.id]: '' } : current)\n }\n const bounded = (key: (typeof limits)[number][0]) => {\n const row = limits.find(item => item[0] === key)!\n return Math.min(row[3], Math.max(row[2], Number.isFinite(draft[key]) ? draft[key] : row[2]))\n }\n await update({\n ...editable(status.settings),\n maxResults: bounded('maxResults'), maxQueries: bounded('maxQueries'),\n timeoutMs: bounded('timeoutMs'), maxFetchChars: bounded('maxFetchChars'),\n endpoints: draft.endpoints, searchOrder: order,\n searchEnabled: on, fetchProvider: 'http', fetchEnabled: on,\n }, status.settings.revision)\n await load()\n }\n\n async function removeKey(provider: ProviderView) {\n if (!provider.credentialRef || provider.credentialShared || !status) return\n unwrap(await client.remote.credentials.unset(provider.credentialRef))\n setKeys(current => ({ ...current, [provider.id]: '' }))\n await load()\n setNote('已删除 Key。')\n }\n\n if (!status || !draft) {\n return <section className=\"web-search-settings\">\n <p role=\"status\">{error || '正在读取设置…'}</p>\n {error ? <button type=\"button\" onClick={() => void action(async () => { await load() })}>重新连接</button> : null}\n </section>\n }\n\n const enabled = searchBackends(status)\n const backends = catalogSearchBackends(status)\n const selected = selectedSearchBackend(status)\n const on = status.settings.searchEnabled\n const order = enabled.map(provider => provider.id)\n const canEnable = canEnableSearch(order, status.providers, keys, writable)\n const limits = [\n ['maxResults', '每条查询的结果上限', 1, 20],\n ['maxQueries', '每次工具调用的查询上限', 1, 5],\n ['timeoutMs', '请求超时(毫秒)', 1000, 120000],\n ['maxFetchChars', '网页正文字符上限', 1000, 200000],\n ] as const\n\n function move(id: string, nextIndex: number) {\n const index = order.indexOf(id)\n if (busy || index < 0 || nextIndex < 0 || nextIndex >= order.length || index === nextIndex) return\n const next = [...order]\n const [item] = next.splice(index, 1)\n next.splice(nextIndex, 0, item)\n void action(async () => {\n await persist(on, next)\n setSortAnnouncement((backends.find(provider => provider.id === id)?.label ?? id) + ' 已移至第 ' + (nextIndex + 1) + ' 位')\n })\n }\n\n function endDrag() {\n setDragSource(null)\n setDropTarget(null)\n }\n\n return <section className=\"web-search-settings\" data-testid=\"web-search-settings\">\n <div className=\"web-search-tabs\" role=\"tablist\" aria-label=\"网络搜索设置\" onKeyDown={event => {\n if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return\n const buttons = [...event.currentTarget.querySelectorAll<HTMLButtonElement>('[role=\"tab\"]')]\n const index = buttons.indexOf(event.target as HTMLButtonElement)\n if (index < 0) return\n event.preventDefault()\n const next = event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1\n : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length\n buttons[next]?.focus()\n buttons[next]?.click()\n }}>\n {([['providers', '搜索服务'], ['limits', '请求限制']] as const).map(([key, label]) => (\n <button key={key} type=\"button\" role=\"tab\" id={tabsId + '-' + key + '-tab'}\n aria-controls={tabsId + '-' + key + '-panel'} aria-selected={tab === key}\n tabIndex={tab === key ? 0 : -1} onClick={() => setTab(key)}>{label}</button>\n ))}\n </div>\n <span className=\"web-search-sr-only\" role=\"status\" aria-live=\"polite\">{sortAnnouncement}</span>\n {error && <p role=\"alert\" className=\"web-search-error\">{error}</p>}\n {note && <p role=\"status\">{note}</p>}\n {status.storageFailed && <p role=\"alert\">配置保存异常,当前运行已暂停网络访问。请重新保存。</p>}\n <div role=\"tabpanel\" id={tabsId + '-providers-panel'} aria-labelledby={tabsId + '-providers-tab'}\n hidden={tab !== 'providers'} tabIndex={0}>\n {backends.length === 0 ? <p className=\"web-search-empty\">还没有可用的搜索后端。</p> : <article\n className=\"web-search-card\"\n data-testid=\"web-search-tool\"\n data-on={on ? 'true' : 'false'}>\n <header>\n <div>\n <h3>联网搜索</h3>\n {!canEnable && <p className=\"web-search-meta\">请选择搜索服务并配置 Key。</p>}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${on ? ' is-on' : ''}`}\n aria-checked={on}\n aria-label={on ? '关闭联网搜索' : '启用联网搜索'}\n disabled={busy || (!on && !canEnable)}\n onClick={() => void action(() => persist(!on, order))}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </header>\n <ol className=\"web-search-rank\" aria-label=\"搜索后端\">\n {backends.map(provider => {\n const participating = order.includes(provider.id)\n const rank = order.indexOf(provider.id)\n const active = status.searchActive && selected?.id === provider.id\n const info = searchProviderInfo(provider)\n const needsKey = Boolean(provider.credentialRef)\n const shared = Boolean(provider.credentialShared)\n const showKey = needsKey && participating && (!shared || !provider.configured)\n const canSort = participating && order.length > 1\n return <li key={provider.id} className={active ? 'is-active' : undefined} data-testid={`web-search-rank-${provider.id}`}\n data-dragging={dragSource === provider.id || undefined}\n data-drop={dropTarget === provider.id && dragSource ? (order.indexOf(dragSource) < rank ? 'after' : 'before') : undefined}\n onDragOver={event => {\n if (busy || !canSort || !dragSource || dragSource === provider.id) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(provider.id)\n }}\n onDragLeave={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropTarget(null)\n }}\n onDrop={event => {\n if (busy || !canSort || !dragSource) return\n event.preventDefault()\n move(dragSource, rank)\n endDrag()\n }}>\n {canSort ? <button type=\"button\" className=\"web-search-drag-handle\"\n draggable={!busy} aria-disabled={busy}\n aria-label={'拖动排序 ' + provider.label}\n title=\"拖动排序,或用上下方向键调整\"\n onDragStart={event => {\n if (busy) { event.preventDefault(); return }\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('application/x-dsh-search-provider', provider.id)\n const row = event.currentTarget.closest('li')\n if (row) event.dataTransfer.setDragImage(row, 16, 16)\n setDragSource(provider.id)\n setSortAnnouncement('')\n }}\n onDragEnd={endDrag}\n onKeyDown={event => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(provider.id, rank + (event.key === 'ArrowUp' ? -1 : 1))\n }}>\n <svg width=\"16\" height=\"20\" viewBox=\"0 0 16 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"5\" cy=\"5\" r=\"1.5\" /><circle cx=\"11\" cy=\"5\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"10\" r=\"1.5\" /><circle cx=\"11\" cy=\"10\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"15\" r=\"1.5\" /><circle cx=\"11\" cy=\"15\" r=\"1.5\" />\n </svg>\n </button> : <span className=\"web-search-rank-index\">{participating ? rank + 1 : '—'}</span>}\n <div className=\"web-search-provider\">\n <div className=\"web-search-provider-heading\">\n <strong>{provider.label}</strong>\n {active && <span className=\"web-search-meta\">当前使用</span>}\n {provider.signupUrl && <ExternalLink url={provider.signupUrl} label={provider.label + ' 去注册'}>去注册</ExternalLink>}\n </div>\n <div className=\"web-search-provider-info\">\n <p>{info.description}</p>\n <p>{info.pricing}{info.pricingUrl && <> <ExternalLink url={info.pricingUrl}\n label={provider.label + ' 费用说明'}>{provider.id === 'ddg' ? '官网' : '费用说明'}</ExternalLink></>}</p>\n </div>\n {participating && !provider.configured && <p className=\"web-search-meta\">{needsKey ? '待配置 Key' : '暂不可用'}</p>}\n {shared && participating && <small>{provider.id === 'zhihu-global' ? '与「知乎资料」共用 Access Secret' : '与模型设置共用 Key'}</small>}\n {showKey ? <label>API Key\n <input type=\"password\" autoComplete=\"off\" spellCheck={false}\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n value={keys[provider.id] ?? ''}\n placeholder={provider.configured ? '已配置,留空不替换' : '请输入 API Key'}\n onChange={event => setKeys(current => ({ ...current, [provider.id]: event.target.value }))} />\n </label> : null}\n {needsKey && !shared && provider.configured ? <ConfirmButton\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n label=\"删除 Key\" confirmLabel=\"确认删除 Key?\"\n onConfirm={() => void action(() => removeKey(provider))} /> : null}\n </div>\n <div className=\"web-search-rank-move\">\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${participating ? ' is-on' : ''}`}\n aria-checked={participating}\n aria-label={`${participating ? '关闭' : '开启'} ${provider.label}`}\n disabled={busy}\n onClick={() => {\n const next = participating ? order.filter(id => id !== provider.id) : [...order, provider.id]\n void action(() => persist(nextSearchEnabled(status.settings.searchEnabled, next, status.providers, keys, writable), next))\n }}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </div>\n </li>\n })}\n </ol>\n <p className=\"web-search-meta\">价格核对于 {PROVIDER_PRICING_CHECKED},实际额度与费用以供应商账户为准。</p>\n {on ? <div className=\"web-search-card-actions\">\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n const result = await call<{ sources: number }>('test')\n await load()\n setNote(`连接正常,返回 ${result.sources} 条来源。`)\n })}>测试连接(可能计费)</button>\n </div> : null}\n </article>}\n </div>\n <div role=\"tabpanel\" id={tabsId + '-limits-panel'} aria-labelledby={tabsId + '-limits-tab'}\n hidden={tab !== 'limits'} tabIndex={0}>\n <fieldset className=\"web-search-limits\" disabled={busy} aria-label=\"请求限制\">\n {limits.map(([key, label, min, max]) => (\n <label key={key}>{label}\n <input type=\"number\" min={min} max={max} step={1} value={draft[key]}\n onChange={event => setDraft({ ...draft, [key]: Number(event.target.value) })} />\n </label>\n ))}\n </fieldset>\n </div>\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n setNote('已保存。')\n })}>保存</button>\n </section>\n}\n\nconst styles = `\n.web-search-settings{max-width:760px;display:grid;gap:24px;color:inherit;font:400 var(--font-size-2,14px)/1.5 var(--default-font-family,system-ui,sans-serif)}\n.web-search-settings p{margin:0;line-height:1.5}\n.web-search-settings small,.web-search-meta,.web-search-empty{font-size:var(--font-size-1,13px);color:var(--gray-11,inherit)}\n.web-search-error{color:var(--red-11,#b42318)}\n.web-search-card{display:grid;gap:12px;padding:16px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent));border-radius:12px}\n.web-search-card header{display:flex;justify-content:space-between;gap:12px;align-items:center;padding-bottom:8px}\n.web-search-card h3{margin:0;font-size:var(--font-size-3,16px);font-weight:600}\n.web-search-provider{display:grid;gap:8px;min-width:0}\n.web-search-provider-info{display:grid;gap:3px;font-size:var(--font-size-1,13px);color:var(--gray-11,inherit);overflow-wrap:anywhere}\n.web-search-provider-heading{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;min-height:24px}\n.web-search-provider-heading strong{font-weight:500}\n.web-search-card label{display:grid;gap:6px}\n.web-search-settings input{box-sizing:border-box;width:100%;min-width:0;padding:8px 10px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 22%,transparent));border-radius:8px;background:var(--color-surface,transparent);color:inherit;font:inherit;transition:border-color 150ms ease,box-shadow 150ms ease}\n.web-search-settings input:focus{border-color:var(--accent-9,#3b82f6);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent-9,#3b82f6) 25%,transparent)}\n.web-search-settings button:not([role=\"switch\"]){min-height:34px;padding:6px 12px;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;background:transparent;color:inherit;cursor:pointer;font:inherit;justify-self:start;transition:background-color 150ms ease,color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,transform 150ms ease}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):hover:not(:disabled){background:var(--gray-3,color-mix(in srgb,currentColor 6%,transparent));border-color:color-mix(in srgb,currentColor 35%,transparent)}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):active:not(:disabled){transform:scale(.97)}\n.web-search-danger:hover:not(:disabled){border-color:var(--red-11,#b42318);color:var(--red-11,#b42318);background:color-mix(in srgb,var(--red-11,#b42318) 8%,transparent)}\n.web-search-settings button:disabled{opacity:.45;cursor:not-allowed}\n.web-search-tabs{display:flex;gap:20px;border-bottom:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-tabs button[role=\"tab\"]{padding:8px 0;border:0;border-bottom:2px solid transparent;border-radius:0;color:var(--gray-11,inherit)}\n.web-search-tabs button[role=\"tab\"]:hover{color:inherit}\n.web-search-tabs button[aria-selected=\"true\"]{border-bottom-color:var(--accent-9,#3b82f6);color:var(--accent-11,inherit);font-weight:600}\n.web-search-settings [role=\"tabpanel\"]{border-radius:12px}\n.web-search-settings .web-search-switch{all:unset;box-sizing:border-box;position:relative;display:inline-block;width:36px;height:20px;flex:none;border-radius:999px;background:var(--gray-7,color-mix(in srgb,currentColor 28%,transparent));cursor:pointer}\n.web-search-switch.is-on{background:var(--accent-9,#3b82f6)}\n.web-search-switch:disabled{cursor:not-allowed;opacity:.7}\n.web-search-switch-thumb{position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:999px;background:#fff;transition:transform 150ms ease}\n.web-search-switch.is-on .web-search-switch-thumb{transform:translateX(16px)}\n.web-search-settings :focus-visible{outline:2px solid var(--accent-9,currentColor);outline-offset:3px}\n.web-search-card-actions{display:flex;flex-wrap:wrap;gap:8px}\n.web-search-rank{margin:0;padding:0;list-style:none}\n.web-search-rank li{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto;gap:10px;align-items:start;padding:14px 0;border-top:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-settings button.web-search-drag-handle{display:grid;place-items:center;width:24px;height:24px;min-height:24px;padding:0;border:0;cursor:grab;color:inherit;opacity:.65}\n.web-search-settings button.web-search-drag-handle:hover{opacity:1;background:var(--gray-3,color-mix(in srgb,currentColor 8%,transparent))}\n.web-search-settings button.web-search-drag-handle:active{cursor:grabbing}\n.web-search-settings button.web-search-drag-handle[aria-disabled=\"true\"]{cursor:wait;opacity:.4}\n.web-search-rank li[data-dragging]{opacity:.45}\n.web-search-rank li[data-drop]::after{content:'';position:absolute;left:0;right:0;height:2px;background:var(--accent-9,#3b82f6);pointer-events:none}\n.web-search-rank li[data-drop=\"before\"]::after{top:-1px}\n.web-search-rank li[data-drop=\"after\"]::after{bottom:-1px}\n.web-search-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n.web-search-rank-index{opacity:.6;font-variant-numeric:tabular-nums;line-height:24px}\n.web-search-rank-move{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-end;padding-top:2px}\n.web-search-settings a{color:var(--accent-11,currentColor);font-size:var(--font-size-1,13px);text-underline-offset:3px}\n.web-search-limits{margin:0;border:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 20px}\n.web-search-limits label{display:grid;gap:6px;font-size:var(--font-size-2,14px)}\n@media(max-width:560px){.web-search-rank-move{flex-direction:column;align-items:flex-end}.web-search-limits{grid-template-columns:minmax(0,1fr)}}\n@media(prefers-reduced-motion:reduce){.web-search-switch-thumb{transition:none}.web-search-settings button:not([role=\"switch\"]),.web-search-settings input{transition:none}}\n`\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-web-search-manager'); style.textContent = styles\n style.dataset.dshWebSearch = ''; document.head.appendChild(style)\n return () => style.remove()\n }, 'web-search-manager.styles')\n ctx.effect(() => client.slots.inject('settings.section', () => client.slots.register({\n name: 'settings.section', id: 'web-search', order: 70, label: '网络搜索',\n }, () => <NetworkSearchSettings client={client} />)), 'web-search-manager.settings')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.inner.cjs","names":["useState","useRef","useId"],"sources":["../src/provider-info.ts","../src/contracts.ts","../src/client.tsx"],"sourcesContent":["import type { ProviderView } from './contracts.ts'\n\n// Public prices checked on 2026-09-21; links remain available for current account terms.\nexport const PROVIDER_PRICING_CHECKED = '2026-09-21'\n\ninterface ProviderInfo {\n description: string\n pricing: string\n pricingUrl?: string\n}\n\nconst providerInfo: Record<string, ProviderInfo> = {\n ddg: {\n description: '搜索 DuckDuckGo 公开网页,无需注册或 API Key。',\n pricing: '免费使用,不产生搜索 API 费用。',\n pricingUrl: 'https://duckduckgo.com/',\n },\n bocha: {\n description: '面向中文内容的网页搜索,返回标题、链接和摘要。',\n pricing: '按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。',\n pricingUrl: 'https://aq6ky2b8nql.feishu.cn/wiki/JYSbwzdPIiFnz4kDYPXcHSDrnZb',\n },\n brave: {\n description: '使用 Brave 独立网页索引,搜索网页、新闻等内容。',\n pricing: '每月赠 $5(约 1,000 次);超出 $5/千次,激活需绑卡。',\n pricingUrl: 'https://brave.com/search/api/',\n },\n 'deepseek-official': {\n description: '由 DeepSeek 模型调用内置搜索,复用模型 API Key。',\n pricing: '默认 Flash 按 tokens 计费:每百万输入 ¥1–2(缓存命中 ¥0.02–0.04)、输出 ¥4–8,随峰谷时段变化;赠送余额和搜索工具费用以平台为准。',\n pricingUrl: 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/',\n },\n exa: {\n description: '面向 AI 的网页搜索,提供相关网页与内容片段。',\n pricing: '注册赠 $20,此后每月赠 $10;$7/千次(每次最多 10 条),超过 10 条每多一条加 $1/千次。',\n pricingUrl: 'https://exa.ai/pricing',\n },\n firecrawl: {\n description: '搜索网页,并可配合网页内容提取。',\n pricing: '每月免费 1,000 积分,搜索每 10 条结果扣 2 积分;Hobby 含 5,000 积分/月,年付折合 $16/月,额外 $5/千积分。',\n pricingUrl: 'https://www.firecrawl.dev/pricing',\n },\n serper: {\n description: '获取 Google 的网页搜索结果。',\n pricing: '注册赠 2,500 次(一次性);充值 $50/5万次($1/千次),付费额度有效期 6 个月。',\n pricingUrl: 'https://serper.dev/',\n },\n tavily: {\n description: '面向 AI 的网页搜索,当前使用基础搜索。',\n pricing: '每月免费 1,000 积分,基础搜索 1 积分/次;按量付费 $0.008/积分($8/千次)。',\n pricingUrl: 'https://docs.tavily.com/documentation/api-credits',\n },\n}\n\nexport function searchProviderInfo(provider: ProviderView): ProviderInfo {\n const known = Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : undefined\n return {\n description: known?.description ?? provider.description,\n pricing: provider.pricing ?? known?.pricing ?? (provider.billing === 'none' ? '此后端不收取搜索费用。'\n : provider.billing === 'model-and-tools' ? '按模型用量和工具调用计费;免费额度及单价请查看供应商说明。'\n : '按 API 调用计费;免费额度及单价请查看供应商说明。'),\n pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl,\n }\n}\n","import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Execution and source vocabulary belong to ctx.web. */\nexport interface ProviderDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface. The page must not delete it. */\n readonly credentialShared?: boolean\n /** Optional notice naming the settings surface that owns a shared credential. */\n readonly credentialHint?: string\n readonly billing: 'request' | 'model-and-tools' | 'none'\n /** Optional provider-supplied billing summary. */\n readonly pricing?: string\n /** Public HTTPS page explaining provider charges. */\n readonly pricingUrl?: string\n /** Public HTTPS page where the user can create an API key. */\n readonly signupUrl?: string\n}\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { useEffect, useId, useRef, useState } from 'react'\nimport { PROVIDER_PRICING_CHECKED, searchProviderInfo } from './provider-info.ts'\nimport {\n WEB_SEARCH_RPC_CHANNEL, pickActiveSearch, resolveSearchOrder,\n type ProviderView, type RpcResult, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport const name = 'dsh-web-search-manager-client'\nexport const inject = ['slots', 'connection', 'remote', 'remote.credentials'] as const\n\ninterface Credentials {\n describe(refs: string[]): Promise<RpcResult<Record<string, { configured: boolean; writable: boolean; source?: string }>>>\n set(ref: string, value: string): Promise<RpcResult<unknown>>\n unset(ref: string): Promise<RpcResult<unknown>>\n}\ninterface Client {\n connection: { rpc: { call(channel: string, endpoint: string, payload: unknown): Promise<unknown> } }\n remote: { credentials: Credentials }\n slots: {\n inject(key: string, callback: () => unknown): () => void\n register(spec: { name: string; id: string; label: string; order: number }, render: unknown): () => void\n }\n}\n\nfunction unwrap<T>(result: RpcResult<T>): T {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\nfunction editable(settings: WebSettings): Omit<WebSettings, 'revision'> {\n const { revision: _revision, ...rest } = settings\n return rest\n}\n\nexport function isProviderOn(status: WebStatus, provider: ProviderView): boolean {\n const enabled = provider.kind === 'search' ? status.searchActive : status.fetchActive\n const selected = status.settings[provider.kind === 'search' ? 'searchProvider' : 'fetchProvider']\n return enabled && selected === provider.id\n}\n\nexport function searchBackends(status: WebStatus): ProviderView[] {\n const all = status.providers.filter(provider => provider.kind === 'search')\n const order = resolveSearchOrder(\n all.map(provider => provider.id),\n status.settings,\n id => Boolean(all.find(provider => provider.id === id)?.credentialRef),\n )\n return order.flatMap(id => all.find(provider => provider.id === id) ?? [])\n}\n\nexport function catalogSearchBackends(status: WebStatus): ProviderView[] {\n const enabled = searchBackends(status)\n const rest = status.providers.filter(provider => provider.kind === 'search' && !enabled.some(row => row.id === provider.id))\n return [...enabled, ...rest]\n}\n\nexport function selectedSearchBackend(status: WebStatus): ProviderView | undefined {\n const backends = searchBackends(status)\n const id = pickActiveSearch(backends.map(provider => provider.id), id => Boolean(backends.find(provider => provider.id === id)?.configured))\n return backends.find(provider => provider.id === id)\n}\n\nfunction ExternalLink({ url, label, children }: { url: string; label: string; children: string }) {\n return <a href={url} target=\"_blank\" rel=\"noreferrer noopener\" aria-label={label}\n onClick={event => {\n const bridge = (globalThis as { dshWindow?: { openExternal?(url: string): void } }).dshWindow\n if (bridge?.openExternal) {\n event.preventDefault()\n bridge.openExternal(url)\n }\n }}>{children}</a>\n}\n\nexport function canEnableSearch(\n order: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return order.some(id => {\n const provider = providers.find(item => item.kind === 'search' && item.id === id)\n if (!provider) return false\n if (provider.configured) return true\n const typed = (keys[provider.id] ?? '').trim()\n return Boolean(provider.credentialRef && typed && writable[provider.credentialRef] !== false)\n })\n}\n\nexport function nextSearchEnabled(\n currentlyEnabled: boolean,\n nextOrder: readonly string[],\n providers: readonly ProviderView[],\n keys: Record<string, string>,\n writable: Record<string, boolean>,\n): boolean {\n return currentlyEnabled && canEnableSearch(nextOrder, providers, keys, writable)\n}\n\nfunction ConfirmButton(props: { label: string; confirmLabel: string; disabled?: boolean; onConfirm(): void }) {\n const [armed, setArmed] = useState(false)\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n useEffect(() => () => clearTimeout(timer.current), [])\n function disarm() {\n clearTimeout(timer.current)\n setArmed(false)\n }\n return <button type=\"button\" className=\"web-search-danger\" disabled={props.disabled}\n onClick={() => {\n if (!armed) {\n setArmed(true)\n timer.current = setTimeout(() => setArmed(false), 3000)\n return\n }\n disarm()\n props.onConfirm()\n }}\n onBlur={disarm}>{armed ? props.confirmLabel : props.label}</button>\n}\n\nexport function NetworkSearchSettings({ client }: { client: Client }) {\n const tabsId = useId()\n const [tab, setTab] = useState<'providers' | 'limits'>('providers')\n const [status, setStatus] = useState<WebStatus>()\n const [draft, setDraft] = useState<WebSettings>()\n const [keys, setKeys] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [dragSource, setDragSource] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<string | null>(null)\n const [sortAnnouncement, setSortAnnouncement] = useState('')\n const [note, setNote] = useState('')\n const [error, setError] = useState('')\n const [writable, setWritable] = useState<Record<string, boolean>>({})\n\n async function call<T>(endpoint: string, payload: unknown = {}): Promise<T> {\n return unwrap(await client.connection.rpc.call(WEB_SEARCH_RPC_CHANNEL, endpoint, payload) as RpcResult<T>)\n }\n async function load() {\n const next = await call<WebStatus>('status')\n setStatus(next); setDraft(next.settings)\n const refs = next.providers.flatMap(provider => provider.credentialRef ? [provider.credentialRef] : [])\n if (refs.length) {\n const credentials = unwrap(await client.remote.credentials.describe(refs))\n setWritable(Object.fromEntries(Object.entries(credentials).map(([ref, value]) => [ref, value.writable])))\n }\n return next\n }\n useEffect(() => { void load().catch(() => setError('无法读取网络搜索设置,请检查 Host 连接后重试。')) }, [client])\n\n async function action(run: () => Promise<void>) {\n setBusy(true); setNote(''); setError('')\n try { await run() }\n catch (cause) { setError(cause instanceof Error ? cause.message : '操作失败。'); await load().catch(() => {}) }\n finally { setBusy(false) }\n }\n async function update(settings: Omit<WebSettings, 'revision'>, expectedRevision: number) {\n const next = await call<WebStatus>('update', { settings, expectedRevision })\n setStatus(next); setDraft(next.settings)\n return next\n }\n\n async function persist(on: boolean, order: string[]) {\n if (!status || !draft) return\n for (const provider of status.providers) {\n if (provider.kind !== 'search') continue\n const snapshot = keys[provider.id] ?? ''\n const typed = snapshot.trim()\n if (!provider.credentialRef || !typed) continue\n if (writable[provider.credentialRef] === false) throw new Error('当前凭据为只读,无法写入。')\n unwrap(await client.remote.credentials.set(provider.credentialRef, typed))\n setKeys(current => current[provider.id] === snapshot ? { ...current, [provider.id]: '' } : current)\n }\n const bounded = (key: (typeof limits)[number][0]) => {\n const row = limits.find(item => item[0] === key)!\n return Math.min(row[3], Math.max(row[2], Number.isFinite(draft[key]) ? draft[key] : row[2]))\n }\n await update({\n ...editable(status.settings),\n maxResults: bounded('maxResults'), maxQueries: bounded('maxQueries'),\n timeoutMs: bounded('timeoutMs'), maxFetchChars: bounded('maxFetchChars'),\n endpoints: draft.endpoints, searchOrder: order,\n searchEnabled: on, fetchProvider: 'http', fetchEnabled: on,\n }, status.settings.revision)\n await load()\n }\n\n async function removeKey(provider: ProviderView) {\n if (!provider.credentialRef || provider.credentialShared || !status) return\n unwrap(await client.remote.credentials.unset(provider.credentialRef))\n setKeys(current => ({ ...current, [provider.id]: '' }))\n await load()\n setNote('已删除 Key。')\n }\n\n if (!status || !draft) {\n return <section className=\"web-search-settings\">\n <p role=\"status\">{error || '正在读取设置…'}</p>\n {error ? <button type=\"button\" onClick={() => void action(async () => { await load() })}>重新连接</button> : null}\n </section>\n }\n\n const enabled = searchBackends(status)\n const backends = catalogSearchBackends(status)\n const selected = selectedSearchBackend(status)\n const on = status.settings.searchEnabled\n const order = enabled.map(provider => provider.id)\n const canEnable = canEnableSearch(order, status.providers, keys, writable)\n const limits = [\n ['maxResults', '每条查询的结果上限', 1, 20],\n ['maxQueries', '每次工具调用的查询上限', 1, 5],\n ['timeoutMs', '请求超时(毫秒)', 1000, 120000],\n ['maxFetchChars', '网页正文字符上限', 1000, 200000],\n ] as const\n\n function move(id: string, nextIndex: number) {\n const index = order.indexOf(id)\n if (busy || index < 0 || nextIndex < 0 || nextIndex >= order.length || index === nextIndex) return\n const next = [...order]\n const [item] = next.splice(index, 1)\n next.splice(nextIndex, 0, item)\n void action(async () => {\n await persist(on, next)\n setSortAnnouncement((backends.find(provider => provider.id === id)?.label ?? id) + ' 已移至第 ' + (nextIndex + 1) + ' 位')\n })\n }\n\n function endDrag() {\n setDragSource(null)\n setDropTarget(null)\n }\n\n return <section className=\"web-search-settings\" data-testid=\"web-search-settings\">\n <div className=\"web-search-tabs\" role=\"tablist\" aria-label=\"网络搜索设置\" onKeyDown={event => {\n if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return\n const buttons = [...event.currentTarget.querySelectorAll<HTMLButtonElement>('[role=\"tab\"]')]\n const index = buttons.indexOf(event.target as HTMLButtonElement)\n if (index < 0) return\n event.preventDefault()\n const next = event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1\n : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length\n buttons[next]?.focus()\n buttons[next]?.click()\n }}>\n {([['providers', '搜索服务'], ['limits', '请求限制']] as const).map(([key, label]) => (\n <button key={key} type=\"button\" role=\"tab\" id={tabsId + '-' + key + '-tab'}\n aria-controls={tabsId + '-' + key + '-panel'} aria-selected={tab === key}\n tabIndex={tab === key ? 0 : -1} onClick={() => setTab(key)}>{label}</button>\n ))}\n </div>\n <span className=\"web-search-sr-only\" role=\"status\" aria-live=\"polite\">{sortAnnouncement}</span>\n {error && <p role=\"alert\" className=\"web-search-error\">{error}</p>}\n {note && <p role=\"status\">{note}</p>}\n {status.storageFailed && <p role=\"alert\">配置保存异常,当前运行已暂停网络访问。请重新保存。</p>}\n <div role=\"tabpanel\" id={tabsId + '-providers-panel'} aria-labelledby={tabsId + '-providers-tab'}\n hidden={tab !== 'providers'} tabIndex={0}>\n {backends.length === 0 ? <p className=\"web-search-empty\">还没有可用的搜索后端。</p> : <article\n className=\"web-search-card\"\n data-testid=\"web-search-tool\"\n data-on={on ? 'true' : 'false'}>\n <header>\n <div>\n <h3>联网搜索</h3>\n {!canEnable && <p className=\"web-search-meta\">请选择搜索服务并配置 Key。</p>}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${on ? ' is-on' : ''}`}\n aria-checked={on}\n aria-label={on ? '关闭联网搜索' : '启用联网搜索'}\n disabled={busy || (!on && !canEnable)}\n onClick={() => void action(() => persist(!on, order))}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </header>\n <ol className=\"web-search-rank\" aria-label=\"搜索后端\">\n {backends.map(provider => {\n const participating = order.includes(provider.id)\n const rank = order.indexOf(provider.id)\n const active = status.searchActive && selected?.id === provider.id\n const info = searchProviderInfo(provider)\n const needsKey = Boolean(provider.credentialRef)\n const shared = Boolean(provider.credentialShared)\n const showKey = needsKey && participating && (!shared || !provider.configured)\n const canSort = participating && order.length > 1\n return <li key={provider.id} className={active ? 'is-active' : undefined} data-testid={`web-search-rank-${provider.id}`}\n data-dragging={dragSource === provider.id || undefined}\n data-drop={dropTarget === provider.id && dragSource ? (order.indexOf(dragSource) < rank ? 'after' : 'before') : undefined}\n onDragOver={event => {\n if (busy || !canSort || !dragSource || dragSource === provider.id) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n setDropTarget(provider.id)\n }}\n onDragLeave={event => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDropTarget(null)\n }}\n onDrop={event => {\n if (busy || !canSort || !dragSource) return\n event.preventDefault()\n move(dragSource, rank)\n endDrag()\n }}>\n {canSort ? <button type=\"button\" className=\"web-search-drag-handle\"\n draggable={!busy} aria-disabled={busy}\n aria-label={'拖动排序 ' + provider.label}\n title=\"拖动排序,或用上下方向键调整\"\n onDragStart={event => {\n if (busy) { event.preventDefault(); return }\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('application/x-dsh-search-provider', provider.id)\n const row = event.currentTarget.closest('li')\n if (row) event.dataTransfer.setDragImage(row, 16, 16)\n setDragSource(provider.id)\n setSortAnnouncement('')\n }}\n onDragEnd={endDrag}\n onKeyDown={event => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(provider.id, rank + (event.key === 'ArrowUp' ? -1 : 1))\n }}>\n <svg width=\"16\" height=\"20\" viewBox=\"0 0 16 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"5\" cy=\"5\" r=\"1.5\" /><circle cx=\"11\" cy=\"5\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"10\" r=\"1.5\" /><circle cx=\"11\" cy=\"10\" r=\"1.5\" />\n <circle cx=\"5\" cy=\"15\" r=\"1.5\" /><circle cx=\"11\" cy=\"15\" r=\"1.5\" />\n </svg>\n </button> : <span className=\"web-search-rank-index\">{participating ? rank + 1 : '—'}</span>}\n <div className=\"web-search-provider\">\n <div className=\"web-search-provider-heading\">\n <strong>{provider.label}</strong>\n {active && <span className=\"web-search-meta\">当前使用</span>}\n {provider.signupUrl && <ExternalLink url={provider.signupUrl} label={provider.label + ' 去注册'}>去注册</ExternalLink>}\n </div>\n <div className=\"web-search-provider-info\">\n <p>{info.description}</p>\n <p>{info.pricing}{info.pricingUrl && <> <ExternalLink url={info.pricingUrl}\n label={provider.label + ' 费用说明'}>{provider.id === 'ddg' ? '官网' : '费用说明'}</ExternalLink></>}</p>\n </div>\n {participating && !provider.configured && <p className=\"web-search-meta\">{needsKey ? '待配置 Key' : '暂不可用'}</p>}\n {shared && participating && <small>{provider.credentialHint ?? '与其他设置共用凭据'}</small>}\n {showKey ? <label>API Key\n <input type=\"password\" autoComplete=\"off\" spellCheck={false}\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n value={keys[provider.id] ?? ''}\n placeholder={provider.configured ? '已配置,留空不替换' : '请输入 API Key'}\n onChange={event => setKeys(current => ({ ...current, [provider.id]: event.target.value }))} />\n </label> : null}\n {needsKey && !shared && provider.configured ? <ConfirmButton\n disabled={busy || writable[provider.credentialRef ?? ''] === false}\n label=\"删除 Key\" confirmLabel=\"确认删除 Key?\"\n onConfirm={() => void action(() => removeKey(provider))} /> : null}\n </div>\n <div className=\"web-search-rank-move\">\n <button\n type=\"button\"\n role=\"switch\"\n className={`web-search-switch${participating ? ' is-on' : ''}`}\n aria-checked={participating}\n aria-label={`${participating ? '关闭' : '开启'} ${provider.label}`}\n disabled={busy}\n onClick={() => {\n const next = participating ? order.filter(id => id !== provider.id) : [...order, provider.id]\n void action(() => persist(nextSearchEnabled(status.settings.searchEnabled, next, status.providers, keys, writable), next))\n }}>\n <span className=\"web-search-switch-thumb\" aria-hidden=\"true\" />\n </button>\n </div>\n </li>\n })}\n </ol>\n <p className=\"web-search-meta\">价格核对于 {PROVIDER_PRICING_CHECKED},实际额度与费用以供应商账户为准。</p>\n {on ? <div className=\"web-search-card-actions\">\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n const result = await call<{ sources: number }>('test')\n await load()\n setNote(`连接正常,返回 ${result.sources} 条来源。`)\n })}>测试连接(可能计费)</button>\n </div> : null}\n </article>}\n </div>\n <div role=\"tabpanel\" id={tabsId + '-limits-panel'} aria-labelledby={tabsId + '-limits-tab'}\n hidden={tab !== 'limits'} tabIndex={0}>\n <fieldset className=\"web-search-limits\" disabled={busy} aria-label=\"请求限制\">\n {limits.map(([key, label, min, max]) => (\n <label key={key}>{label}\n <input type=\"number\" min={min} max={max} step={1} value={draft[key]}\n onChange={event => setDraft({ ...draft, [key]: Number(event.target.value) })} />\n </label>\n ))}\n </fieldset>\n </div>\n <button type=\"button\" disabled={busy} onClick={() => void action(async () => {\n await persist(status.settings.searchEnabled, order)\n setNote('已保存。')\n })}>保存</button>\n </section>\n}\n\nconst styles = `\n.web-search-settings{max-width:760px;display:grid;gap:24px;color:inherit;font:400 var(--font-size-2,14px)/1.5 var(--default-font-family,system-ui,sans-serif)}\n.web-search-settings p{margin:0;line-height:1.5}\n.web-search-settings small,.web-search-meta,.web-search-empty{font-size:var(--font-size-1,13px);color:var(--gray-11,inherit)}\n.web-search-error{color:var(--red-11,#b42318)}\n.web-search-card{display:grid;gap:12px;padding:16px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent));border-radius:12px}\n.web-search-card header{display:flex;justify-content:space-between;gap:12px;align-items:center;padding-bottom:8px}\n.web-search-card h3{margin:0;font-size:var(--font-size-3,16px);font-weight:600}\n.web-search-provider{display:grid;gap:8px;min-width:0}\n.web-search-provider-info{display:grid;gap:3px;font-size:var(--font-size-1,13px);color:var(--gray-11,inherit);overflow-wrap:anywhere}\n.web-search-provider-heading{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;min-height:24px}\n.web-search-provider-heading strong{font-weight:500}\n.web-search-card label{display:grid;gap:6px}\n.web-search-settings input{box-sizing:border-box;width:100%;min-width:0;padding:8px 10px;border:1px solid var(--gray-6,color-mix(in srgb,currentColor 22%,transparent));border-radius:8px;background:var(--color-surface,transparent);color:inherit;font:inherit;transition:border-color 150ms ease,box-shadow 150ms ease}\n.web-search-settings input:focus{border-color:var(--accent-9,#3b82f6);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent-9,#3b82f6) 25%,transparent)}\n.web-search-settings button:not([role=\"switch\"]){min-height:34px;padding:6px 12px;border:1px solid color-mix(in srgb,currentColor 25%,transparent);border-radius:8px;background:transparent;color:inherit;cursor:pointer;font:inherit;justify-self:start;transition:background-color 150ms ease,color 150ms ease,border-color 150ms ease,box-shadow 150ms ease,transform 150ms ease}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):hover:not(:disabled){background:var(--gray-3,color-mix(in srgb,currentColor 6%,transparent));border-color:color-mix(in srgb,currentColor 35%,transparent)}\n.web-search-settings button:not([role=\"switch\"]):not([role=\"tab\"]):not(.web-search-drag-handle):active:not(:disabled){transform:scale(.97)}\n.web-search-danger:hover:not(:disabled){border-color:var(--red-11,#b42318);color:var(--red-11,#b42318);background:color-mix(in srgb,var(--red-11,#b42318) 8%,transparent)}\n.web-search-settings button:disabled{opacity:.45;cursor:not-allowed}\n.web-search-tabs{display:flex;gap:20px;border-bottom:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-tabs button[role=\"tab\"]{padding:8px 0;border:0;border-bottom:2px solid transparent;border-radius:0;color:var(--gray-11,inherit)}\n.web-search-tabs button[role=\"tab\"]:hover{color:inherit}\n.web-search-tabs button[aria-selected=\"true\"]{border-bottom-color:var(--accent-9,#3b82f6);color:var(--accent-11,inherit);font-weight:600}\n.web-search-settings [role=\"tabpanel\"]{border-radius:12px}\n.web-search-settings .web-search-switch{all:unset;box-sizing:border-box;position:relative;display:inline-block;width:36px;height:20px;flex:none;border-radius:999px;background:var(--gray-7,color-mix(in srgb,currentColor 28%,transparent));cursor:pointer}\n.web-search-switch.is-on{background:var(--accent-9,#3b82f6)}\n.web-search-switch:disabled{cursor:not-allowed;opacity:.7}\n.web-search-switch-thumb{position:absolute;top:3px;left:3px;width:14px;height:14px;border-radius:999px;background:#fff;transition:transform 150ms ease}\n.web-search-switch.is-on .web-search-switch-thumb{transform:translateX(16px)}\n.web-search-settings :focus-visible{outline:2px solid var(--accent-9,currentColor);outline-offset:3px}\n.web-search-card-actions{display:flex;flex-wrap:wrap;gap:8px}\n.web-search-rank{margin:0;padding:0;list-style:none}\n.web-search-rank li{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto;gap:10px;align-items:start;padding:14px 0;border-top:1px solid var(--gray-6,color-mix(in srgb,currentColor 15%,transparent))}\n.web-search-settings button.web-search-drag-handle{display:grid;place-items:center;width:24px;height:24px;min-height:24px;padding:0;border:0;cursor:grab;color:inherit;opacity:.65}\n.web-search-settings button.web-search-drag-handle:hover{opacity:1;background:var(--gray-3,color-mix(in srgb,currentColor 8%,transparent))}\n.web-search-settings button.web-search-drag-handle:active{cursor:grabbing}\n.web-search-settings button.web-search-drag-handle[aria-disabled=\"true\"]{cursor:wait;opacity:.4}\n.web-search-rank li[data-dragging]{opacity:.45}\n.web-search-rank li[data-drop]::after{content:'';position:absolute;left:0;right:0;height:2px;background:var(--accent-9,#3b82f6);pointer-events:none}\n.web-search-rank li[data-drop=\"before\"]::after{top:-1px}\n.web-search-rank li[data-drop=\"after\"]::after{bottom:-1px}\n.web-search-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n.web-search-rank-index{opacity:.6;font-variant-numeric:tabular-nums;line-height:24px}\n.web-search-rank-move{display:flex;flex-wrap:wrap;gap:6px;align-items:center;justify-content:flex-end;padding-top:2px}\n.web-search-settings a{color:var(--accent-11,currentColor);font-size:var(--font-size-1,13px);text-underline-offset:3px}\n.web-search-limits{margin:0;border:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 20px}\n.web-search-limits label{display:grid;gap:6px;font-size:var(--font-size-2,14px)}\n@media(max-width:560px){.web-search-rank-move{flex-direction:column;align-items:flex-end}.web-search-limits{grid-template-columns:minmax(0,1fr)}}\n@media(prefers-reduced-motion:reduce){.web-search-switch-thumb{transition:none}.web-search-settings button:not([role=\"switch\"]),.web-search-settings input{transition:none}}\n`\n\nexport function apply(ctx: Context): void {\n const client = ctx as unknown as Client\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const style = document.createElement('style')\n style.setAttribute('data-plugin', '@klarkxy/dsh-web-search-manager'); style.textContent = styles\n style.dataset.dshWebSearch = ''; document.head.appendChild(style)\n return () => style.remove()\n }, 'web-search-manager.styles')\n ctx.effect(() => client.slots.inject('settings.section', () => client.slots.register({\n name: 'settings.section', id: 'web-search', order: 70, label: '网络搜索',\n }, () => <NetworkSearchSettings client={client} />)), 'web-search-manager.settings')\n}\n"],"mappings":"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"}
|
package/lib/client.js
CHANGED
|
@@ -176,11 +176,6 @@ const providerInfo = {
|
|
|
176
176
|
pricing: "免费使用,不产生搜索 API 费用。",
|
|
177
177
|
pricingUrl: "https://duckduckgo.com/"
|
|
178
178
|
},
|
|
179
|
-
"zhihu-global": {
|
|
180
|
-
description: "融合知乎问答与全网内容,返回可溯源的搜索结果。",
|
|
181
|
-
pricing: "注册可获 5,000 次/天试用额度;超额价格需向平台咨询。",
|
|
182
|
-
pricingUrl: "https://developer.zhihu.com/"
|
|
183
|
-
},
|
|
184
179
|
bocha: {
|
|
185
180
|
description: "面向中文内容的网页搜索,返回标题、链接和摘要。",
|
|
186
181
|
pricing: "按调用次数计费,目录价 ¥0.036/次;试用额度以官方活动为准。",
|
|
@@ -218,10 +213,11 @@ const providerInfo = {
|
|
|
218
213
|
}
|
|
219
214
|
};
|
|
220
215
|
function searchProviderInfo(provider) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
216
|
+
const known = Object.hasOwn(providerInfo, provider.id) ? providerInfo[provider.id] : void 0;
|
|
217
|
+
return {
|
|
218
|
+
description: known?.description ?? provider.description,
|
|
219
|
+
pricing: provider.pricing ?? known?.pricing ?? (provider.billing === "none" ? "此后端不收取搜索费用。" : provider.billing === "model-and-tools" ? "按模型用量和工具调用计费;免费额度及单价请查看供应商说明。" : "按 API 调用计费;免费额度及单价请查看供应商说明。"),
|
|
220
|
+
pricingUrl: provider.pricingUrl ?? known?.pricingUrl ?? provider.signupUrl
|
|
225
221
|
};
|
|
226
222
|
}
|
|
227
223
|
//#endregion
|
|
@@ -690,7 +686,7 @@ function NetworkSearchSettings({ client }) {
|
|
|
690
686
|
className: "web-search-meta",
|
|
691
687
|
children: needsKey ? "待配置 Key" : "暂不可用"
|
|
692
688
|
}),
|
|
693
|
-
shared && participating && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: provider.
|
|
689
|
+
shared && participating && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: provider.credentialHint ?? "与其他设置共用凭据" }),
|
|
694
690
|
showKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: ["API Key", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
695
691
|
type: "password",
|
|
696
692
|
autoComplete: "off",
|
package/lib/contracts.d.ts
CHANGED
|
@@ -20,9 +20,15 @@ interface ProviderDescriptor {
|
|
|
20
20
|
readonly description: string;
|
|
21
21
|
readonly defaultBaseURL?: string;
|
|
22
22
|
readonly credentialRef?: string;
|
|
23
|
-
/** True when the key is shared with another surface
|
|
23
|
+
/** True when the key is shared with another surface. The page must not delete it. */
|
|
24
24
|
readonly credentialShared?: boolean;
|
|
25
|
+
/** Optional notice naming the settings surface that owns a shared credential. */
|
|
26
|
+
readonly credentialHint?: string;
|
|
25
27
|
readonly billing: 'request' | 'model-and-tools' | 'none';
|
|
28
|
+
/** Optional provider-supplied billing summary. */
|
|
29
|
+
readonly pricing?: string;
|
|
30
|
+
/** Public HTTPS page explaining provider charges. */
|
|
31
|
+
readonly pricingUrl?: string;
|
|
26
32
|
/** Public HTTPS page where the user can create an API key. */
|
|
27
33
|
readonly signupUrl?: string;
|
|
28
34
|
}
|
package/lib/contracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.js","names":[],"sources":["../src/contracts.ts"],"sourcesContent":["import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Execution and source vocabulary belong to ctx.web. */\nexport interface ProviderDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface
|
|
1
|
+
{"version":3,"file":"contracts.js","names":[],"sources":["../src/contracts.ts"],"sourcesContent":["import type { WebFetchProvider, WebSearchProvider } from '@deepseek-ai/dsh-web'\n\nexport const WEB_SEARCH_RPC_CHANNEL = '/web-search-manager'\nexport type RpcResult<T = unknown> = { ok: true; value: T } | {\n ok: false; error: { code: string; message: string; details: Record<string, unknown> }\n}\nexport type ProviderKind = 'search' | 'fetch'\n/** Management metadata only. Execution and source vocabulary belong to ctx.web. */\nexport interface ProviderDescriptor {\n readonly id: string\n readonly label: string\n readonly description: string\n readonly defaultBaseURL?: string\n readonly credentialRef?: string\n /** True when the key is shared with another surface. The page must not delete it. */\n readonly credentialShared?: boolean\n /** Optional notice naming the settings surface that owns a shared credential. */\n readonly credentialHint?: string\n readonly billing: 'request' | 'model-and-tools' | 'none'\n /** Optional provider-supplied billing summary. */\n readonly pricing?: string\n /** Public HTTPS page explaining provider charges. */\n readonly pricingUrl?: string\n /** Public HTTPS page where the user can create an API key. */\n readonly signupUrl?: string\n}\nexport interface ProviderOptions {\n readonly apiKey?: string\n readonly baseURL?: string\n readonly timeoutMs: number\n readonly maxFetchChars: number\n}\nexport type SearchProviderFactory = (options: ProviderOptions) => WebSearchProvider\nexport type FetchProviderFactory = (options: ProviderOptions) => WebFetchProvider\n/** Never store credential values here. Keys are resolved by reference per call. */\nexport interface WebSettings {\n revision: number\n searchEnabled: boolean\n fetchEnabled: boolean\n searchProvider: string\n fetchProvider: string\n searchOrder: string[]\n maxResults: number\n maxQueries: number\n timeoutMs: number\n maxFetchChars: number\n endpoints: Record<string, string>\n}\nexport const DEFAULT_SEARCH_ORDER = ['ddg'] as const\n\nexport const defaultSettings = (): WebSettings => ({\n revision: 0, searchEnabled: true, fetchEnabled: true,\n searchProvider: 'ddg', fetchProvider: 'http', searchOrder: [...DEFAULT_SEARCH_ORDER],\n maxResults: 5, maxQueries: 1,\n timeoutMs: 30_000, maxFetchChars: 100_000, endpoints: {},\n})\n\n/** Keyed backends first, keyless last — same idea as OpenClaw auto-detect. */\nexport function defaultSearchOrder(ids: readonly string[], keyed: (id: string) => boolean): string[] {\n return [...ids.filter(keyed), ...ids.filter(id => !keyed(id))]\n}\n\nexport function migrateSearchOrder(settings: { searchOrder?: string[]; searchProvider?: string }): string[] {\n return settings.searchOrder ?? (settings.searchProvider ? [settings.searchProvider] : [...DEFAULT_SEARCH_ORDER])\n}\n\nexport function resolveSearchOrder(\n knownIds: readonly string[],\n settings: Pick<WebSettings, 'searchOrder' | 'searchProvider'>,\n _keyed: (id: string) => boolean,\n): string[] {\n return [...new Set(settings.searchOrder.filter(id => knownIds.includes(id)))]\n}\n\nexport function pickActiveSearch(order: readonly string[], configured: (id: string) => boolean): string {\n return order.find(configured) ?? ''\n}\nexport interface ProviderView extends ProviderDescriptor {\n kind: ProviderKind\n configured: boolean\n baseURL?: string\n calls: number\n failures: number\n}\nexport interface WebStatus {\n settings: WebSettings\n providers: ProviderView[]\n searchActive: boolean\n fetchActive: boolean\n storageFailed: boolean\n}\nexport function providerKey(kind: ProviderKind, id: string): string { return `${kind}:${id}` }\nexport function validateBaseURL(value: string): string {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {\n throw new Error('供应商地址必须是 HTTPS,且不能包含用户名、密码、查询参数或片段。')\n }\n return url.href.replace(/\\/+$/, '')\n}\n"],"mappings":";AAEA,MAAa,yBAAyB;AA8CtC,MAAa,uBAAuB,CAAC,KAAK;AAE1C,MAAa,yBAAsC;CACjD,UAAU;CAAG,eAAe;CAAM,cAAc;CAChD,gBAAgB;CAAO,eAAe;CAAQ,aAAa,CAAC,GAAG,oBAAoB;CACnF,YAAY;CAAG,YAAY;CAC3B,WAAW;CAAQ,eAAe;CAAS,WAAW,CAAC;AACzD;;AAGA,SAAgB,mBAAmB,KAAwB,OAA0C;CACnG,OAAO,CAAC,GAAG,IAAI,OAAO,KAAK,GAAG,GAAG,IAAI,QAAO,OAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/D;AAEA,SAAgB,mBAAmB,UAAyE;CAC1G,OAAO,SAAS,gBAAgB,SAAS,iBAAiB,CAAC,SAAS,cAAc,IAAI,CAAC,GAAG,oBAAoB;AAChH;AAEA,SAAgB,mBACd,UACA,UACA,QACU;CACV,OAAO,CAAC,GAAG,IAAI,IAAI,SAAS,YAAY,QAAO,OAAM,SAAS,SAAS,EAAE,CAAC,CAAC,CAAC;AAC9E;AAEA,SAAgB,iBAAiB,OAA0B,YAA6C;CACtG,OAAO,MAAM,KAAK,UAAU,KAAK;AACnC;AAeA,SAAgB,YAAY,MAAoB,IAAoB;CAAE,OAAO,GAAG,KAAK,GAAG;AAAK;AAC7F,SAAgB,gBAAgB,OAAuB;CACrD,MAAM,MAAM,IAAI,IAAI,KAAK;CACzB,IAAI,IAAI,aAAa,YAAY,IAAI,YAAY,IAAI,YAAY,IAAI,UAAU,IAAI,MACjF,MAAM,IAAI,MAAM,qCAAqC;CAEvD,OAAO,IAAI,KAAK,QAAQ,QAAQ,EAAE;AACpC"}
|
package/lib/index.js
CHANGED
|
@@ -444,6 +444,7 @@ function registerBuiltins(manager) {
|
|
|
444
444
|
defaultBaseURL: DEEPSEEK_DEFAULT_BASE_URL,
|
|
445
445
|
credentialRef: "DEEPSEEK_API_KEY",
|
|
446
446
|
credentialShared: true,
|
|
447
|
+
credentialHint: "与模型设置共用 Key",
|
|
447
448
|
billing: "model-and-tools",
|
|
448
449
|
signupUrl: "https://platform.deepseek.com/api_keys"
|
|
449
450
|
}, (options) => new DeepSeekSearchProvider(() => ({
|
|
@@ -758,11 +759,14 @@ var WebSearchManager = class {
|
|
|
758
759
|
if (!/^[a-z][a-z0-9-]{0,63}$/.test(descriptor.id) || !descriptor.label.trim()) fail("WEB_INVALID_PROVIDER", "供应商标识或名称无效。");
|
|
759
760
|
if (descriptor.credentialRef && !/^[A-Z][A-Z0-9_]{1,127}$/.test(descriptor.credentialRef)) fail("WEB_INVALID_PROVIDER", "供应商凭据引用无效。");
|
|
760
761
|
if (descriptor.defaultBaseURL) validateBaseURL(descriptor.defaultBaseURL);
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
762
|
+
for (const [value, label] of [[descriptor.signupUrl, "注册地址"], [descriptor.pricingUrl, "费用说明地址"]]) {
|
|
763
|
+
if (!value) continue;
|
|
764
|
+
try {
|
|
765
|
+
const url = new URL(value);
|
|
766
|
+
if (url.protocol !== "https:" || url.username || url.password) fail("WEB_INVALID_PROVIDER", `${label}须为 HTTPS。`);
|
|
767
|
+
} catch {
|
|
768
|
+
fail("WEB_INVALID_PROVIDER", `${label}须为 HTTPS。`);
|
|
769
|
+
}
|
|
766
770
|
}
|
|
767
771
|
if (this.entries.has(key)) fail("WEB_DUPLICATE_PROVIDER", "供应商标识已注册。");
|
|
768
772
|
const entry = {
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["MAX_RESPONSE_BYTES","MAX_RESPONSE_BYTES","record","readJSON","readJSON","record"],"sources":["../src/search-engine.ts","../src/http-json.ts","../src/rest-search.ts","../src/tavily.ts","../src/builtins.ts","../src/host-rpc.ts","../src/manager.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\n\n/** Keyless `ctx.web` search backend. The model still calls official `web_search`. */\nexport const SEARCH_ENGINE_ID = 'ddg'\nexport const SEARCH_ENGINE_PAGE = 'https://lite.duckduckgo.com/lite/'\nconst MAX_RESPONSE_BYTES = 2_000_000\nconst USER_AGENT = 'Mozilla/5.0 (compatible; dsh-editor-search/1.0)'\n\nexport function searchEngineUrl(query: string, page = SEARCH_ENGINE_PAGE): string {\n const url = new URL(page)\n if (url.protocol !== 'https:' || url.username || url.password) throw new Error('Invalid search page')\n url.searchParams.set('q', query)\n return url.href\n}\n\nfunction decodeEntities(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, ' ')\n}\n\nfunction stripTags(value: string): string {\n return value.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ')\n}\n\nexport function unwrapSearchHref(href: string, pageUrl: string): string | undefined {\n let raw = decodeEntities(href.trim())\n let url: URL\n try { url = new URL(raw, pageUrl) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n const host = url.hostname\n const isDdg = host === 'duckduckgo.com' || host.endsWith('.duckduckgo.com')\n if (isDdg && (url.pathname === '/l/' || url.pathname === '/l')) {\n const nested = url.searchParams.get('uddg') ?? url.searchParams.get('u')\n if (nested) {\n try { url = new URL(nested) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n }\n }\n const resultHost = url.hostname.replace(/^www\\./, '')\n if (resultHost === 'duckduckgo.com' || resultHost.endsWith('.duckduckgo.com')) return\n return url.href\n}\n\nexport function parseSearchEngineHtml(html: string, pageUrl = SEARCH_ENGINE_PAGE): WebSearchSource[] {\n const sources: WebSearchSource[] = []\n const seen = new Set<string>()\n const re = /<a\\b[^>]*href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')[^>]*>([\\s\\S]*?)<\\/a>/gi\n let match: RegExpExecArray | null\n while ((match = re.exec(html))) {\n const url = unwrapSearchHref(match[1] ?? match[2] ?? '', pageUrl)\n if (!url || seen.has(url)) continue\n seen.add(url)\n const title = decodeEntities(stripTags(match[3] ?? '')).trim().slice(0, 1000)\n sources.push(title ? { url, title } : { url })\n }\n return sources\n}\n\nasync function readText(response: Response, signal?: AbortSignal): Promise<string> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return new TextDecoder().decode(bytes)\n}\n\n/** Adapter for `ctx.web.search`; `dsh-tool-web` still owns the model-facing `web_search` tool. */\nexport class SearchEngineProvider implements WebSearchProvider {\n readonly id = SEARCH_ENGINE_ID\n constructor(private readonly options: { fetch?: typeof fetch } = {}) {}\n available(): boolean { return true }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const url = searchEngineUrl(request.query.trim())\n const response = await (this.options.fetch ?? fetch)(url, {\n method: 'GET', redirect: 'follow', signal,\n headers: { accept: 'text/html', 'user-agent': USER_AGENT },\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索页请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = parseSearchEngineHtml(await readText(response, signal), response.url || SEARCH_ENGINE_PAGE)\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法读取公开搜索页。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import type { WebSearchSource } from '@deepseek-ai/dsh-web'\n\nexport const MAX_RESPONSE_BYTES = 2_000_000\n\nexport function record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nexport async function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n}\n\nexport function httpsEndpoint(base: string, path = ''): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}${path}`\n}\n\nexport function sourceFrom(raw: unknown, urlKey: string, titleKey?: string, snippetKey?: string): WebSearchSource | undefined {\n const row = record(raw)\n if (typeof row?.[urlKey] !== 'string') return\n let url: URL\n try { url = new URL(row[urlKey] as string) } catch { return }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) return\n const title = titleKey && typeof row[titleKey] === 'string' ? (row[titleKey] as string).slice(0, 1000) : undefined\n const snippet = snippetKey && typeof row[snippetKey] === 'string' ? (row[snippetKey] as string).slice(0, 8000) : undefined\n return { url: url.href, ...(title ? { title } : {}), ...(snippet ? { snippet } : {}) }\n}\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nimport { httpsEndpoint, readJSON, record, sourceFrom } from './http-json.ts'\n\nexport interface RestSearchOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\n\ntype RestSpec = {\n id: string\n fallback: string\n path: string\n method: 'GET' | 'POST'\n headers(apiKey: string): Record<string, string>\n query?: (request: { query: string; count: number }) => Record<string, string>\n body?: (request: { query: string; count: number }) => unknown\n parse(payload: unknown): WebSearchSource[]\n}\n\nfunction createRestSearch(spec: RestSpec): new (options: RestSearchOptions) => WebSearchProvider {\n return class implements WebSearchProvider {\n readonly id = spec.id\n constructor(private readonly options: RestSearchOptions) {}\n available(): boolean {\n try { httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError(`${spec.id} 配置不可用。`, 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n const query = request.query.trim()\n try {\n signal?.throwIfAborted()\n const url = new URL(httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path))\n for (const [key, value] of Object.entries(spec.query?.({ query, count }) ?? {})) url.searchParams.set(key, value)\n const response = await (this.options.fetch ?? fetch)(url, {\n method: spec.method, redirect: 'error', signal,\n headers: { accept: 'application/json', ...spec.headers(this.options.apiKey) },\n ...(spec.body ? { body: JSON.stringify(spec.body({ query, count })) } : {}),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = spec.parse(await readJSON(response, signal))\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法完成搜索请求。', 'WEB_PROVIDER_ERROR')\n }\n }\n }\n}\n\nexport const BraveSearchProvider = createRestSearch({\n id: 'brave', fallback: 'https://api.search.brave.com', path: '/res/v1/web/search', method: 'GET',\n headers: apiKey => ({ 'x-subscription-token': apiKey }),\n query: ({ query, count }) => ({ q: query, count: String(count) }),\n parse(payload) {\n const results = record(record(payload)?.web)?.results\n return Array.isArray(results) ? results.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n\nexport const BochaSearchProvider = createRestSearch({\n id: 'bocha', fallback: 'https://api.bochaai.com', path: '/v1/web-search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, count, freshness: 'noLimit', summary: false }),\n parse(payload) {\n const pages = record(record(record(payload)?.data)?.webPages)?.value\n ?? record(record(payload)?.webPages)?.value\n return Array.isArray(pages) ? pages.flatMap(row => sourceFrom(row, 'url', 'name', 'snippet') ?? []) : []\n },\n})\n\nexport const SerperSearchProvider = createRestSearch({\n id: 'serper', fallback: 'https://google.serper.dev', path: '/search', method: 'POST',\n headers: apiKey => ({ 'x-api-key': apiKey, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ q: query, num: count }),\n parse(payload) {\n const organic = record(payload)?.organic\n return Array.isArray(organic) ? organic.flatMap(row => sourceFrom(row, 'link', 'title', 'snippet') ?? []) : []\n },\n})\n\nexport const FirecrawlSearchProvider = createRestSearch({\n id: 'firecrawl', fallback: 'https://api.firecrawl.dev', path: '/v1/search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, limit: count }),\n parse(payload) {\n const data = record(payload)?.data\n return Array.isArray(data) ? data.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nexport interface TavilyOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\nconst MAX_RESPONSE_BYTES = 2_000_000\nfunction endpoint(base: string): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}/search`\n}\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\nasync function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n}\n/** Direct REST adapter. No auxiliary LLM, retries, anonymous fallback or auto-depth. */\nexport class TavilySearchProvider implements WebSearchProvider {\n readonly id = 'tavily'\n constructor(private readonly options: TavilyOptions) {}\n available(): boolean {\n try { endpoint(this.options.baseURL ?? 'https://api.tavily.com'); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError('Tavily 配置不可用。', 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const response = await (this.options.fetch ?? fetch)(endpoint(this.options.baseURL ?? 'https://api.tavily.com'), {\n method: 'POST', redirect: 'error', signal,\n headers: { authorization: `Bearer ${this.options.apiKey}`, 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n query: request.query.trim(), max_results: count, search_depth: 'basic', topic: 'general',\n auto_parameters: false, include_answer: false, include_raw_content: false, include_images: false,\n }),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`Tavily 请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const payload = record(await readJSON(response, signal))\n if (!Array.isArray(payload?.results)) throw new Error('Invalid response shape')\n const sources: WebSearchSource[] = []\n for (const raw of payload.results) {\n const row = record(raw)\n if (typeof row?.url !== 'string') continue\n let url: URL\n try { url = new URL(row.url) } catch { continue }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) continue\n sources.push({\n url: url.href,\n ...(typeof row.title === 'string' ? { title: row.title.slice(0, 1000) } : {}),\n ...(typeof row.content === 'string' ? { snippet: row.content.slice(0, 8000) } : {}),\n ...(typeof row.published_date === 'string' && Number.isFinite(Date.parse(row.published_date))\n ? { publishedAt: new Date(row.published_date).toISOString() } : {}),\n })\n }\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('Tavily 搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('Tavily 返回无效响应或网络连接失败。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import {\n DEEPSEEK_DEFAULT_API_VERSION,\n DEEPSEEK_DEFAULT_BASE_URL,\n DEEPSEEK_DEFAULT_MAX_TOKENS,\n DEEPSEEK_DEFAULT_MAX_USES,\n DEEPSEEK_DEFAULT_MODEL,\n DeepSeekSearchProvider,\n} from '@deepseek-ai/dsh-web-search-deepseek'\nimport { ExaSearchProvider } from '@deepseek-ai/dsh-web-search-exa'\nimport { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http'\nimport { SEARCH_ENGINE_ID, SearchEngineProvider } from './search-engine.ts'\nimport { BochaSearchProvider, BraveSearchProvider, FirecrawlSearchProvider, SerperSearchProvider } from './rest-search.ts'\nimport { TavilySearchProvider } from './tavily.ts'\nimport type { WebSearchManager } from './manager.ts'\n\nexport function registerBuiltins(manager: WebSearchManager): () => void {\n const offEngine = manager.registerSearchProvider({\n id: SEARCH_ENGINE_ID, label: 'DuckDuckGo', billing: 'none',\n description: '无需注册。默认后端。',\n }, () => new SearchEngineProvider())\n const offDeepSeek = manager.registerSearchProvider({\n id: 'deepseek-official', label: 'DeepSeek 搜索',\n description: '使用已配置的 DeepSeek API Key。可能产生额外搜索费用。',\n defaultBaseURL: DEEPSEEK_DEFAULT_BASE_URL, credentialRef: 'DEEPSEEK_API_KEY',\n credentialShared: true, 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 if (descriptor.signupUrl) {\n try {\n const url = new URL(descriptor.signupUrl)\n if (url.protocol !== 'https:' || url.username || url.password) fail('WEB_INVALID_PROVIDER', '注册地址须为 HTTPS。')\n } catch { fail('WEB_INVALID_PROVIDER', '注册地址须为 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/search-engine.ts","../src/http-json.ts","../src/rest-search.ts","../src/tavily.ts","../src/builtins.ts","../src/host-rpc.ts","../src/manager.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\n\n/** Keyless `ctx.web` search backend. The model still calls official `web_search`. */\nexport const SEARCH_ENGINE_ID = 'ddg'\nexport const SEARCH_ENGINE_PAGE = 'https://lite.duckduckgo.com/lite/'\nconst MAX_RESPONSE_BYTES = 2_000_000\nconst USER_AGENT = 'Mozilla/5.0 (compatible; dsh-editor-search/1.0)'\n\nexport function searchEngineUrl(query: string, page = SEARCH_ENGINE_PAGE): string {\n const url = new URL(page)\n if (url.protocol !== 'https:' || url.username || url.password) throw new Error('Invalid search page')\n url.searchParams.set('q', query)\n return url.href\n}\n\nfunction decodeEntities(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, ' ')\n}\n\nfunction stripTags(value: string): string {\n return value.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ')\n}\n\nexport function unwrapSearchHref(href: string, pageUrl: string): string | undefined {\n let raw = decodeEntities(href.trim())\n let url: URL\n try { url = new URL(raw, pageUrl) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n const host = url.hostname\n const isDdg = host === 'duckduckgo.com' || host.endsWith('.duckduckgo.com')\n if (isDdg && (url.pathname === '/l/' || url.pathname === '/l')) {\n const nested = url.searchParams.get('uddg') ?? url.searchParams.get('u')\n if (nested) {\n try { url = new URL(nested) } catch { return }\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return\n }\n }\n const resultHost = url.hostname.replace(/^www\\./, '')\n if (resultHost === 'duckduckgo.com' || resultHost.endsWith('.duckduckgo.com')) return\n return url.href\n}\n\nexport function parseSearchEngineHtml(html: string, pageUrl = SEARCH_ENGINE_PAGE): WebSearchSource[] {\n const sources: WebSearchSource[] = []\n const seen = new Set<string>()\n const re = /<a\\b[^>]*href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')[^>]*>([\\s\\S]*?)<\\/a>/gi\n let match: RegExpExecArray | null\n while ((match = re.exec(html))) {\n const url = unwrapSearchHref(match[1] ?? match[2] ?? '', pageUrl)\n if (!url || seen.has(url)) continue\n seen.add(url)\n const title = decodeEntities(stripTags(match[3] ?? '')).trim().slice(0, 1000)\n sources.push(title ? { url, title } : { url })\n }\n return sources\n}\n\nasync function readText(response: Response, signal?: AbortSignal): Promise<string> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return new TextDecoder().decode(bytes)\n}\n\n/** Adapter for `ctx.web.search`; `dsh-tool-web` still owns the model-facing `web_search` tool. */\nexport class SearchEngineProvider implements WebSearchProvider {\n readonly id = SEARCH_ENGINE_ID\n constructor(private readonly options: { fetch?: typeof fetch } = {}) {}\n available(): boolean { return true }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const url = searchEngineUrl(request.query.trim())\n const response = await (this.options.fetch ?? fetch)(url, {\n method: 'GET', redirect: 'follow', signal,\n headers: { accept: 'text/html', 'user-agent': USER_AGENT },\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索页请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = parseSearchEngineHtml(await readText(response, signal), response.url || SEARCH_ENGINE_PAGE)\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法读取公开搜索页。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import type { WebSearchSource } from '@deepseek-ai/dsh-web'\n\nexport const MAX_RESPONSE_BYTES = 2_000_000\n\nexport function record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nexport async function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n}\n\nexport function httpsEndpoint(base: string, path = ''): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}${path}`\n}\n\nexport function sourceFrom(raw: unknown, urlKey: string, titleKey?: string, snippetKey?: string): WebSearchSource | undefined {\n const row = record(raw)\n if (typeof row?.[urlKey] !== 'string') return\n let url: URL\n try { url = new URL(row[urlKey] as string) } catch { return }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) return\n const title = titleKey && typeof row[titleKey] === 'string' ? (row[titleKey] as string).slice(0, 1000) : undefined\n const snippet = snippetKey && typeof row[snippetKey] === 'string' ? (row[snippetKey] as string).slice(0, 8000) : undefined\n return { url: url.href, ...(title ? { title } : {}), ...(snippet ? { snippet } : {}) }\n}\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nimport { httpsEndpoint, readJSON, record, sourceFrom } from './http-json.ts'\n\nexport interface RestSearchOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\n\ntype RestSpec = {\n id: string\n fallback: string\n path: string\n method: 'GET' | 'POST'\n headers(apiKey: string): Record<string, string>\n query?: (request: { query: string; count: number }) => Record<string, string>\n body?: (request: { query: string; count: number }) => unknown\n parse(payload: unknown): WebSearchSource[]\n}\n\nfunction createRestSearch(spec: RestSpec): new (options: RestSearchOptions) => WebSearchProvider {\n return class implements WebSearchProvider {\n readonly id = spec.id\n constructor(private readonly options: RestSearchOptions) {}\n available(): boolean {\n try { httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError(`${spec.id} 配置不可用。`, 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n const query = request.query.trim()\n try {\n signal?.throwIfAborted()\n const url = new URL(httpsEndpoint(this.options.baseURL ?? spec.fallback, spec.path))\n for (const [key, value] of Object.entries(spec.query?.({ query, count }) ?? {})) url.searchParams.set(key, value)\n const response = await (this.options.fetch ?? fetch)(url, {\n method: spec.method, redirect: 'error', signal,\n headers: { accept: 'application/json', ...spec.headers(this.options.apiKey) },\n ...(spec.body ? { body: JSON.stringify(spec.body({ query, count })) } : {}),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`搜索请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const sources = spec.parse(await readJSON(response, signal))\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('无法完成搜索请求。', 'WEB_PROVIDER_ERROR')\n }\n }\n }\n}\n\nexport const BraveSearchProvider = createRestSearch({\n id: 'brave', fallback: 'https://api.search.brave.com', path: '/res/v1/web/search', method: 'GET',\n headers: apiKey => ({ 'x-subscription-token': apiKey }),\n query: ({ query, count }) => ({ q: query, count: String(count) }),\n parse(payload) {\n const results = record(record(payload)?.web)?.results\n return Array.isArray(results) ? results.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n\nexport const BochaSearchProvider = createRestSearch({\n id: 'bocha', fallback: 'https://api.bochaai.com', path: '/v1/web-search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, count, freshness: 'noLimit', summary: false }),\n parse(payload) {\n const pages = record(record(record(payload)?.data)?.webPages)?.value\n ?? record(record(payload)?.webPages)?.value\n return Array.isArray(pages) ? pages.flatMap(row => sourceFrom(row, 'url', 'name', 'snippet') ?? []) : []\n },\n})\n\nexport const SerperSearchProvider = createRestSearch({\n id: 'serper', fallback: 'https://google.serper.dev', path: '/search', method: 'POST',\n headers: apiKey => ({ 'x-api-key': apiKey, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ q: query, num: count }),\n parse(payload) {\n const organic = record(payload)?.organic\n return Array.isArray(organic) ? organic.flatMap(row => sourceFrom(row, 'link', 'title', 'snippet') ?? []) : []\n },\n})\n\nexport const FirecrawlSearchProvider = createRestSearch({\n id: 'firecrawl', fallback: 'https://api.firecrawl.dev', path: '/v1/search', method: 'POST',\n headers: apiKey => ({ authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }),\n body: ({ query, count }) => ({ query, limit: count }),\n parse(payload) {\n const data = record(payload)?.data\n return Array.isArray(data) ? data.flatMap(row => sourceFrom(row, 'url', 'title', 'description') ?? []) : []\n },\n})\n","import { WebError, type WebSearchProvider, type WebSearchRequest, type WebSearchResult, type WebSearchSource } from '@deepseek-ai/dsh-web'\nexport interface TavilyOptions { apiKey: string; baseURL?: string; fetch?: typeof fetch }\nconst MAX_RESPONSE_BYTES = 2_000_000\nfunction endpoint(base: string): string {\n const url = new URL(base)\n if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new Error('Invalid endpoint')\n return `${url.href.replace(/\\/+$/, '')}/search`\n}\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\nasync function readJSON(response: Response, signal?: AbortSignal): Promise<unknown> {\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Missing response')\n let size = 0\n const chunks: Uint8Array[] = []\n try {\n while (true) {\n signal?.throwIfAborted()\n const { value, done } = await reader.read()\n if (done) break\n size += value.length\n if (size > MAX_RESPONSE_BYTES) throw new Error('Response too large')\n chunks.push(value)\n }\n const bytes = new Uint8Array(size)\n let offset = 0\n for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.length }\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown\n } finally { await reader.cancel().catch(() => {}); reader.releaseLock() }\n}\n/** Direct REST adapter. No auxiliary LLM, retries, anonymous fallback or auto-depth. */\nexport class TavilySearchProvider implements WebSearchProvider {\n readonly id = 'tavily'\n constructor(private readonly options: TavilyOptions) {}\n available(): boolean {\n try { endpoint(this.options.baseURL ?? 'https://api.tavily.com'); return Boolean(this.options.apiKey.trim()) }\n catch { return false }\n }\n async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {\n if (!this.available()) throw new WebError('Tavily 配置不可用。', 'WEB_CREDENTIAL_MISSING')\n if (!request.query?.trim() || request.query.length > 4000) throw new WebError('查询无效。', 'WEB_INVALID_REQUEST')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > 20) throw new WebError('结果数须为 1 至 20。', 'WEB_INVALID_REQUEST')\n try {\n signal?.throwIfAborted()\n const response = await (this.options.fetch ?? fetch)(endpoint(this.options.baseURL ?? 'https://api.tavily.com'), {\n method: 'POST', redirect: 'error', signal,\n headers: { authorization: `Bearer ${this.options.apiKey}`, 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({\n query: request.query.trim(), max_results: count, search_depth: 'basic', topic: 'general',\n auto_parameters: false, include_answer: false, include_raw_content: false, include_images: false,\n }),\n })\n if (!response.ok) {\n await response.body?.cancel().catch(() => {})\n throw new WebError(`Tavily 请求失败(HTTP ${response.status})。`, 'WEB_PROVIDER_ERROR')\n }\n const payload = record(await readJSON(response, signal))\n if (!Array.isArray(payload?.results)) throw new Error('Invalid response shape')\n const sources: WebSearchSource[] = []\n for (const raw of payload.results) {\n const row = record(raw)\n if (typeof row?.url !== 'string') continue\n let url: URL\n try { url = new URL(row.url) } catch { continue }\n if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) continue\n sources.push({\n url: url.href,\n ...(typeof row.title === 'string' ? { title: row.title.slice(0, 1000) } : {}),\n ...(typeof row.content === 'string' ? { snippet: row.content.slice(0, 8000) } : {}),\n ...(typeof row.published_date === 'string' && Number.isFinite(Date.parse(row.published_date))\n ? { publishedAt: new Date(row.published_date).toISOString() } : {}),\n })\n }\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n if (signal?.aborted) throw new WebError('Tavily 搜索已取消。', 'WEB_ABORTED')\n if (error instanceof WebError) throw error\n throw new WebError('Tavily 返回无效响应或网络连接失败。', 'WEB_PROVIDER_ERROR')\n }\n }\n}\n","import {\n DEEPSEEK_DEFAULT_API_VERSION,\n DEEPSEEK_DEFAULT_BASE_URL,\n DEEPSEEK_DEFAULT_MAX_TOKENS,\n DEEPSEEK_DEFAULT_MAX_USES,\n DEEPSEEK_DEFAULT_MODEL,\n DeepSeekSearchProvider,\n} from '@deepseek-ai/dsh-web-search-deepseek'\nimport { ExaSearchProvider } from '@deepseek-ai/dsh-web-search-exa'\nimport { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http'\nimport { SEARCH_ENGINE_ID, SearchEngineProvider } from './search-engine.ts'\nimport { BochaSearchProvider, BraveSearchProvider, FirecrawlSearchProvider, SerperSearchProvider } from './rest-search.ts'\nimport { TavilySearchProvider } from './tavily.ts'\nimport type { WebSearchManager } from './manager.ts'\n\nexport function registerBuiltins(manager: WebSearchManager): () => void {\n const offEngine = manager.registerSearchProvider({\n id: SEARCH_ENGINE_ID, label: 'DuckDuckGo', billing: 'none',\n description: '无需注册。默认后端。',\n }, () => new SearchEngineProvider())\n const offDeepSeek = manager.registerSearchProvider({\n id: 'deepseek-official', label: 'DeepSeek 搜索',\n description: '使用已配置的 DeepSeek API Key。可能产生额外搜索费用。',\n defaultBaseURL: DEEPSEEK_DEFAULT_BASE_URL, credentialRef: 'DEEPSEEK_API_KEY',\n credentialShared: true, credentialHint: '与模型设置共用 Key', billing: 'model-and-tools',\n signupUrl: 'https://platform.deepseek.com/api_keys',\n }, options => new DeepSeekSearchProvider(() => ({\n apiKey: options.apiKey, baseURL: options.baseURL ?? DEEPSEEK_DEFAULT_BASE_URL,\n model: DEEPSEEK_DEFAULT_MODEL, apiVersion: DEEPSEEK_DEFAULT_API_VERSION,\n maxTokens: DEEPSEEK_DEFAULT_MAX_TOKENS, maxUses: DEEPSEEK_DEFAULT_MAX_USES,\n })))\n const offExa = manager.registerSearchProvider({\n id: 'exa', label: 'Exa', description: '独立 Search API。',\n defaultBaseURL: 'https://api.exa.ai', credentialRef: 'DSH_EDITOR_WEB_EXA_API_KEY', billing: 'request',\n signupUrl: 'https://dashboard.exa.ai/api-keys',\n }, options => new ExaSearchProvider({\n apiKey: options.apiKey ?? '', baseURL: options.baseURL ?? 'https://api.exa.ai',\n searchType: 'auto', highlightsPerResult: 1,\n }))\n const offBrave = manager.registerSearchProvider({\n id: 'brave', label: 'Brave', description: 'Brave Search API。',\n defaultBaseURL: 'https://api.search.brave.com', credentialRef: 'DSH_EDITOR_WEB_BRAVE_API_KEY', billing: 'request',\n signupUrl: 'https://api.search.brave.com',\n }, options => new BraveSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offBocha = manager.registerSearchProvider({\n id: 'bocha', label: '博查', description: '中文网页搜索 API。',\n defaultBaseURL: 'https://api.bochaai.com', credentialRef: 'DSH_EDITOR_WEB_BOCHA_API_KEY', billing: 'request',\n signupUrl: 'https://open.bochaai.com',\n }, options => new BochaSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offSerper = manager.registerSearchProvider({\n id: 'serper', label: 'Serper', description: 'Google 结果的 Search API。',\n defaultBaseURL: 'https://google.serper.dev', credentialRef: 'DSH_EDITOR_WEB_SERPER_API_KEY', billing: 'request',\n signupUrl: 'https://serper.dev',\n }, options => new SerperSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offFirecrawl = manager.registerSearchProvider({\n id: 'firecrawl', label: 'Firecrawl', description: '网页搜索与提取 API。',\n defaultBaseURL: 'https://api.firecrawl.dev', credentialRef: 'DSH_EDITOR_WEB_FIRECRAWL_API_KEY', billing: 'request',\n signupUrl: 'https://www.firecrawl.dev',\n }, options => new FirecrawlSearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offTavily = manager.registerSearchProvider({\n id: 'tavily', label: 'Tavily', defaultBaseURL: 'https://api.tavily.com',\n description: 'Search API;固定 basic 检索,不自动升级搜索深度。',\n credentialRef: 'DSH_EDITOR_WEB_TAVILY_API_KEY', billing: 'request',\n signupUrl: 'https://app.tavily.com',\n }, options => new TavilySearchProvider({ apiKey: options.apiKey ?? '', baseURL: options.baseURL }))\n const offHttp = manager.registerFetchProvider({\n id: 'http', label: 'HTTP 读取', billing: 'none',\n description: '本机直连公开网页,无需 Key,不产生搜索费用。',\n }, options => new HttpFetchProvider({\n maxResponseBytes: 5_000_000, maxBodyChars: options.maxFetchChars,\n timeoutMs: options.timeoutMs, maxRedirects: 5, userAgent: 'dsh-editor/managed-web',\n }))\n return () => {\n offHttp(); offTavily(); offFirecrawl(); offSerper(); offBocha(); offBrave(); offExa(); offDeepSeek(); offEngine()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { RpcResult } from './contracts.ts'\nexport interface HostRpcContext {\n webServer: { register(route: { kind: 'prefix'; path: string; handler(req: IncomingMessage, res: ServerResponse): void | Promise<void> }): () => void }\n connection: { requestRejection?: (request: IncomingMessage) => number | undefined }\n}\nexport function registerHostRpc(ctx: HostRpcContext, channel: string,\n call: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<RpcResult>): () => void {\n return ctx.webServer.register({ kind: 'prefix', path: channel, handler: async (req, res) => {\n // Require the host's origin/auth policy. Missing policy must not expose settings.\n if (!ctx.connection.requestRejection) { res.writeHead(503); res.end(); return }\n const rejection = ctx.connection.requestRejection(req)\n if (rejection !== undefined) { res.writeHead(rejection); res.end(); return }\n const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname\n const endpoint = pathname.startsWith(`${channel}/`) ? pathname.slice(channel.length + 1) : ''\n if (req.method !== 'POST' || !/^[a-z.]+$/.test(endpoint)) { res.writeHead(404); res.end(); return }\n if (String(req.headers['content-type'] ?? '').split(';')[0]?.trim() !== 'application/json') {\n res.writeHead(415); res.end(); return\n }\n const controller = new AbortController()\n res.on('close', () => { if (!res.writableEnded) controller.abort() })\n try {\n const chunks: Buffer[] = []\n let size = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buffer.length\n if (size > 64 * 1024) { res.writeHead(413, { connection: 'close' }); res.end(); req.destroy(); return }\n chunks.push(buffer)\n }\n let request: Record<string, unknown>\n try {\n const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error()\n request = parsed as Record<string, unknown>\n } catch { res.writeHead(400); res.end(); return }\n if (request.type !== 'client-request' || request.method !== endpoint || typeof request.rpcId !== 'string') {\n res.writeHead(400); res.end(); return\n }\n const result = await call(endpoint, request.payload, controller.signal)\n if (!res.destroyed) {\n res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n res.end(JSON.stringify({ type: 'server-response', rpcId: request.rpcId, result }))\n }\n } catch {\n if (!res.destroyed) { res.writeHead(500); res.end('network settings request failed') }\n }\n } })\n}\n","import { WebError, type WebFetchProvider, type WebSearchProvider } from '@deepseek-ai/dsh-web'\nimport {\n defaultSettings, migrateSearchOrder, pickActiveSearch, providerKey, resolveSearchOrder, validateBaseURL,\n type FetchProviderFactory, type ProviderDescriptor, type ProviderKind, type ProviderOptions,\n type SearchProviderFactory, type WebSettings, type WebStatus,\n} from './contracts.ts'\n\nexport interface WebRegistry {\n registerSearchProvider(provider: WebSearchProvider): () => void\n registerFetchProvider(provider: WebFetchProvider): () => void\n}\nexport interface ManagerOptions {\n web: WebRegistry\n initial?: Omit<WebSettings, 'searchOrder'> & { searchOrder?: string[] }\n resolveCredential(ref: string): Promise<string | undefined>\n save(settings: WebSettings): Promise<void>\n}\ntype Entry = {\n descriptor: ProviderDescriptor; kind: ProviderKind; configured: boolean\n calls: number; failures: number; unregister(): void; active: Set<AbortController>\n}\nfunction fail(code: string, message: string): never { throw new WebError(message, code) }\nfunction bounded(value: number, min: number, max: number, label: string): number {\n if (!Number.isInteger(value) || value < min || value > max) fail('WEB_INVALID_CONFIG', `${label}须为 ${min} 至 ${max} 的整数。`)\n return value\n}\n/** Enforce cancellation even when an extension ignores its cooperative signal. */\nasync function abortable<T>(work: () => Promise<T>, signal: AbortSignal): Promise<T> {\n signal.throwIfAborted()\n let off = () => {}\n const aborted = new Promise<never>((_, reject) => {\n const onAbort = () => reject(new WebError('网络请求已取消或超时。', 'WEB_ABORTED'))\n signal.addEventListener('abort', onAbort, { once: true })\n off = () => signal.removeEventListener('abort', onAbort)\n })\n try {\n return await Promise.race([Promise.resolve().then(() => { signal.throwIfAborted(); return work() }), aborted])\n } finally { off() }\n}\n\n/** Manages consent and provider lifecycle. Selection and execution stay in ctx.web. */\nexport class WebSearchManager {\n private settings: WebSettings\n private entries = new Map<string, Entry>()\n private listeners = new Set<() => void>()\n private pending = Promise.resolve()\n private epoch = 0\n private suspended = false\n private disposed = false\n private storageFailed = false\n\n constructor(private readonly options: ManagerOptions) {\n const initial = structuredClone(options.initial ?? defaultSettings())\n this.settings = { ...defaultSettings(), ...initial, searchOrder: migrateSearchOrder(initial) }\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n private notify(): void {\n for (const listener of this.listeners) {\n try { listener() } catch { /* observer failure cannot authorize a request */ }\n }\n }\n private knownSearchIds(): string[] {\n return [...this.entries.values()].filter(entry => entry.kind === 'search').map(entry => entry.descriptor.id)\n }\n private keyed(id: string): boolean {\n return Boolean(this.entries.get(providerKey('search', id))?.descriptor.credentialRef)\n }\n private configured(id: string): boolean {\n return Boolean(this.entries.get(providerKey('search', id))?.configured)\n }\n private searchOrder(settings: WebSettings = this.settings): string[] {\n return resolveSearchOrder(this.knownSearchIds(), settings, id => this.keyed(id))\n }\n private activeSearchId(settings: WebSettings = this.settings): string {\n return pickActiveSearch(this.searchOrder(settings), id => this.configured(id))\n }\n private selected(entry: Entry): boolean {\n if (this.disposed || this.suspended || !entry.configured) return false\n if (entry.kind === 'fetch') {\n return this.settings.fetchEnabled\n && this.settings.fetchProvider === entry.descriptor.id\n && this.entries.get(providerKey('fetch', entry.descriptor.id)) === entry\n }\n return this.settings.searchEnabled && this.activeSearchId() === entry.descriptor.id\n }\n status(): WebStatus {\n const entries = [...this.entries.values()]\n return {\n settings: structuredClone(this.settings), storageFailed: this.storageFailed,\n searchActive: entries.some(entry => entry.kind === 'search' && this.selected(entry)),\n fetchActive: entries.some(entry => entry.kind === 'fetch' && this.selected(entry)),\n providers: entries.map(entry => ({\n ...entry.descriptor, kind: entry.kind, configured: entry.configured,\n baseURL: this.settings.endpoints[providerKey(entry.kind, entry.descriptor.id)] ?? entry.descriptor.defaultBaseURL,\n calls: entry.calls, failures: entry.failures,\n })).sort((a, b) => providerKey(a.kind, a.id).localeCompare(providerKey(b.kind, b.id))),\n }\n }\n async refresh(): Promise<WebStatus> {\n const epoch = this.epoch\n await Promise.all([...this.entries.entries()].map(async ([key, entry]) => {\n let configured = !entry.descriptor.credentialRef\n if (entry.descriptor.credentialRef) {\n try { configured = Boolean((await this.options.resolveCredential(entry.descriptor.credentialRef))?.trim()) }\n catch { configured = false }\n }\n if (this.disposed || epoch !== this.epoch || this.entries.get(key) !== entry) return\n if (entry.configured !== configured) {\n entry.configured = configured\n if (!configured) this.abort(entry)\n }\n }))\n this.notify()\n return this.status()\n }\n private abort(entry?: Entry): void {\n for (const target of entry ? [entry] : this.entries.values()) {\n for (const controller of target.active) controller.abort()\n }\n }\n private add(kind: ProviderKind, descriptor: ProviderDescriptor): Entry {\n if (this.disposed) fail('WEB_DISABLED', '网络搜索插件已停止。')\n const key = providerKey(kind, descriptor.id)\n if (!/^[a-z][a-z0-9-]{0,63}$/.test(descriptor.id) || !descriptor.label.trim()) fail('WEB_INVALID_PROVIDER', '供应商标识或名称无效。')\n if (descriptor.credentialRef && !/^[A-Z][A-Z0-9_]{1,127}$/.test(descriptor.credentialRef)) fail('WEB_INVALID_PROVIDER', '供应商凭据引用无效。')\n if (descriptor.defaultBaseURL) validateBaseURL(descriptor.defaultBaseURL)\n for (const [value, label] of [[descriptor.signupUrl, '注册地址'], [descriptor.pricingUrl, '费用说明地址']] as const) {\n if (!value) continue\n try {\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password) fail('WEB_INVALID_PROVIDER', `${label}须为 HTTPS。`)\n } catch { fail('WEB_INVALID_PROVIDER', `${label}须为 HTTPS。`) }\n }\n if (this.entries.has(key)) fail('WEB_DUPLICATE_PROVIDER', '供应商标识已注册。')\n const entry: Entry = {\n descriptor: Object.freeze({ ...descriptor }), kind, configured: !descriptor.credentialRef,\n calls: 0, failures: 0, unregister() {}, active: new Set(),\n }\n this.entries.set(key, entry)\n return entry\n }\n private registration(entry: Entry, register: () => () => void): () => void {\n const key = providerKey(entry.kind, entry.descriptor.id)\n try { entry.unregister = register() }\n catch (error) { this.entries.delete(key); throw error }\n void this.refresh()\n let removed = false\n return () => {\n if (removed) return\n removed = true\n this.abort(entry)\n this.entries.delete(key)\n entry.unregister()\n this.notify()\n }\n }\n registerSearchProvider(descriptor: ProviderDescriptor, factory: SearchProviderFactory): () => void {\n const entry = this.add('search', descriptor)\n return this.registration(entry, () => this.options.web.registerSearchProvider({\n id: descriptor.id, available: () => this.selected(entry),\n search: (request, signal) => {\n if (typeof request.query !== 'string' || !request.query.trim() || request.query.length > 4000) fail('WEB_INVALID_REQUEST', '查询须为 1 至 4000 字符。')\n const count = Math.min(bounded(request.maxResults ?? this.settings.maxResults, 1, 100, '结果数'), this.settings.maxResults)\n return this.execute(entry, signal, async (options, combined) => {\n const provider = factory(options)\n if (!provider.available()) fail('WEB_PROVIDER_ERROR', '供应商配置不可用。')\n const result = await provider.search({ query: request.query.trim(), maxResults: count }, combined)\n const sources = result.sources.slice(0, count).filter(source => {\n try { const url = new URL(source.url); return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password } catch { return false }\n }).map(source => ({ ...source, title: source.title?.slice(0, 1000), snippet: source.snippet?.slice(0, 8000) }))\n return { ...result, content: result.content?.slice(0, 40_000), sources,\n truncated: result.truncated || result.sources.length > count }\n })\n },\n }))\n }\n registerFetchProvider(descriptor: ProviderDescriptor, factory: FetchProviderFactory): () => void {\n const entry = this.add('fetch', descriptor)\n return this.registration(entry, () => this.options.web.registerFetchProvider({\n id: descriptor.id, available: () => this.selected(entry),\n fetch: (request, signal) => this.execute(entry, signal, async (options, combined) => {\n const provider = factory(options)\n if (!provider.available()) fail('WEB_PROVIDER_ERROR', '供应商配置不可用。')\n const result = await provider.fetch(request, combined)\n const content = result.body.content.slice(0, options.maxFetchChars)\n return { ...result, body: { ...result.body, content },\n truncated: result.truncated || content.length !== result.body.content.length }\n }),\n }))\n }\n private async execute<T>(entry: Entry, signal: AbortSignal | undefined,\n run: (options: ProviderOptions, signal: AbortSignal) => Promise<T>): Promise<T> {\n if (!this.selected(entry)) fail('WEB_DISABLED', '请在设置 → 网络搜索中配置凭据并主动启用。')\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.settings.timeoutMs)\n const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal\n const epoch = this.epoch\n entry.active.add(controller)\n let started = false\n try {\n const ref = entry.descriptor.credentialRef\n const apiKey = ref ? await abortable(() => this.options.resolveCredential(ref), combined) : undefined\n if (ref && !apiKey?.trim()) {\n entry.configured = false\n this.abort(entry)\n this.notify()\n fail('WEB_CREDENTIAL_MISSING', '搜索凭据已删除或不可用,请重新配置。')\n }\n if (epoch !== this.epoch || !this.selected(entry)) fail('WEB_DISABLED', '网络访问设置已改变,请重新发起请求。')\n const options = {\n apiKey, baseURL: this.settings.endpoints[providerKey(entry.kind, entry.descriptor.id)] ?? entry.descriptor.defaultBaseURL,\n timeoutMs: this.settings.timeoutMs, maxFetchChars: this.settings.maxFetchChars,\n }\n started = true\n entry.calls += 1\n const result = await abortable(() => run(options, combined), combined)\n if (epoch !== this.epoch || !this.selected(entry)) fail('WEB_ABORTED', '网络请求已取消。')\n return result\n } catch (error) {\n if (started) entry.failures += 1\n const code = error instanceof WebError ? error.code : undefined\n if (code === 'WEB_CREDENTIAL_MISSING') throw new WebError('搜索凭据不可用,请重新配置。', 'WEB_CREDENTIAL_MISSING')\n if (code === 'WEB_DISABLED') throw new WebError('网络搜索未启用或设置已改变。', 'WEB_DISABLED')\n if (combined.aborted || code === 'WEB_ABORTED') throw new WebError('网络请求已取消或超时。', 'WEB_ABORTED')\n // Do not forward raw provider responses, token-bearing errors, or causes.\n throw new WebError('网络供应商请求失败,请检查凭据、额度和连接。未切换到其他供应商。', 'WEB_PROVIDER_ERROR')\n } finally {\n clearTimeout(timeout)\n entry.active.delete(controller)\n }\n }\n update(next: Omit<WebSettings, 'revision'>, expectedRevision: number): Promise<WebStatus> {\n const task = this.pending.then(async () => {\n if (this.disposed) fail('WEB_DISABLED', '网络搜索插件已停止。')\n if (expectedRevision !== this.settings.revision) fail('WEB_CONFIG_CONFLICT', '设置已被其他页面修改,请刷新后重试。')\n const proposed: WebSettings = { ...structuredClone(next), revision: expectedRevision + 1 }\n bounded(proposed.maxResults, 1, 20, '结果上限')\n bounded(proposed.maxQueries, 1, 5, '查询上限')\n bounded(proposed.timeoutMs, 1000, 120_000, '超时毫秒数')\n bounded(proposed.maxFetchChars, 1000, 200_000, '正文长度')\n for (const [key, value] of Object.entries(proposed.endpoints)) {\n const entry = this.entries.get(key)\n if (entry && !entry.descriptor.defaultBaseURL) fail('WEB_INVALID_CONFIG', '供应商不支持自定义地址。')\n try { proposed.endpoints[key] = validateBaseURL(value) }\n catch { fail('WEB_INVALID_CONFIG', '供应商地址须为无凭据、无查询参数的 HTTPS 地址。') }\n }\n await this.refresh()\n proposed.searchOrder = this.searchOrder(proposed)\n proposed.searchProvider = this.activeSearchId(proposed)\n if (proposed.searchEnabled && !proposed.searchProvider) {\n fail('WEB_CREDENTIAL_MISSING', '请先选择已安装的供应商并填写有效凭据。')\n }\n if (proposed.searchEnabled && this.entries.get(providerKey('fetch', 'http'))) {\n proposed.fetchEnabled = true\n proposed.fetchProvider = 'http'\n }\n if (!proposed.searchEnabled) proposed.fetchEnabled = false\n if (proposed.fetchEnabled) {\n const entry = this.entries.get(providerKey('fetch', proposed.fetchProvider))\n if (!entry) proposed.fetchEnabled = false\n else if (!entry.configured) fail('WEB_CREDENTIAL_MISSING', '请先选择已安装的供应商并填写有效凭据。')\n }\n this.suspended = true\n this.epoch += 1\n this.abort()\n this.notify()\n try { await this.options.save(proposed) }\n catch {\n this.storageFailed = true\n this.notify()\n fail('WEB_CONFIG_SAVE_FAILED', '设置保存失败。为避免意外联网,本次运行已暂停网络访问。')\n }\n this.settings = proposed\n this.storageFailed = false\n this.suspended = false\n this.notify()\n return this.status()\n })\n this.pending = task.then(() => {}, () => {})\n return task\n }\n async dispose(): Promise<void> {\n this.disposed = true\n this.epoch += 1\n this.abort()\n this.notify()\n for (const entry of this.entries.values()) entry.unregister()\n this.entries.clear()\n this.listeners.clear()\n await this.pending\n }\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport { migrateSearchOrder } from './contracts.ts'\nexport const settingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n searchEnabled: z.boolean(), fetchEnabled: z.boolean(),\n searchProvider: z.string().max(64), fetchProvider: z.string().max(64),\n searchOrder: z.array(z.string().min(1).max(64)).max(32),\n maxResults: z.number().int().min(1).max(20), maxQueries: z.number().int().min(1).max(5),\n timeoutMs: z.number().int().min(1000).max(120_000), maxFetchChars: z.number().int().min(1000).max(200_000),\n endpoints: z.record(z.string().regex(/^(search|fetch):[a-z][a-z0-9-]{0,63}$/), z.string().max(2048))\n .refine(value => Object.keys(value).length <= 50),\n}).strict()\nexport const updateSchema = z.object({\n expectedRevision: z.number().int().nonnegative(), settings: settingsSchema.omit({ revision: true }),\n}).strict()\nexport const storedSettingsSchema = settingsSchema.extend({\n searchOrder: settingsSchema.shape.searchOrder.optional(),\n}).transform(settings => ({ ...settings, searchOrder: migrateSearchOrder(settings) }))\nexport const webSettingsDomain = defineDomain({\n name: 'dsh_editor_web_search', version: 1,\n tables: { settings: domainTable<string, z.output<typeof storedSettingsSchema>>(storedSettingsSchema) },\n})\n","import type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@deepseek-ai/dsh-credentials'\nimport { WebError } from '@deepseek-ai/dsh-web'\nimport { WEB_SEARCH_RPC_CHANNEL, type RpcResult } from './contracts.ts'\nimport { registerBuiltins } from './builtins.ts'\nimport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nimport { WebSearchManager } from './manager.ts'\nimport { updateSchema, webSettingsDomain } from './storage.ts'\nexport { WebSearchManager } from './manager.ts'\nexport type { ProviderDescriptor, ProviderOptions, SearchProviderFactory, FetchProviderFactory } from './contracts.ts'\nexport const name = '@klarkxy/dsh-web-search-manager'\nexport const inject = ['web', 'credentials', 'storageDomain', 'connection', 'webServer'] as const\n\ndeclare module '@deepseek-ai/cordis' { interface Context { webSearchManager: WebSearchManager } }\ntype Host = Context & HostRpcContext & {\n credentials: { resolve(ref: ReturnType<typeof credentialRef>): Promise<{ value: string } | undefined> }\n}\nexport async function apply(ctx: Context): Promise<void> {\n const host = ctx as Host\n const domain = await ctx.storageDomain.open(webSettingsDomain)\n const table = domain.table('settings')\n const manager = new WebSearchManager({\n web: ctx.web, initial: table.get('global'),\n resolveCredential: async ref => (await host.credentials.resolve(credentialRef(ref)))?.value,\n save: settings => table.put('global', settings),\n })\n ctx.effect(() => async () => { await manager.dispose(); await domain.close() }, 'web-search-manager.dispose')\n ctx.provide('webSearchManager', manager)\n ctx.effect(() => registerBuiltins(manager), 'web-search-manager.providers')\n ctx.effect(() => registerHostRpc(host, WEB_SEARCH_RPC_CHANNEL, async (endpoint, payload, signal): Promise<RpcResult> => {\n try {\n signal.throwIfAborted()\n if (endpoint === 'status') return { ok: true, value: await manager.refresh() }\n if (endpoint === 'update') {\n const parsed = updateSchema.safeParse(payload)\n if (!parsed.success) return { ok: false, error: { code: 'WEB_INVALID_CONFIG', message: '网络搜索设置格式无效。', details: {} } }\n return { ok: true, value: await manager.update(parsed.data.settings, parsed.data.expectedRevision) }\n }\n if (endpoint === 'test') {\n // Testing is explicit, uses the same authorized registry, and sends no manuscript text.\n const result = await ctx.web.search({ query: 'IANA example domains', maxResults: 1 }, signal)\n return { ok: true, value: { sources: result.sources.length } }\n }\n return { ok: false, error: { code: 'WEB_INVALID_REQUEST', message: '未知操作。', details: {} } }\n } catch (error) {\n return { ok: false, error: {\n code: error instanceof WebError ? error.code : 'WEB_REQUEST_FAILED',\n message: error instanceof WebError ? error.message : '网络设置操作失败,请刷新后重试。', details: {},\n } }\n }\n }), 'web-search-manager.rpc')\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klarkxy/dsh-web-search-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Manage web search providers and public-page fetching in DeepSeek Harness.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -67,23 +67,23 @@
|
|
|
67
67
|
]
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@deepseek-ai/dsh-storage-domain": "0.1.7-
|
|
71
|
-
"@deepseek-ai/dsh-web-search-deepseek": "0.1.7-
|
|
72
|
-
"@deepseek-ai/dsh-web-search-exa": "0.1.7-
|
|
73
|
-
"@deepseek-ai/dsh-web-fetch-http": "0.1.7-
|
|
70
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.7-rc.2",
|
|
71
|
+
"@deepseek-ai/dsh-web-search-deepseek": "0.1.7-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-web-search-exa": "0.1.7-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-web-fetch-http": "0.1.7-rc.2",
|
|
74
74
|
"zod": "^4.1.5"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
78
|
-
"@deepseek-ai/dsh-credentials": "0.1.7-
|
|
79
|
-
"@deepseek-ai/dsh-web": "0.1.7-
|
|
80
|
-
"@deepseek-ai/dsh-tool-web": "0.1.7-
|
|
77
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
78
|
+
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-web": "0.1.7-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-tool-web": "0.1.7-rc.2"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
84
|
-
"@deepseek-ai/dsh-credentials": "0.1.7-
|
|
85
|
-
"@deepseek-ai/dsh-web": "0.1.7-
|
|
86
|
-
"@deepseek-ai/dsh-tool-web": "0.1.7-
|
|
83
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
84
|
+
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-web": "0.1.7-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-tool-web": "0.1.7-rc.2",
|
|
87
87
|
"react": "^18.2.0",
|
|
88
88
|
"@types/react": "18.3.31"
|
|
89
89
|
},
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"registry": "https://registry.npmjs.org/"
|
|
113
113
|
},
|
|
114
114
|
"dshRelease": {
|
|
115
|
-
"contentHash": "sha256-
|
|
115
|
+
"contentHash": "sha256-5b908be3edc69572607bf0211aca943367994405a0c534660fda783fc9eb0219"
|
|
116
116
|
},
|
|
117
117
|
"scripts": {
|
|
118
118
|
"build": "tsdown && node ../../scripts/wrap-client.mjs @klarkxy/dsh-web-search-manager",
|