dsh-web-search-enhanced 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -1
- package/README.md +19 -8
- package/cordis.patch.yml +1 -1
- package/lib/client.js +28 -1
- package/lib/client.js.map +1 -1
- package/lib/index.js +45 -13
- package/lib/types/client/SearchSettingsCard.d.ts +1 -1
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/index.d.ts +4 -4
- package/lib/types/protocols.d.ts +8 -0
- package/lib/types/provider.d.ts +3 -2
- package/package.json +39 -30
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `dsh-web-search-enhanced` package will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.0.5] - 2026-09-11
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **DSH 0.1.5-rc.2 Exclusivity & Compatibility**: Upgraded engine requirement to `engines.dsh >= 0.1.5-rc.2` and all `@deepseek-ai/*` peerDependencies/devDependencies to `^0.1.5-rc.2`, dropping legacy support for `v0.1.2-rc.1`.
|
|
9
|
+
- **Default Model Upgrade**: Updated global `DEFAULT_MODEL` and profile patch fallback model from `deepseek-v4-flash` to `deepseek-flash` (DeepSeek-V41-Flash) to match upstream DSH 0.1.5 conventions.
|
|
10
|
+
- **Session Audit & Transparency (`recordRequest`)**: Implemented pre-dispatch request logging via `session.append('web/deepseek-search-llm-request')`, safely capturing resolved endpoints, protocol identifiers, API versions, and secret-free payloads for turn auditing and trajectory exports.
|
|
11
|
+
- **Client Action Command (`/search-config`)**: Added client-side `/search-config` action command leveraging DSH 0.1.5 `ActionSpec` (`kind: 'action'`) to provide instant one-click navigation directly to the Web Search Enhanced settings card without interrupting conversation flow.
|
|
12
|
+
- **Egress & Proxy Verification**: Expanded egress test suite to verify outbound request dispatching under proxy and custom fetcher environments.
|
|
13
|
+
|
|
14
|
+
## [0.0.4] - 2026-09-03
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **DSH 0.1.2-rc.1 Minimum Requirement & Compatibility**: Updated `engines.dsh` to `>= 0.1.2-rc.1` and `peerDependencies` across all `@deepseek-ai/dsh-*` packages to `^0.1.2-rc.1`.
|
|
18
|
+
- **RC-Only Support Policy**: Formally documented in `README.md` that the plugin exclusively supports DSH Release Candidate (RC) versions with a minimum requirement of `>= 0.1.2-rc.1`, and does not support intermediate alpha/beta/nightly snapshots.
|
|
19
|
+
- **Anthropic Endpoint Normalization**: Aligned with upstream PR #3451 specifications; added automatic normalization for Anthropic Messages `baseURL` with or without trailing `/v1` (e.g., `https://api.anthropic.com` safely resolves to `/v1/messages`), preventing 404 errors on official Anthropic endpoints and compatible gateways.
|
|
20
|
+
- **Out-of-the-Box Credential Fallback**: Added automatic fallback to `DEEPSEEK_API_KEY` when `apiKeyEnv` remains at default `WEB_SEARCH_ENHANCED_API` and the target endpoint is DeepSeek, enabling seamless drop-in replacement of the native search provider without redundant credential re-entry.
|
|
21
|
+
- **Client Build Defines Alignment**: Updated `tsdown.config.ts` define configuration to supply the root `import.meta.env` object alongside `import.meta.env.MODE`, matching upstream preset conventions and preventing property-probe runtime errors in bundled client libraries.
|
|
22
|
+
- **Egress & Compatibility Tests**: Added `tests/egress.spec.ts` and updated `tests/protocols.spec.ts` and `tests/plugin.spec.ts` to cover endpoint normalization and credential fallback behaviors.
|
|
23
|
+
|
|
5
24
|
## [0.0.3] - 2026-08-31
|
|
6
25
|
|
|
7
26
|
### Fixed
|
|
@@ -21,4 +40,4 @@ All notable changes to the `dsh-web-search-enhanced` package will be documented
|
|
|
21
40
|
- Initial release of `dsh-web-search-enhanced`.
|
|
22
41
|
- Support for Anthropic Messages, OpenAI Responses, and OpenAI Chat Completions search protocols.
|
|
23
42
|
- Follow mode (`current-session`) and fixed search configuration mode.
|
|
24
|
-
- DeepSeek Harness Web GUI settings card integration.
|
|
43
|
+
- DeepSeek Harness Web GUI settings card integration.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dsh-web-search-enhanced)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-
[-blueviolet)](https://github.com/deepseek-ai/deepseek-harness)
|
|
6
6
|
|
|
7
7
|
**dsh-web-search-enhanced** 是为 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 量身打造的增强型多协议联网搜索插件。
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- 🌐 **多协议全面兼容**:无缝支持 **Anthropic Messages v1** (`/messages`)、**OpenAI Responses API** (`/responses`) 与 **OpenAI Chat Completions API** (`/chat/completions`) 三大协议,兼容各类官方端点与第三方 / 自建 AI 网关。
|
|
25
25
|
- 🔄 **灵活的双路由模式**:
|
|
26
26
|
- **跟随当前会话 (`current-session`)**:自动继承当前会话正在使用的模型、提供方、调用协议与凭据,直接使用主模型供应商的搜索功能。
|
|
27
|
-
- **固定搜索路由 (`configured`)**:为联网搜索单独指定专用搜索模型(如 `deepseek-
|
|
27
|
+
- **固定搜索路由 (`configured`)**:为联网搜索单独指定专用搜索模型(如 `deepseek-flash`、`claude-3-7-sonnet`、`gpt-4o` 等),主对话与搜索模型分工明确。
|
|
28
28
|
- **智能平滑兜底 (`fallbackModel`)**:当跟随模式遇到未知或未配置的模型路由时,自动降级至备用模型,确保搜索永不断流。
|
|
29
29
|
- 🖥️ **Web 控制台可视化配置**:深度集成 DSH Web 端「设置 → 插件 → Web Search Enhanced」,支持中英文双语、即时表单校验、草稿暂存、一键保存与重置。
|
|
30
30
|
- 🔒 **凭据安全与隐私保护**:通过 DSH 原生 Credentials 机制安全写入并保管 API Key,公开设置文档绝不保存明文密钥;请求强制阻断携带凭据的恶意 HTTP 重定向。
|
|
@@ -32,6 +32,15 @@
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
## 📋 版本兼容与支持策略 (Compatibility & Requirements)
|
|
36
|
+
|
|
37
|
+
- **最低支持的 DSH 版本**:`>= 0.1.5-rc.2`
|
|
38
|
+
- **版本支持范围声明**:
|
|
39
|
+
> ⚠️ **重要声明**:**本插件仅对 DeepSeek Harness 的 Release Candidate (RC) 版本进行官方维护与兼容性支持(最低版本要求为 0.1.5-rc.2)**。
|
|
40
|
+
> 插件不提供对开发中的 Alpha / Beta / Nightly 构建版本的稳定性承诺与 API 兼容保证。在升级 DSH 或使用本插件时,请确保运行环境使用的是官方正式的 RC 发布版本。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
35
44
|
## 📦 快速安装
|
|
36
45
|
|
|
37
46
|
插件已正式发布至 npmjs,使用 DSH CLI 即可一键安装到 `web` Profile:
|
|
@@ -56,8 +65,10 @@ dsh web
|
|
|
56
65
|
|
|
57
66
|
### 方式一:Web 界面可视化配置(推荐)
|
|
58
67
|
|
|
68
|
+
> ⚡ **快捷命令**:在任何会话输入框中键入斜杠命令 `/search-config` 并回车,即可一键直达本插件设置面板!
|
|
69
|
+
|
|
59
70
|
1. 打开 DeepSeek Harness Web 界面(默认 `http://127.0.0.1:3080`)。
|
|
60
|
-
2.
|
|
71
|
+
2. 点击左侧导航栏的 **设置 (Settings)** → **插件 (Plugins)**(或使用 `/search-config`)。
|
|
61
72
|
3. 找到 **Web Search Enhanced (插件配置)** 卡片。
|
|
62
73
|
4. 根据需要选择**模型路由**、**API 协议**、**接口地址 (Base URL)** 与 **模型标识 (Model ID)**。
|
|
63
74
|
5. 在 **API Key** 输入框中输入对应的模型供应商 API 密钥(输入后仅用于安全保存,保存成功后输入框自动清空,不留明文痕迹)。
|
|
@@ -82,7 +93,7 @@ dsh web
|
|
|
82
93
|
modelMode: configured
|
|
83
94
|
protocol: anthropic-messages
|
|
84
95
|
baseURL: https://api.deepseek.com/anthropic/v1
|
|
85
|
-
model: deepseek-
|
|
96
|
+
model: deepseek-flash
|
|
86
97
|
apiKeyEnv: WEB_SEARCH_ENHANCED_API
|
|
87
98
|
maxTokens: 4096
|
|
88
99
|
```
|
|
@@ -98,7 +109,7 @@ dsh web
|
|
|
98
109
|
- **模型路由 (modelMode)**: `固定配置 (configured)`
|
|
99
110
|
- **API 协议 (protocol)**: `Anthropic Messages v1`
|
|
100
111
|
- **接口地址 (baseURL)**: `https://api.deepseek.com/anthropic/v1`
|
|
101
|
-
- **模型标识 (model)**: `deepseek-
|
|
112
|
+
- **模型标识 (model)**: `deepseek-flash`
|
|
102
113
|
- **API Key**: 填入你的 DeepSeek API Key
|
|
103
114
|
|
|
104
115
|
---
|
|
@@ -139,7 +150,7 @@ dsh web
|
|
|
139
150
|
### 5. 跟随当前会话模型(自动继承厂商搜索)
|
|
140
151
|
让搜索自动使用当前 Agent 会话正在对话的模型供应商与协议:
|
|
141
152
|
- **模型路由 (modelMode)**: `当前会话模型 (current-session)`
|
|
142
|
-
- **兜底搜索模型 (fallbackModel)**: `deepseek-
|
|
153
|
+
- **兜底搜索模型 (fallbackModel)**: `deepseek-flash`(当当前模型不支持搜索协议或未解析到凭据时自动平滑回退)
|
|
143
154
|
- **API 协议 / 接口地址 / API Key**: 自动跟随当前会话的 LLM 路由配置
|
|
144
155
|
|
|
145
156
|
---
|
|
@@ -151,7 +162,7 @@ dsh web
|
|
|
151
162
|
| `modelMode` | 模型路由 | `configured` | `configured` (固定配置)<br>`current-session` (跟随当前会话) | 决定搜索使用固定的独立模型,还是跟随当前会话的模型与协议。 |
|
|
152
163
|
| `protocol` | API 协议 | `anthropic-messages` | `anthropic-messages`<br>`openai-responses`<br>`openai-chat-completions` | 固定路由或兜底路由使用的上游 HTTP 协议。 |
|
|
153
164
|
| `baseURL` | 接口基础地址 | `https://api.deepseek.com/anthropic/v1` | 绝对 HTTP(S) URL | 上游接口的基础 URL。若末尾未包含协议端点后缀,插件会自动补全。 |
|
|
154
|
-
| `model` | 模型标识 | `deepseek-
|
|
165
|
+
| `model` | 模型标识 | `deepseek-flash` | 字符串 | 固定路由使用的模型 ID,或无会话模型时的默认模型。 |
|
|
155
166
|
| `fallbackModel` | 兜底搜索模型 | 留空(默认同 `model`) | 字符串 | 仅跟随模式:当无法解析当前会话路由时所采用的兜底模型。 |
|
|
156
167
|
| `apiKeyEnv` | API Key 环境变量 | `WEB_SEARCH_ENHANCED_API` | 环境变量名 | 读取 API Key 的凭据引用名称。 |
|
|
157
168
|
| `apiKey` | API Key | 无 | 字符串(密钥) | Web 设置界面提供的一次性保存输入框,安全存入 DSH 凭据中心后自动清空。 |
|
|
@@ -238,4 +249,4 @@ npx vitest run
|
|
|
238
249
|
|
|
239
250
|
## 📄 开源许可证
|
|
240
251
|
|
|
241
|
-
本项目基于 [MIT License](LICENSE) 开源。
|
|
252
|
+
本项目基于 [MIT License](LICENSE) 开源。
|
package/cordis.patch.yml
CHANGED
package/lib/client.js
CHANGED
|
@@ -27,7 +27,7 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
modelMode: value?.modelMode ?? "configured",
|
|
28
28
|
protocol: value?.protocol ?? "anthropic-messages",
|
|
29
29
|
baseURL: value?.baseURL ?? "https://api.deepseek.com/anthropic/v1",
|
|
30
|
-
model: value?.model ?? "deepseek-
|
|
30
|
+
model: value?.model ?? "deepseek-flash",
|
|
31
31
|
fallbackModel: value?.fallbackModel ?? "",
|
|
32
32
|
apiKeyEnv: value?.apiKeyEnv ?? "WEB_SEARCH_ENHANCED_API",
|
|
33
33
|
apiVersion: value?.apiVersion ?? "2023-06-01",
|
|
@@ -365,6 +365,8 @@ window.__ModuleLoader__.load({
|
|
|
365
365
|
return (0, react_jsx_runtime.jsxs)("li", {
|
|
366
366
|
className: "wse-card",
|
|
367
367
|
"data-open": open,
|
|
368
|
+
"data-plugin-key": "web-search-enhanced",
|
|
369
|
+
"data-wse-card": "true",
|
|
368
370
|
children: [
|
|
369
371
|
(0, react_jsx_runtime.jsx)("style", { children: cardCss }),
|
|
370
372
|
(0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -760,6 +762,8 @@ window.__ModuleLoader__.load({
|
|
|
760
762
|
contextLow: "Low",
|
|
761
763
|
contextMedium: "Medium",
|
|
762
764
|
contextHigh: "High",
|
|
765
|
+
commandLabel: "Search settings",
|
|
766
|
+
commandDescription: "Configure enhanced web search model route and parameters",
|
|
763
767
|
save: "Save",
|
|
764
768
|
saving: "Saving…",
|
|
765
769
|
reset: "Reset to Profile",
|
|
@@ -819,6 +823,8 @@ window.__ModuleLoader__.load({
|
|
|
819
823
|
contextLow: "低",
|
|
820
824
|
contextMedium: "中",
|
|
821
825
|
contextHigh: "高",
|
|
826
|
+
commandLabel: "搜索设置",
|
|
827
|
+
commandDescription: "配置增强联网搜索的模型路由与参数",
|
|
822
828
|
save: "保存",
|
|
823
829
|
saving: "正在保存…",
|
|
824
830
|
reset: "重置为 Profile 配置",
|
|
@@ -861,6 +867,27 @@ window.__ModuleLoader__.load({
|
|
|
861
867
|
t
|
|
862
868
|
})
|
|
863
869
|
}, SearchSettingsCard));
|
|
870
|
+
ctx.inject(["commandUi"], (scope) => {
|
|
871
|
+
const commandUi = scope.commandUi;
|
|
872
|
+
if (typeof commandUi?.register === "function") scope.effect(() => commandUi.register({
|
|
873
|
+
name: "search-config",
|
|
874
|
+
label: () => t("commandLabel"),
|
|
875
|
+
description: () => t("commandDescription"),
|
|
876
|
+
available: () => true,
|
|
877
|
+
ui: {
|
|
878
|
+
kind: "action",
|
|
879
|
+
run: () => {
|
|
880
|
+
document.querySelector("[data-slot=\"sidebar.settings\"] button")?.click();
|
|
881
|
+
setTimeout(() => {
|
|
882
|
+
document.querySelector("[data-wse-card=\"true\"]")?.scrollIntoView({
|
|
883
|
+
behavior: "smooth",
|
|
884
|
+
block: "center"
|
|
885
|
+
});
|
|
886
|
+
}, 200);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}), "web-search-enhanced: /search-config action command");
|
|
890
|
+
});
|
|
864
891
|
}
|
|
865
892
|
//#endregion
|
|
866
893
|
exports.apply = apply;
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useSyncExternalStore","useMemo","useState","useId","_jsxs","_jsx","_Fragment"],"sources":["types/client/SearchSettingsCard.js","types/client/locales.js","types/client/index.js"],"sourcesContent":["import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from \"react/jsx-runtime\";\nimport { useEffect, useId, useMemo, useState, useSyncExternalStore } from 'react';\nconst editableFields = ['modelMode', 'protocol', 'baseURL', 'model', 'fallbackModel', 'apiKeyEnv', 'apiVersion', 'toolIdentifier', 'maxTokens', 'maxUses', 'chatSearchMode', 'searchContextSize'];\n/** Convert resolved settings into a stable form draft. */\nexport function draftFrom(value) {\n return {\n modelMode: value?.modelMode ?? 'configured',\n protocol: value?.protocol ?? 'anthropic-messages',\n baseURL: value?.baseURL ?? 'https://api.deepseek.com/anthropic/v1',\n model: value?.model ?? 'deepseek-v4-flash',\n fallbackModel: value?.fallbackModel ?? '',\n apiKeyEnv: value?.apiKeyEnv ?? 'WEB_SEARCH_ENHANCED_API',\n apiVersion: value?.apiVersion ?? '2023-06-01',\n toolIdentifier: value?.toolIdentifier ?? '',\n maxTokens: String(value?.maxTokens ?? 4096),\n maxUses: String(value?.maxUses ?? 5),\n chatSearchMode: value?.chatSearchMode ?? 'search-model',\n searchContextSize: value?.searchContextSize ?? '',\n };\n}\n/** Validate a draft and return field-specific locale keys. */\nexport async function saveCredential(credentials, ref, value) {\n const secret = value.trim();\n if (secret.length === 0)\n return false;\n const result = await credentials.set(ref.trim(), secret);\n if (!result.ok)\n throw new Error(result.error.message);\n return true;\n}\nexport function validateDraft(draft) {\n const errors = {};\n try {\n const url = new URL(draft.baseURL);\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n errors.baseURL = 'invalidURL';\n }\n catch {\n errors.baseURL = 'invalidURL';\n }\n if (draft.model.trim().length === 0)\n errors.model = 'invalidRequired';\n if (draft.apiKeyEnv.trim().length === 0)\n errors.apiKeyEnv = 'invalidRequired';\n if (draft.apiKeyEnv.trim().length > 0 && !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(draft.apiKeyEnv.trim()))\n errors.apiKeyEnv = 'invalidCredentialRef';\n if (draft.apiVersion.trim().length === 0)\n errors.apiVersion = 'invalidRequired';\n if (draft.toolIdentifier.length > 0 && !/^[A-Za-z][A-Za-z0-9_.:-]{0,127}$/u.test(draft.toolIdentifier))\n errors.toolIdentifier = 'invalidIdentifier';\n if (!positiveInteger(draft.maxTokens))\n errors.maxTokens = 'invalidInteger';\n if (!positiveInteger(draft.maxUses))\n errors.maxUses = 'invalidInteger';\n return errors;\n}\nconst cardCss = `\n.wse-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n transition: border-color .16s, background .16s;\n}\n.wse-card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n.wse-card[data-open='true'] {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.wse-header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n.wse-header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n.wse-head {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.wse-name {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n.wse-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 1.5;\n}\n.wse-pending {\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n border-radius: 999px;\n flex: none;\n padding: 1px 8px;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n}\n.wse-chevron {\n box-sizing: border-box;\n flex: none;\n width: 8px;\n height: 8px;\n margin-right: 3px;\n border-right: 1.5px solid var(--dsw-alias-label-tertiary);\n border-bottom: 1.5px solid var(--dsw-alias-label-tertiary);\n transform: rotate(45deg);\n transition: transform .16s;\n}\n.wse-card[data-open='true'] .wse-chevron {\n transform: rotate(225deg);\n}\n.wse-body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding-bottom: 8px;\n}\n.wse-fields {\n display: flex;\n flex-direction: column;\n}\n.wse-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 0;\n}\n.wse-field + .wse-field {\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.wse-label {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n}\n.wse-input, .wse-select {\n box-sizing: border-box;\n width: 100%;\n height: 34px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 0 12px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n outline: none;\n transition: border-color .16s, background .16s;\n}\n.wse-input:focus-visible, .wse-select:focus-visible,\n.wse-input:focus, .wse-select:focus {\n border-color: var(--dsw-alias-brand-primary);\n outline: none;\n}\n.wse-input[aria-invalid='true'] {\n border-color: var(--dsw-alias-label-error);\n}\n.wse-input:disabled, .wse-select:disabled {\n cursor: default;\n color: var(--dsw-alias-label-tertiary);\n opacity: .55;\n}\n.wse-hint, .wse-error, .wse-readonly, .wse-failed {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n}\n.wse-hint {\n color: var(--dsw-alias-label-tertiary);\n}\n.wse-error, .wse-failed {\n color: var(--dsw-alias-label-error);\n}\n.wse-readonly {\n margin: 12px 0 0;\n color: var(--dsw-alias-label-tertiary);\n}\n.wse-footer {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 4px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n}\n.wse-failed {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n.wse-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n}\n.wse-button {\n appearance: none;\n font: inherit;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 5px 14px;\n font-size: 13px;\n line-height: 1.5;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n transition: color .16s, border-color .16s, background .16s, opacity .16s;\n}\n.wse-button:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n background: var(--dsw-alias-bg-layer-4);\n}\n.wse-button:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n.wse-button:disabled {\n opacity: .4;\n cursor: default;\n}\n.wse-save {\n border-color: transparent;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-layer-3));\n}\n.wse-save:hover:not(:disabled) {\n background: var(--dsw-alias-label-primary);\n opacity: .9;\n}\n`;\nexport function SearchSettingsCard({ scope, credentials, t }) {\n const snapshot = useSyncExternalStore(scope.subscribe.bind(scope), scope.getSnapshot.bind(scope));\n const resolved = useMemo(() => draftFrom(snapshot.value), [snapshot.value]);\n const [draft, setDraft] = useState(resolved);\n const [open, setOpen] = useState(false);\n const [saving, setSaving] = useState(false);\n const [failed, setFailed] = useState(false);\n const [hasDraft, setHasDraft] = useState(false);\n const [resetToProfile, setResetToProfile] = useState(false);\n const [apiKey, setApiKey] = useState('');\n const [credentialConfigured, setCredentialConfigured] = useState(false);\n const bodyId = useId();\n useEffect(() => { if (!saving && !hasDraft)\n setDraft(resolved); }, [hasDraft, resolved, saving]);\n useEffect(() => {\n const ref = draft.apiKeyEnv.trim();\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(ref)) {\n setCredentialConfigured(false);\n return;\n }\n let active = true;\n void credentials.describe([ref]).then(result => {\n if (active)\n setCredentialConfigured(result.ok && result.value[ref]?.configured === true);\n }).catch(() => { if (active)\n setCredentialConfigured(false); });\n return () => { active = false; };\n }, [credentials, draft.apiKeyEnv]);\n const errors = useMemo(() => validateDraft(draft), [draft]);\n const dirty = resetToProfile || apiKey.length > 0 || JSON.stringify(draft) !== JSON.stringify(resolved);\n const invalid = Object.keys(errors).length > 0;\n const disabled = snapshot.status !== 'ready' || !snapshot.writable || saving;\n const edit = (field, value) => {\n setDraft(current => ({ ...current, [field]: value }));\n setResetToProfile(false);\n setHasDraft(true);\n setFailed(false);\n };\n const save = async () => {\n if (disabled || !dirty || invalid)\n return;\n setSaving(true);\n setFailed(false);\n try {\n if (await saveCredential(credentials, draft.apiKeyEnv, apiKey))\n setCredentialConfigured(true);\n if (resetToProfile) {\n for (const field of editableFields)\n await scope.unset(field);\n }\n else {\n await scope.set('modelMode', draft.modelMode);\n await scope.set('protocol', draft.protocol);\n await scope.set('baseURL', draft.baseURL.trim());\n await scope.set('model', draft.model.trim());\n if (draft.fallbackModel.trim().length === 0)\n await scope.unset('fallbackModel');\n else\n await scope.set('fallbackModel', draft.fallbackModel.trim());\n await scope.set('apiKeyEnv', draft.apiKeyEnv.trim());\n await scope.set('apiVersion', draft.apiVersion.trim());\n if (draft.toolIdentifier.trim().length === 0)\n await scope.unset('toolIdentifier');\n else\n await scope.set('toolIdentifier', draft.toolIdentifier.trim());\n await scope.set('maxTokens', Number(draft.maxTokens));\n await scope.set('maxUses', Number(draft.maxUses));\n await scope.set('chatSearchMode', draft.chatSearchMode);\n if (draft.searchContextSize === '')\n await scope.unset('searchContextSize');\n else\n await scope.set('searchContextSize', draft.searchContextSize);\n }\n setApiKey('');\n setHasDraft(false);\n setResetToProfile(false);\n setOpen(false);\n }\n catch {\n setFailed(true);\n }\n finally {\n setSaving(false);\n }\n };\n const reset = () => { setApiKey(''); setDraft(draftFrom(asSearchSettings(snapshot.base))); setResetToProfile(true); setHasDraft(true); setFailed(false); };\n const discard = () => { setApiKey(''); setDraft(resolved); setResetToProfile(false); setHasDraft(false); setFailed(false); };\n if (snapshot.status === 'unavailable')\n return null;\n const toolHint = draft.protocol === 'anthropic-messages' ? 'toolHintAnthropic' : draft.protocol === 'openai-responses' ? 'toolHintResponses' : draft.chatSearchMode === 'search-model' ? 'toolHintChatOfficial' : 'toolHintChatVendor';\n return _jsxs(\"li\", { className: \"wse-card\", \"data-open\": open, children: [_jsx(\"style\", { children: cardCss }), _jsxs(\"button\", { type: \"button\", className: \"wse-header\", \"aria-expanded\": open, \"aria-controls\": bodyId, \"aria-label\": `${t(open ? 'collapse' : 'expand')}: ${t('title')}`, onClick: () => { setOpen(value => !value); }, children: [_jsxs(\"span\", { className: \"wse-head\", children: [_jsx(\"span\", { className: \"wse-name\", children: t('title') }), _jsx(\"span\", { className: \"wse-desc\", children: t('description') })] }), dirty ? _jsx(\"span\", { className: \"wse-pending\", children: t('unsaved') }) : null, _jsx(\"span\", { className: \"wse-chevron\", \"aria-hidden\": \"true\" })] }), open ? _jsxs(\"div\", { className: \"wse-body\", id: bodyId, children: [!snapshot.writable && snapshot.status === 'ready' ? _jsx(\"p\", { className: \"wse-readonly\", role: \"status\", children: t('readOnly') }) : null, _jsxs(\"div\", { className: \"wse-fields\", children: [_jsx(SelectField, { label: t('modelMode'), hint: t('modelModeHint'), value: draft.modelMode, disabled: disabled, onChange: value => { edit('modelMode', value); }, options: [[\"configured\", t('configuredMode')], [\"current-session\", t('currentSessionMode')]] }), _jsx(SelectField, { label: t('protocol'), hint: t('protocolHint'), value: draft.protocol, disabled: disabled, onChange: value => { edit('protocol', value); }, options: [[\"anthropic-messages\", t('anthropic')], [\"openai-responses\", t('responses')], [\"openai-chat-completions\", t('chat')]] }), _jsx(TextField, { label: t('toolIdentifier'), hint: t(toolHint), value: draft.toolIdentifier, error: errors.toolIdentifier === undefined ? undefined : t(errors.toolIdentifier), disabled: disabled || (draft.protocol === 'openai-chat-completions' && draft.chatSearchMode === 'search-model'), onChange: value => { edit('toolIdentifier', value); } }), _jsx(TextField, { label: t('baseURL'), hint: t('baseURLHint'), value: draft.baseURL, error: errors.baseURL === undefined ? undefined : t(errors.baseURL), disabled: disabled, onChange: value => { edit('baseURL', value); } }), _jsx(TextField, { label: t('model'), hint: t('modelHint'), value: draft.model, error: errors.model === undefined ? undefined : t(errors.model), disabled: disabled, onChange: value => { edit('model', value); } }), _jsx(TextField, { label: t('fallbackModel'), hint: t('fallbackModelHint'), value: draft.fallbackModel, error: errors.fallbackModel === undefined ? undefined : t(errors.fallbackModel), disabled: disabled, onChange: value => { edit('fallbackModel', value); } }), _jsx(TextField, { label: t('apiKeyEnv'), hint: credentialConfigured ? t('credentialConfigured') : t('apiKeyEnvHint'), value: draft.apiKeyEnv, error: errors.apiKeyEnv === undefined ? undefined : t(errors.apiKeyEnv), disabled: disabled, onChange: value => { edit('apiKeyEnv', value); } }), _jsx(TextField, { type: \"password\", label: t('apiKey'), hint: t('apiKeyHint'), value: apiKey, disabled: disabled, onChange: value => { setApiKey(value); setHasDraft(true); setFailed(false); } }), _jsx(TextField, { inputMode: \"numeric\", label: t('maxTokens'), hint: t('maxTokensHint'), value: draft.maxTokens, error: errors.maxTokens === undefined ? undefined : t(errors.maxTokens), disabled: disabled, onChange: value => { edit('maxTokens', value); } }), draft.modelMode === 'current-session' ? (_jsxs(_Fragment, { children: [_jsx(TextField, { inputMode: \"numeric\", label: t('maxUses'), hint: t('maxUsesHint'), value: draft.maxUses, error: errors.maxUses === undefined ? undefined : t(errors.maxUses), disabled: disabled, onChange: value => { edit('maxUses', value); } }), _jsx(TextField, { label: t('apiVersion'), hint: t('apiVersionHint'), value: draft.apiVersion, error: errors.apiVersion === undefined ? undefined : t(errors.apiVersion), disabled: disabled, onChange: value => { edit('apiVersion', value); } }), _jsx(SelectField, { label: t('chatSearchMode'), hint: t('chatSearchModeHint'), value: draft.chatSearchMode, disabled: disabled, onChange: value => { edit('chatSearchMode', value); }, options: [[\"search-model\", t('chatOfficial')], [\"vendor-options\", t('chatVendor')]] }), _jsx(SelectField, { label: t('searchContextSize'), hint: t('searchContextSizeHint'), value: draft.searchContextSize, disabled: disabled, onChange: value => { edit('searchContextSize', value); }, options: [[\"\", t('contextDefault')], [\"low\", t('contextLow')], [\"medium\", t('contextMedium')], [\"high\", t('contextHigh')]] })] })) : draft.protocol === 'anthropic-messages' ? (_jsxs(_Fragment, { children: [_jsx(TextField, { inputMode: \"numeric\", label: t('maxUses'), hint: t('maxUsesHint'), value: draft.maxUses, error: errors.maxUses === undefined ? undefined : t(errors.maxUses), disabled: disabled, onChange: value => { edit('maxUses', value); } }), _jsx(TextField, { label: t('apiVersion'), hint: t('apiVersionHint'), value: draft.apiVersion, error: errors.apiVersion === undefined ? undefined : t(errors.apiVersion), disabled: disabled, onChange: value => { edit('apiVersion', value); } })] })) : (_jsxs(_Fragment, { children: [draft.protocol === 'openai-chat-completions' ? (_jsx(SelectField, { label: t('chatSearchMode'), hint: t('chatSearchModeHint'), value: draft.chatSearchMode, disabled: disabled, onChange: value => { edit('chatSearchMode', value); }, options: [[\"search-model\", t('chatOfficial')], [\"vendor-options\", t('chatVendor')]] })) : null, _jsx(SelectField, { label: t('searchContextSize'), hint: t('searchContextSizeHint'), value: draft.searchContextSize, disabled: disabled, onChange: value => { edit('searchContextSize', value); }, options: [[\"\", t('contextDefault')], [\"low\", t('contextLow')], [\"medium\", t('contextMedium')], [\"high\", t('contextHigh')]] })] }))] }), _jsxs(\"div\", { className: \"wse-footer\", children: [failed ? _jsx(\"p\", { className: \"wse-failed\", role: \"alert\", children: t('failed') }) : null, _jsxs(\"div\", { className: \"wse-actions\", children: [_jsx(\"button\", { type: \"button\", className: \"wse-button\", disabled: disabled || resetToProfile, onClick: reset, children: t('reset') }), _jsx(\"button\", { type: \"button\", className: \"wse-button\", disabled: disabled || !dirty, onClick: discard, children: t('discard') }), _jsx(\"button\", { type: \"button\", className: \"wse-button wse-save\", disabled: disabled || !dirty || invalid, onClick: () => { void save(); }, children: saving ? t('saving') : t('save') })] })] })] }) : null] });\n}\nfunction TextField(props) {\n const id = useId();\n const describedBy = useId();\n return _jsxs(\"div\", { className: \"wse-field\", children: [_jsx(\"label\", { className: \"wse-label\", htmlFor: id, children: props.label }), _jsx(\"input\", { id: id, className: \"wse-input\", type: props.type ?? 'text', value: props.value, disabled: props.disabled, inputMode: props.inputMode, \"aria-invalid\": props.error === undefined ? undefined : true, \"aria-describedby\": describedBy, spellCheck: false, onChange: event => { props.onChange(event.target.value); } }), _jsx(\"p\", { id: describedBy, className: props.error === undefined ? 'wse-hint' : 'wse-error', children: props.error ?? props.hint })] });\n}\nfunction SelectField(props) {\n const id = useId();\n const describedBy = useId();\n return _jsxs(\"div\", { className: \"wse-field\", children: [_jsx(\"label\", { className: \"wse-label\", htmlFor: id, children: props.label }), _jsx(\"select\", { id: id, className: \"wse-select\", value: props.value, disabled: props.disabled, \"aria-describedby\": describedBy, onChange: event => { props.onChange(event.target.value); }, children: props.options.map(([value, label]) => _jsx(\"option\", { value: value, children: label }, value)) }), _jsx(\"p\", { id: describedBy, className: \"wse-hint\", children: props.hint })] });\n}\nfunction asSearchSettings(value) { return typeof value === 'object' && value !== null ? value : undefined; }\nfunction positiveInteger(value) { const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0; }\n//# sourceMappingURL=SearchSettingsCard.js.map","export const en = {\n title: 'Web Search Enhanced',\n description: 'Choose the model route and built-in search settings used by web_search.',\n expand: 'Expand', collapse: 'Collapse', unsaved: 'Unsaved',\n modelMode: 'Model route', modelModeHint: 'Use the fixed search route or follow the model and calling protocol of the current session.',\n configuredMode: 'Fixed configuration', currentSessionMode: 'Current session model',\n protocol: 'API protocol', protocolHint: 'The fixed or fallback route protocol. Follow mode uses the current model protocol when available.',\n anthropic: 'Anthropic Messages v1', responses: 'OpenAI Responses API', chat: 'OpenAI Chat Completions API',\n baseURL: 'Endpoint base URL', baseURLHint: 'The protocol endpoint suffix is appended only when it is not already present.',\n model: 'Model ID', modelHint: 'The model used by the fixed route and when no session model is available.',\n fallbackModel: 'Fallback search model', fallbackModelHint: 'Optional model used when the current session route cannot be resolved; blank uses Model ID.',\n apiKeyEnv: 'API key environment variable', apiKeyEnvHint: 'The fixed and fallback route reads the API key from this credential reference.',\n apiKey: 'API key', apiKeyHint: 'Enter a new key to store it securely. The field is cleared after saving.', credentialConfigured: 'A credential is configured for this reference.',\n toolIdentifier: 'Internal search identifier',\n toolHintAnthropic: 'Anthropic tool type. Leave blank for web_search_20250305.',\n toolHintResponses: 'Responses built-in tool type. Leave blank for web_search.',\n toolHintChatOfficial: 'Official Chat search models use the fixed web_search_options field.',\n toolHintChatVendor: 'Vendor-specific Chat search options field. Leave blank for web_search_options.',\n maxTokens: 'Max output tokens', maxTokensHint: 'Positive integer sent as max_tokens or max_output_tokens for one search request.',\n maxUses: 'Max search uses', maxUsesHint: 'Anthropic-only maximum number of server-tool uses in one request.',\n apiVersion: 'Anthropic API version', apiVersionHint: 'Value sent in the anthropic-version request header.',\n chatSearchMode: 'Chat search capability', chatSearchModeHint: 'Use the official specialized-search-model contract unless the gateway documents a custom options field.',\n chatOfficial: 'Official search model', chatVendor: 'Vendor custom options field',\n searchContextSize: 'Search context size', searchContextSizeHint: 'Optional OpenAI-compatible context budget.',\n contextDefault: 'Provider default', contextLow: 'Low', contextMedium: 'Medium', contextHigh: 'High',\n save: 'Save', saving: 'Saving…', reset: 'Reset to Profile', discard: 'Discard',\n readOnly: 'This connection cannot persist Host Profile settings.',\n failed: 'The Host rejected the change. Review the fields and try again.',\n invalidURL: 'Enter an absolute HTTP(S) endpoint URL.', invalidRequired: 'This field must not be blank.', invalidCredentialRef: 'Use a valid environment-variable name such as WEB_SEARCH_ENHANCED_API.',\n invalidIdentifier: 'Use 1–128 identifier characters and start with a letter.', invalidInteger: 'Enter a positive integer.',\n};\nexport const zh = {\n title: 'Web Search Enhanced',\n description: '配置 web_search 使用的模型路由与内置搜索参数。',\n expand: '展开', collapse: '折叠', unsaved: '未保存',\n modelMode: '模型路由', modelModeHint: '选择固定搜索路由,或跟随当前会话模型及其调用协议。',\n configuredMode: '固定配置', currentSessionMode: '当前会话模型',\n protocol: 'API 协议', protocolHint: '固定或兜底路由使用的协议;跟随模式会优先使用当前模型协议。',\n anthropic: 'Anthropic Messages v1', responses: 'OpenAI Responses API', chat: 'OpenAI Chat Completions API',\n baseURL: '接口基础地址', baseURLHint: '仅当地址尚未包含协议端点时,插件才会自动追加相应路径。',\n model: '模型标识', modelHint: '固定路由使用的模型;当前会话没有可用模型时也会使用它。',\n fallbackModel: '兜底搜索模型', fallbackModelHint: '当前会话路由无法解析时使用的可选模型;留空使用模型标识。',\n apiKeyEnv: 'API Key 环境变量', apiKeyEnvHint: '固定和兜底路由从此凭据引用读取 API Key。',\n apiKey: 'API Key', apiKeyHint: '输入新 Key 后安全保存;保存成功后输入框会清空。', credentialConfigured: '此凭据引用已配置。',\n toolIdentifier: '内部搜索标识符',\n toolHintAnthropic: 'Anthropic 工具类型;留空使用 web_search_20250305。',\n toolHintResponses: 'Responses 内置工具类型;留空使用 web_search。',\n toolHintChatOfficial: '官方 Chat 搜索模型固定使用 web_search_options 字段。',\n toolHintChatVendor: '提供方自定义的 Chat 搜索选项字段;留空使用 web_search_options。',\n maxTokens: '最大输出 Token', maxTokensHint: '单次搜索请求使用的正整数 max_tokens 或 max_output_tokens。',\n maxUses: '最大搜索次数', maxUsesHint: '仅 Anthropic:单次请求最多调用服务端搜索工具的次数。',\n apiVersion: 'Anthropic API 版本', apiVersionHint: '写入 anthropic-version 请求头的版本值。',\n chatSearchMode: 'Chat 搜索能力', chatSearchModeHint: '除非网关明确说明自定义选项字段,否则请选择官方专用搜索模型协议。',\n chatOfficial: '官方专用搜索模型', chatVendor: '提供方自定义选项字段',\n searchContextSize: '搜索上下文大小', searchContextSizeHint: '可选的 OpenAI 兼容搜索上下文预算。',\n contextDefault: '提供方默认值', contextLow: '低', contextMedium: '中', contextHigh: '高',\n save: '保存', saving: '正在保存…', reset: '重置为 Profile 配置', discard: '放弃修改',\n readOnly: '当前连接不能持久化 Host Profile 设置。',\n failed: 'Host 拒绝了修改,请检查字段后重试。',\n invalidURL: '请输入绝对 HTTP(S) 接口地址。', invalidRequired: '此字段不能为空。', invalidCredentialRef: '请输入有效的环境变量名,例如 WEB_SEARCH_ENHANCED_API。',\n invalidIdentifier: '请输入 1–128 位标识符字符,并以字母开头。', invalidInteger: '请输入正整数。',\n};\n//# sourceMappingURL=locales.js.map","import { SearchSettingsCard } from \"./SearchSettingsCard.js\";\nimport { en, zh } from \"./locales.js\";\nconst SETTINGS_NAMESPACE = 'web-search-enhanced';\nconst LOCALE_NAMESPACE = 'settings.webSearchEnhanced';\n/** Browser plugin dependencies. */\nexport const inject = ['slots', 'locale', 'settingsScope', 'remote', 'remote.credentials'];\n/** Register the localized card under Settings -> Plugins -> Plugin configuration. */\nexport function apply(ctx) {\n const t = ctx.locale.bind(LOCALE_NAMESPACE);\n ctx.effect(() => ctx.locale.register(LOCALE_NAMESPACE, { en, zh }), 'web-search-enhanced: settings dictionaries');\n const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n key: SETTINGS_NAMESPACE,\n locale: LOCALE_NAMESPACE,\n inject: () => ({ scope, credentials: ctx.remote.credentials, t }),\n }, SearchSettingsCard));\n}\n//# sourceMappingURL=index.js.map"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useSyncExternalStore","useMemo","useState","useId","_jsxs","_jsx","_Fragment"],"sources":["types/client/SearchSettingsCard.js","types/client/locales.js","types/client/index.js"],"sourcesContent":["import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from \"react/jsx-runtime\";\nimport { useEffect, useId, useMemo, useState, useSyncExternalStore } from 'react';\nconst editableFields = ['modelMode', 'protocol', 'baseURL', 'model', 'fallbackModel', 'apiKeyEnv', 'apiVersion', 'toolIdentifier', 'maxTokens', 'maxUses', 'chatSearchMode', 'searchContextSize'];\n/** Convert resolved settings into a stable form draft. */\nexport function draftFrom(value) {\n return {\n modelMode: value?.modelMode ?? 'configured',\n protocol: value?.protocol ?? 'anthropic-messages',\n baseURL: value?.baseURL ?? 'https://api.deepseek.com/anthropic/v1',\n model: value?.model ?? 'deepseek-flash',\n fallbackModel: value?.fallbackModel ?? '',\n apiKeyEnv: value?.apiKeyEnv ?? 'WEB_SEARCH_ENHANCED_API',\n apiVersion: value?.apiVersion ?? '2023-06-01',\n toolIdentifier: value?.toolIdentifier ?? '',\n maxTokens: String(value?.maxTokens ?? 4096),\n maxUses: String(value?.maxUses ?? 5),\n chatSearchMode: value?.chatSearchMode ?? 'search-model',\n searchContextSize: value?.searchContextSize ?? '',\n };\n}\n/** Validate a draft and return field-specific locale keys. */\nexport async function saveCredential(credentials, ref, value) {\n const secret = value.trim();\n if (secret.length === 0)\n return false;\n const result = await credentials.set(ref.trim(), secret);\n if (!result.ok)\n throw new Error(result.error.message);\n return true;\n}\nexport function validateDraft(draft) {\n const errors = {};\n try {\n const url = new URL(draft.baseURL);\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n errors.baseURL = 'invalidURL';\n }\n catch {\n errors.baseURL = 'invalidURL';\n }\n if (draft.model.trim().length === 0)\n errors.model = 'invalidRequired';\n if (draft.apiKeyEnv.trim().length === 0)\n errors.apiKeyEnv = 'invalidRequired';\n if (draft.apiKeyEnv.trim().length > 0 && !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(draft.apiKeyEnv.trim()))\n errors.apiKeyEnv = 'invalidCredentialRef';\n if (draft.apiVersion.trim().length === 0)\n errors.apiVersion = 'invalidRequired';\n if (draft.toolIdentifier.length > 0 && !/^[A-Za-z][A-Za-z0-9_.:-]{0,127}$/u.test(draft.toolIdentifier))\n errors.toolIdentifier = 'invalidIdentifier';\n if (!positiveInteger(draft.maxTokens))\n errors.maxTokens = 'invalidInteger';\n if (!positiveInteger(draft.maxUses))\n errors.maxUses = 'invalidInteger';\n return errors;\n}\nconst cardCss = `\n.wse-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n transition: border-color .16s, background .16s;\n}\n.wse-card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n.wse-card[data-open='true'] {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.wse-header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n.wse-header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n.wse-head {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.wse-name {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n.wse-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 1.5;\n}\n.wse-pending {\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n border-radius: 999px;\n flex: none;\n padding: 1px 8px;\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n}\n.wse-chevron {\n box-sizing: border-box;\n flex: none;\n width: 8px;\n height: 8px;\n margin-right: 3px;\n border-right: 1.5px solid var(--dsw-alias-label-tertiary);\n border-bottom: 1.5px solid var(--dsw-alias-label-tertiary);\n transform: rotate(45deg);\n transition: transform .16s;\n}\n.wse-card[data-open='true'] .wse-chevron {\n transform: rotate(225deg);\n}\n.wse-body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding-bottom: 8px;\n}\n.wse-fields {\n display: flex;\n flex-direction: column;\n}\n.wse-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 0;\n}\n.wse-field + .wse-field {\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.wse-label {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n}\n.wse-input, .wse-select {\n box-sizing: border-box;\n width: 100%;\n height: 34px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 0 12px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n outline: none;\n transition: border-color .16s, background .16s;\n}\n.wse-input:focus-visible, .wse-select:focus-visible,\n.wse-input:focus, .wse-select:focus {\n border-color: var(--dsw-alias-brand-primary);\n outline: none;\n}\n.wse-input[aria-invalid='true'] {\n border-color: var(--dsw-alias-label-error);\n}\n.wse-input:disabled, .wse-select:disabled {\n cursor: default;\n color: var(--dsw-alias-label-tertiary);\n opacity: .55;\n}\n.wse-hint, .wse-error, .wse-readonly, .wse-failed {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n}\n.wse-hint {\n color: var(--dsw-alias-label-tertiary);\n}\n.wse-error, .wse-failed {\n color: var(--dsw-alias-label-error);\n}\n.wse-readonly {\n margin: 12px 0 0;\n color: var(--dsw-alias-label-tertiary);\n}\n.wse-footer {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 4px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n}\n.wse-failed {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n.wse-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n}\n.wse-button {\n appearance: none;\n font: inherit;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 5px 14px;\n font-size: 13px;\n line-height: 1.5;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n transition: color .16s, border-color .16s, background .16s, opacity .16s;\n}\n.wse-button:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n background: var(--dsw-alias-bg-layer-4);\n}\n.wse-button:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n.wse-button:disabled {\n opacity: .4;\n cursor: default;\n}\n.wse-save {\n border-color: transparent;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-layer-3));\n}\n.wse-save:hover:not(:disabled) {\n background: var(--dsw-alias-label-primary);\n opacity: .9;\n}\n`;\nexport function SearchSettingsCard({ scope, credentials, t }) {\n const snapshot = useSyncExternalStore(scope.subscribe.bind(scope), scope.getSnapshot.bind(scope));\n const resolved = useMemo(() => draftFrom(snapshot.value), [snapshot.value]);\n const [draft, setDraft] = useState(resolved);\n const [open, setOpen] = useState(false);\n const [saving, setSaving] = useState(false);\n const [failed, setFailed] = useState(false);\n const [hasDraft, setHasDraft] = useState(false);\n const [resetToProfile, setResetToProfile] = useState(false);\n const [apiKey, setApiKey] = useState('');\n const [credentialConfigured, setCredentialConfigured] = useState(false);\n const bodyId = useId();\n useEffect(() => { if (!saving && !hasDraft)\n setDraft(resolved); }, [hasDraft, resolved, saving]);\n useEffect(() => {\n const ref = draft.apiKeyEnv.trim();\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(ref)) {\n setCredentialConfigured(false);\n return;\n }\n let active = true;\n void credentials.describe([ref]).then(result => {\n if (active)\n setCredentialConfigured(result.ok && result.value[ref]?.configured === true);\n }).catch(() => { if (active)\n setCredentialConfigured(false); });\n return () => { active = false; };\n }, [credentials, draft.apiKeyEnv]);\n const errors = useMemo(() => validateDraft(draft), [draft]);\n const dirty = resetToProfile || apiKey.length > 0 || JSON.stringify(draft) !== JSON.stringify(resolved);\n const invalid = Object.keys(errors).length > 0;\n const disabled = snapshot.status !== 'ready' || !snapshot.writable || saving;\n const edit = (field, value) => {\n setDraft(current => ({ ...current, [field]: value }));\n setResetToProfile(false);\n setHasDraft(true);\n setFailed(false);\n };\n const save = async () => {\n if (disabled || !dirty || invalid)\n return;\n setSaving(true);\n setFailed(false);\n try {\n if (await saveCredential(credentials, draft.apiKeyEnv, apiKey))\n setCredentialConfigured(true);\n if (resetToProfile) {\n for (const field of editableFields)\n await scope.unset(field);\n }\n else {\n await scope.set('modelMode', draft.modelMode);\n await scope.set('protocol', draft.protocol);\n await scope.set('baseURL', draft.baseURL.trim());\n await scope.set('model', draft.model.trim());\n if (draft.fallbackModel.trim().length === 0)\n await scope.unset('fallbackModel');\n else\n await scope.set('fallbackModel', draft.fallbackModel.trim());\n await scope.set('apiKeyEnv', draft.apiKeyEnv.trim());\n await scope.set('apiVersion', draft.apiVersion.trim());\n if (draft.toolIdentifier.trim().length === 0)\n await scope.unset('toolIdentifier');\n else\n await scope.set('toolIdentifier', draft.toolIdentifier.trim());\n await scope.set('maxTokens', Number(draft.maxTokens));\n await scope.set('maxUses', Number(draft.maxUses));\n await scope.set('chatSearchMode', draft.chatSearchMode);\n if (draft.searchContextSize === '')\n await scope.unset('searchContextSize');\n else\n await scope.set('searchContextSize', draft.searchContextSize);\n }\n setApiKey('');\n setHasDraft(false);\n setResetToProfile(false);\n setOpen(false);\n }\n catch {\n setFailed(true);\n }\n finally {\n setSaving(false);\n }\n };\n const reset = () => { setApiKey(''); setDraft(draftFrom(asSearchSettings(snapshot.base))); setResetToProfile(true); setHasDraft(true); setFailed(false); };\n const discard = () => { setApiKey(''); setDraft(resolved); setResetToProfile(false); setHasDraft(false); setFailed(false); };\n if (snapshot.status === 'unavailable')\n return null;\n const toolHint = draft.protocol === 'anthropic-messages' ? 'toolHintAnthropic' : draft.protocol === 'openai-responses' ? 'toolHintResponses' : draft.chatSearchMode === 'search-model' ? 'toolHintChatOfficial' : 'toolHintChatVendor';\n return _jsxs(\"li\", { className: \"wse-card\", \"data-open\": open, \"data-plugin-key\": \"web-search-enhanced\", \"data-wse-card\": \"true\", children: [_jsx(\"style\", { children: cardCss }), _jsxs(\"button\", { type: \"button\", className: \"wse-header\", \"aria-expanded\": open, \"aria-controls\": bodyId, \"aria-label\": `${t(open ? 'collapse' : 'expand')}: ${t('title')}`, onClick: () => { setOpen(value => !value); }, children: [_jsxs(\"span\", { className: \"wse-head\", children: [_jsx(\"span\", { className: \"wse-name\", children: t('title') }), _jsx(\"span\", { className: \"wse-desc\", children: t('description') })] }), dirty ? _jsx(\"span\", { className: \"wse-pending\", children: t('unsaved') }) : null, _jsx(\"span\", { className: \"wse-chevron\", \"aria-hidden\": \"true\" })] }), open ? _jsxs(\"div\", { className: \"wse-body\", id: bodyId, children: [!snapshot.writable && snapshot.status === 'ready' ? _jsx(\"p\", { className: \"wse-readonly\", role: \"status\", children: t('readOnly') }) : null, _jsxs(\"div\", { className: \"wse-fields\", children: [_jsx(SelectField, { label: t('modelMode'), hint: t('modelModeHint'), value: draft.modelMode, disabled: disabled, onChange: value => { edit('modelMode', value); }, options: [[\"configured\", t('configuredMode')], [\"current-session\", t('currentSessionMode')]] }), _jsx(SelectField, { label: t('protocol'), hint: t('protocolHint'), value: draft.protocol, disabled: disabled, onChange: value => { edit('protocol', value); }, options: [[\"anthropic-messages\", t('anthropic')], [\"openai-responses\", t('responses')], [\"openai-chat-completions\", t('chat')]] }), _jsx(TextField, { label: t('toolIdentifier'), hint: t(toolHint), value: draft.toolIdentifier, error: errors.toolIdentifier === undefined ? undefined : t(errors.toolIdentifier), disabled: disabled || (draft.protocol === 'openai-chat-completions' && draft.chatSearchMode === 'search-model'), onChange: value => { edit('toolIdentifier', value); } }), _jsx(TextField, { label: t('baseURL'), hint: t('baseURLHint'), value: draft.baseURL, error: errors.baseURL === undefined ? undefined : t(errors.baseURL), disabled: disabled, onChange: value => { edit('baseURL', value); } }), _jsx(TextField, { label: t('model'), hint: t('modelHint'), value: draft.model, error: errors.model === undefined ? undefined : t(errors.model), disabled: disabled, onChange: value => { edit('model', value); } }), _jsx(TextField, { label: t('fallbackModel'), hint: t('fallbackModelHint'), value: draft.fallbackModel, error: errors.fallbackModel === undefined ? undefined : t(errors.fallbackModel), disabled: disabled, onChange: value => { edit('fallbackModel', value); } }), _jsx(TextField, { label: t('apiKeyEnv'), hint: credentialConfigured ? t('credentialConfigured') : t('apiKeyEnvHint'), value: draft.apiKeyEnv, error: errors.apiKeyEnv === undefined ? undefined : t(errors.apiKeyEnv), disabled: disabled, onChange: value => { edit('apiKeyEnv', value); } }), _jsx(TextField, { type: \"password\", label: t('apiKey'), hint: t('apiKeyHint'), value: apiKey, disabled: disabled, onChange: value => { setApiKey(value); setHasDraft(true); setFailed(false); } }), _jsx(TextField, { inputMode: \"numeric\", label: t('maxTokens'), hint: t('maxTokensHint'), value: draft.maxTokens, error: errors.maxTokens === undefined ? undefined : t(errors.maxTokens), disabled: disabled, onChange: value => { edit('maxTokens', value); } }), draft.modelMode === 'current-session' ? (_jsxs(_Fragment, { children: [_jsx(TextField, { inputMode: \"numeric\", label: t('maxUses'), hint: t('maxUsesHint'), value: draft.maxUses, error: errors.maxUses === undefined ? undefined : t(errors.maxUses), disabled: disabled, onChange: value => { edit('maxUses', value); } }), _jsx(TextField, { label: t('apiVersion'), hint: t('apiVersionHint'), value: draft.apiVersion, error: errors.apiVersion === undefined ? undefined : t(errors.apiVersion), disabled: disabled, onChange: value => { edit('apiVersion', value); } }), _jsx(SelectField, { label: t('chatSearchMode'), hint: t('chatSearchModeHint'), value: draft.chatSearchMode, disabled: disabled, onChange: value => { edit('chatSearchMode', value); }, options: [[\"search-model\", t('chatOfficial')], [\"vendor-options\", t('chatVendor')]] }), _jsx(SelectField, { label: t('searchContextSize'), hint: t('searchContextSizeHint'), value: draft.searchContextSize, disabled: disabled, onChange: value => { edit('searchContextSize', value); }, options: [[\"\", t('contextDefault')], [\"low\", t('contextLow')], [\"medium\", t('contextMedium')], [\"high\", t('contextHigh')]] })] })) : draft.protocol === 'anthropic-messages' ? (_jsxs(_Fragment, { children: [_jsx(TextField, { inputMode: \"numeric\", label: t('maxUses'), hint: t('maxUsesHint'), value: draft.maxUses, error: errors.maxUses === undefined ? undefined : t(errors.maxUses), disabled: disabled, onChange: value => { edit('maxUses', value); } }), _jsx(TextField, { label: t('apiVersion'), hint: t('apiVersionHint'), value: draft.apiVersion, error: errors.apiVersion === undefined ? undefined : t(errors.apiVersion), disabled: disabled, onChange: value => { edit('apiVersion', value); } })] })) : (_jsxs(_Fragment, { children: [draft.protocol === 'openai-chat-completions' ? (_jsx(SelectField, { label: t('chatSearchMode'), hint: t('chatSearchModeHint'), value: draft.chatSearchMode, disabled: disabled, onChange: value => { edit('chatSearchMode', value); }, options: [[\"search-model\", t('chatOfficial')], [\"vendor-options\", t('chatVendor')]] })) : null, _jsx(SelectField, { label: t('searchContextSize'), hint: t('searchContextSizeHint'), value: draft.searchContextSize, disabled: disabled, onChange: value => { edit('searchContextSize', value); }, options: [[\"\", t('contextDefault')], [\"low\", t('contextLow')], [\"medium\", t('contextMedium')], [\"high\", t('contextHigh')]] })] }))] }), _jsxs(\"div\", { className: \"wse-footer\", children: [failed ? _jsx(\"p\", { className: \"wse-failed\", role: \"alert\", children: t('failed') }) : null, _jsxs(\"div\", { className: \"wse-actions\", children: [_jsx(\"button\", { type: \"button\", className: \"wse-button\", disabled: disabled || resetToProfile, onClick: reset, children: t('reset') }), _jsx(\"button\", { type: \"button\", className: \"wse-button\", disabled: disabled || !dirty, onClick: discard, children: t('discard') }), _jsx(\"button\", { type: \"button\", className: \"wse-button wse-save\", disabled: disabled || !dirty || invalid, onClick: () => { void save(); }, children: saving ? t('saving') : t('save') })] })] })] }) : null] });\n}\nfunction TextField(props) {\n const id = useId();\n const describedBy = useId();\n return _jsxs(\"div\", { className: \"wse-field\", children: [_jsx(\"label\", { className: \"wse-label\", htmlFor: id, children: props.label }), _jsx(\"input\", { id: id, className: \"wse-input\", type: props.type ?? 'text', value: props.value, disabled: props.disabled, inputMode: props.inputMode, \"aria-invalid\": props.error === undefined ? undefined : true, \"aria-describedby\": describedBy, spellCheck: false, onChange: event => { props.onChange(event.target.value); } }), _jsx(\"p\", { id: describedBy, className: props.error === undefined ? 'wse-hint' : 'wse-error', children: props.error ?? props.hint })] });\n}\nfunction SelectField(props) {\n const id = useId();\n const describedBy = useId();\n return _jsxs(\"div\", { className: \"wse-field\", children: [_jsx(\"label\", { className: \"wse-label\", htmlFor: id, children: props.label }), _jsx(\"select\", { id: id, className: \"wse-select\", value: props.value, disabled: props.disabled, \"aria-describedby\": describedBy, onChange: event => { props.onChange(event.target.value); }, children: props.options.map(([value, label]) => _jsx(\"option\", { value: value, children: label }, value)) }), _jsx(\"p\", { id: describedBy, className: \"wse-hint\", children: props.hint })] });\n}\nfunction asSearchSettings(value) { return typeof value === 'object' && value !== null ? value : undefined; }\nfunction positiveInteger(value) { const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0; }\n//# sourceMappingURL=SearchSettingsCard.js.map","export const en = {\n title: 'Web Search Enhanced',\n description: 'Choose the model route and built-in search settings used by web_search.',\n expand: 'Expand', collapse: 'Collapse', unsaved: 'Unsaved',\n modelMode: 'Model route', modelModeHint: 'Use the fixed search route or follow the model and calling protocol of the current session.',\n configuredMode: 'Fixed configuration', currentSessionMode: 'Current session model',\n protocol: 'API protocol', protocolHint: 'The fixed or fallback route protocol. Follow mode uses the current model protocol when available.',\n anthropic: 'Anthropic Messages v1', responses: 'OpenAI Responses API', chat: 'OpenAI Chat Completions API',\n baseURL: 'Endpoint base URL', baseURLHint: 'The protocol endpoint suffix is appended only when it is not already present.',\n model: 'Model ID', modelHint: 'The model used by the fixed route and when no session model is available.',\n fallbackModel: 'Fallback search model', fallbackModelHint: 'Optional model used when the current session route cannot be resolved; blank uses Model ID.',\n apiKeyEnv: 'API key environment variable', apiKeyEnvHint: 'The fixed and fallback route reads the API key from this credential reference.',\n apiKey: 'API key', apiKeyHint: 'Enter a new key to store it securely. The field is cleared after saving.', credentialConfigured: 'A credential is configured for this reference.',\n toolIdentifier: 'Internal search identifier',\n toolHintAnthropic: 'Anthropic tool type. Leave blank for web_search_20250305.',\n toolHintResponses: 'Responses built-in tool type. Leave blank for web_search.',\n toolHintChatOfficial: 'Official Chat search models use the fixed web_search_options field.',\n toolHintChatVendor: 'Vendor-specific Chat search options field. Leave blank for web_search_options.',\n maxTokens: 'Max output tokens', maxTokensHint: 'Positive integer sent as max_tokens or max_output_tokens for one search request.',\n maxUses: 'Max search uses', maxUsesHint: 'Anthropic-only maximum number of server-tool uses in one request.',\n apiVersion: 'Anthropic API version', apiVersionHint: 'Value sent in the anthropic-version request header.',\n chatSearchMode: 'Chat search capability', chatSearchModeHint: 'Use the official specialized-search-model contract unless the gateway documents a custom options field.',\n chatOfficial: 'Official search model', chatVendor: 'Vendor custom options field',\n searchContextSize: 'Search context size', searchContextSizeHint: 'Optional OpenAI-compatible context budget.',\n contextDefault: 'Provider default', contextLow: 'Low', contextMedium: 'Medium', contextHigh: 'High',\n commandLabel: 'Search settings', commandDescription: 'Configure enhanced web search model route and parameters',\n save: 'Save', saving: 'Saving…', reset: 'Reset to Profile', discard: 'Discard',\n readOnly: 'This connection cannot persist Host Profile settings.',\n failed: 'The Host rejected the change. Review the fields and try again.',\n invalidURL: 'Enter an absolute HTTP(S) endpoint URL.', invalidRequired: 'This field must not be blank.', invalidCredentialRef: 'Use a valid environment-variable name such as WEB_SEARCH_ENHANCED_API.',\n invalidIdentifier: 'Use 1–128 identifier characters and start with a letter.', invalidInteger: 'Enter a positive integer.',\n};\nexport const zh = {\n title: 'Web Search Enhanced',\n description: '配置 web_search 使用的模型路由与内置搜索参数。',\n expand: '展开', collapse: '折叠', unsaved: '未保存',\n modelMode: '模型路由', modelModeHint: '选择固定搜索路由,或跟随当前会话模型及其调用协议。',\n configuredMode: '固定配置', currentSessionMode: '当前会话模型',\n protocol: 'API 协议', protocolHint: '固定或兜底路由使用的协议;跟随模式会优先使用当前模型协议。',\n anthropic: 'Anthropic Messages v1', responses: 'OpenAI Responses API', chat: 'OpenAI Chat Completions API',\n baseURL: '接口基础地址', baseURLHint: '仅当地址尚未包含协议端点时,插件才会自动追加相应路径。',\n model: '模型标识', modelHint: '固定路由使用的模型;当前会话没有可用模型时也会使用它。',\n fallbackModel: '兜底搜索模型', fallbackModelHint: '当前会话路由无法解析时使用的可选模型;留空使用模型标识。',\n apiKeyEnv: 'API Key 环境变量', apiKeyEnvHint: '固定和兜底路由从此凭据引用读取 API Key。',\n apiKey: 'API Key', apiKeyHint: '输入新 Key 后安全保存;保存成功后输入框会清空。', credentialConfigured: '此凭据引用已配置。',\n toolIdentifier: '内部搜索标识符',\n toolHintAnthropic: 'Anthropic 工具类型;留空使用 web_search_20250305。',\n toolHintResponses: 'Responses 内置工具类型;留空使用 web_search。',\n toolHintChatOfficial: '官方 Chat 搜索模型固定使用 web_search_options 字段。',\n toolHintChatVendor: '提供方自定义的 Chat 搜索选项字段;留空使用 web_search_options。',\n maxTokens: '最大输出 Token', maxTokensHint: '单次搜索请求使用的正整数 max_tokens 或 max_output_tokens。',\n maxUses: '最大搜索次数', maxUsesHint: '仅 Anthropic:单次请求最多调用服务端搜索工具的次数。',\n apiVersion: 'Anthropic API 版本', apiVersionHint: '写入 anthropic-version 请求头的版本值。',\n chatSearchMode: 'Chat 搜索能力', chatSearchModeHint: '除非网关明确说明自定义选项字段,否则请选择官方专用搜索模型协议。',\n chatOfficial: '官方专用搜索模型', chatVendor: '提供方自定义选项字段',\n searchContextSize: '搜索上下文大小', searchContextSizeHint: '可选的 OpenAI 兼容搜索上下文预算。',\n contextDefault: '提供方默认值', contextLow: '低', contextMedium: '中', contextHigh: '高',\n commandLabel: '搜索设置', commandDescription: '配置增强联网搜索的模型路由与参数',\n save: '保存', saving: '正在保存…', reset: '重置为 Profile 配置', discard: '放弃修改',\n readOnly: '当前连接不能持久化 Host Profile 设置。',\n failed: 'Host 拒绝了修改,请检查字段后重试。',\n invalidURL: '请输入绝对 HTTP(S) 接口地址。', invalidRequired: '此字段不能为空。', invalidCredentialRef: '请输入有效的环境变量名,例如 WEB_SEARCH_ENHANCED_API。',\n invalidIdentifier: '请输入 1–128 位标识符字符,并以字母开头。', invalidInteger: '请输入正整数。',\n};\n//# sourceMappingURL=locales.js.map","import { SearchSettingsCard } from \"./SearchSettingsCard.js\";\nimport { en, zh } from \"./locales.js\";\nconst SETTINGS_NAMESPACE = 'web-search-enhanced';\nconst LOCALE_NAMESPACE = 'settings.webSearchEnhanced';\n/** Browser plugin dependencies. */\nexport const inject = ['slots', 'locale', 'settingsScope', 'remote', 'remote.credentials'];\n/** Register the localized card under Settings -> Plugins -> Plugin configuration. */\nexport function apply(ctx) {\n const t = ctx.locale.bind(LOCALE_NAMESPACE);\n ctx.effect(() => ctx.locale.register(LOCALE_NAMESPACE, { en, zh }), 'web-search-enhanced: settings dictionaries');\n const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n key: SETTINGS_NAMESPACE,\n locale: LOCALE_NAMESPACE,\n inject: () => ({ scope, credentials: ctx.remote.credentials, t }),\n }, SearchSettingsCard));\n ctx.inject(['commandUi'], (scope) => {\n const commandUi = scope.commandUi;\n if (typeof commandUi?.register === 'function') {\n scope.effect(() => commandUi.register({\n name: 'search-config',\n label: () => t('commandLabel'),\n description: () => t('commandDescription'),\n available: () => true,\n ui: {\n kind: 'action',\n run: () => {\n const trigger = document.querySelector('[data-slot=\"sidebar.settings\"] button');\n trigger?.click();\n setTimeout(() => {\n const card = document.querySelector('[data-wse-card=\"true\"]');\n card?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }, 200);\n },\n },\n }), 'web-search-enhanced: /search-config action command');\n }\n });\n}\n//# sourceMappingURL=index.js.map"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -22,11 +22,11 @@ function buildWireRequest(config, query, apiKey) {
|
|
|
22
22
|
const commonHeaders = {
|
|
23
23
|
"accept": "application/json",
|
|
24
24
|
"content-type": "application/json",
|
|
25
|
-
"user-agent": "dsh-web-search-enhanced/0.0.
|
|
25
|
+
"user-agent": "dsh-web-search-enhanced/0.0.5"
|
|
26
26
|
};
|
|
27
27
|
switch (config.protocol) {
|
|
28
28
|
case "anthropic-messages": return {
|
|
29
|
-
endpoint: appendEndpoint(config.baseURL,
|
|
29
|
+
endpoint: appendEndpoint(config.baseURL, config.protocol),
|
|
30
30
|
headers: {
|
|
31
31
|
...commonHeaders,
|
|
32
32
|
"anthropic-version": config.apiVersion,
|
|
@@ -51,7 +51,7 @@ function buildWireRequest(config, query, apiKey) {
|
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
case "openai-responses": return {
|
|
54
|
-
endpoint: appendEndpoint(config.baseURL,
|
|
54
|
+
endpoint: appendEndpoint(config.baseURL, config.protocol),
|
|
55
55
|
headers: {
|
|
56
56
|
...commonHeaders,
|
|
57
57
|
authorization: `Bearer ${apiKey}`
|
|
@@ -87,7 +87,7 @@ function buildWireRequest(config, query, apiKey) {
|
|
|
87
87
|
const optionsField = config.chatSearchMode === "search-model" ? "web_search_options" : config.toolIdentifier;
|
|
88
88
|
body[optionsField] = config.searchContextSize === void 0 ? {} : { search_context_size: config.searchContextSize };
|
|
89
89
|
return {
|
|
90
|
-
endpoint: appendEndpoint(config.baseURL,
|
|
90
|
+
endpoint: appendEndpoint(config.baseURL, config.protocol),
|
|
91
91
|
headers: {
|
|
92
92
|
...commonHeaders,
|
|
93
93
|
authorization: `Bearer ${apiKey}`
|
|
@@ -186,9 +186,12 @@ function parseChatCompletionsResponse(payload) {
|
|
|
186
186
|
truncated: false
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
|
-
function appendEndpoint(baseURL,
|
|
189
|
+
function appendEndpoint(baseURL, protocol) {
|
|
190
190
|
const trimmed = baseURL.replace(/\/+$/u, "");
|
|
191
|
-
|
|
191
|
+
const suffix = ENDPOINT_SUFFIX[protocol];
|
|
192
|
+
if (trimmed.toLowerCase().endsWith(suffix)) return trimmed;
|
|
193
|
+
if (protocol === "anthropic-messages") return `${trimmed.endsWith("/v1") ? trimmed.slice(0, -3) : trimmed}/v1/messages`;
|
|
194
|
+
return trimmed + suffix;
|
|
192
195
|
}
|
|
193
196
|
function addSource(target, value, forcedSnippet) {
|
|
194
197
|
if (value === void 0) return;
|
|
@@ -233,13 +236,15 @@ var EnhancedSearchProvider = class {
|
|
|
233
236
|
resolveConfig;
|
|
234
237
|
fetcher;
|
|
235
238
|
resolveApiKey;
|
|
239
|
+
recordRequest;
|
|
236
240
|
/** Stable provider id selected by ctx.web. */
|
|
237
241
|
id;
|
|
238
242
|
/** Create a provider backed by a dynamic settings resolver. */
|
|
239
|
-
constructor(resolveConfig, fetcher = globalThis.fetch, resolveApiKey = async (config) => config.apiKey ?? process.env[config.apiKeyEnv]) {
|
|
243
|
+
constructor(resolveConfig, fetcher = globalThis.fetch, resolveApiKey = async (config) => config.apiKey ?? process.env[config.apiKeyEnv], recordRequest) {
|
|
240
244
|
this.resolveConfig = resolveConfig;
|
|
241
245
|
this.fetcher = fetcher;
|
|
242
246
|
this.resolveApiKey = resolveApiKey;
|
|
247
|
+
this.recordRequest = recordRequest;
|
|
243
248
|
this.id = resolveConfig().providerId;
|
|
244
249
|
}
|
|
245
250
|
/** Report whether local configuration can attempt a request without network I/O. */
|
|
@@ -258,6 +263,12 @@ var EnhancedSearchProvider = class {
|
|
|
258
263
|
const apiKey = config.apiKey ?? await resolveCredential(this.resolveApiKey(config), signal);
|
|
259
264
|
if (apiKey === void 0 || apiKey.length === 0) throw new WebError(`dsh-web-search-enhanced: no API key for "${config.apiKeyEnv}"`, "WEB_PROVIDER_CREDENTIAL_MISSING");
|
|
260
265
|
const wire = buildWireRequest(config, request.query, apiKey);
|
|
266
|
+
this.recordRequest?.({
|
|
267
|
+
endpoint: wire.endpoint,
|
|
268
|
+
protocol: config.protocol,
|
|
269
|
+
apiVersion: config.apiVersion,
|
|
270
|
+
body: wire.body
|
|
271
|
+
});
|
|
261
272
|
let response;
|
|
262
273
|
try {
|
|
263
274
|
response = await this.fetcher(wire.endpoint, {
|
|
@@ -331,7 +342,7 @@ const DEFAULT_PROVIDER_ID = "enhanced-search";
|
|
|
331
342
|
/** Default Anthropic-compatible search endpoint. */
|
|
332
343
|
const DEFAULT_BASE_URL = "https://api.deepseek.com/anthropic/v1";
|
|
333
344
|
/** Default model aligned with DSH's built-in search provider. */
|
|
334
|
-
const DEFAULT_MODEL = "deepseek-
|
|
345
|
+
const DEFAULT_MODEL = "deepseek-flash";
|
|
335
346
|
/** Credential reference owned by this plugin's fixed and fallback routes. */
|
|
336
347
|
const DEFAULT_API_KEY_ENV = "WEB_SEARCH_ENHANCED_API";
|
|
337
348
|
/** Settings namespace paired with the plugin configuration card. */
|
|
@@ -368,7 +379,7 @@ const Config = z.object({
|
|
|
368
379
|
/** Resolve and validate one fixed/fallback route. */
|
|
369
380
|
function resolveConfig(config) {
|
|
370
381
|
const protocol = config.protocol ?? "anthropic-messages";
|
|
371
|
-
const model = config.model ?? "deepseek-
|
|
382
|
+
const model = config.model ?? "deepseek-flash";
|
|
372
383
|
const resolved = {
|
|
373
384
|
providerId: config.providerId ?? "enhanced-search",
|
|
374
385
|
modelMode: config.modelMode ?? "configured",
|
|
@@ -453,9 +464,9 @@ async function resolveRuntimeConfig(ctx, config) {
|
|
|
453
464
|
});
|
|
454
465
|
}
|
|
455
466
|
/** Construct a fixed-config provider for tests or custom compositions. */
|
|
456
|
-
function createProvider(config = {}, fetcher = globalThis.fetch) {
|
|
467
|
+
function createProvider(config = {}, fetcher = globalThis.fetch, recordRequest) {
|
|
457
468
|
const resolved = resolveConfig(config);
|
|
458
|
-
return new EnhancedSearchProvider(() => resolved, fetcher);
|
|
469
|
+
return new EnhancedSearchProvider(() => resolved, fetcher, async (c) => c.apiKey ?? process.env[c.apiKeyEnv], recordRequest);
|
|
459
470
|
}
|
|
460
471
|
function installPluginSettings(ctx, ns, schema, entry, hooks) {
|
|
461
472
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
@@ -503,8 +514,29 @@ function apply(ctx, config) {
|
|
|
503
514
|
if (runtime.apiKey !== void 0 && runtime.apiKey.length > 0) return runtime.apiKey;
|
|
504
515
|
const ref = credentialRef(runtime.apiKeyEnv);
|
|
505
516
|
const credentials = ctx.get("credentials");
|
|
506
|
-
if (credentials !== void 0)
|
|
507
|
-
|
|
517
|
+
if (credentials !== void 0) {
|
|
518
|
+
const resolvedValue = (await credentials.resolve(ref))?.value;
|
|
519
|
+
if (resolvedValue !== void 0 && resolvedValue.length > 0) return resolvedValue;
|
|
520
|
+
}
|
|
521
|
+
const ambient = launchEnvironmentOf(ctx).get(runtime.apiKeyEnv)?.value;
|
|
522
|
+
if (ambient !== void 0 && ambient.length > 0) return ambient;
|
|
523
|
+
if (runtime.apiKeyEnv === "WEB_SEARCH_ENHANCED_API" && runtime.baseURL.includes("deepseek.com")) {
|
|
524
|
+
const dsRef = credentialRef("DEEPSEEK_API_KEY");
|
|
525
|
+
if (credentials !== void 0) {
|
|
526
|
+
const dsVal = (await credentials.resolve(dsRef))?.value;
|
|
527
|
+
if (dsVal !== void 0 && dsVal.length > 0) return dsVal;
|
|
528
|
+
}
|
|
529
|
+
const dsAmbient = launchEnvironmentOf(ctx).get("DEEPSEEK_API_KEY")?.value;
|
|
530
|
+
if (dsAmbient !== void 0 && dsAmbient.length > 0) return dsAmbient;
|
|
531
|
+
}
|
|
532
|
+
}, (record) => {
|
|
533
|
+
try {
|
|
534
|
+
(contextService(ctx, "agents")?.currentInitiator?.())?.session?.append?.("web/deepseek-search-llm-request", {
|
|
535
|
+
endpoint: record.endpoint,
|
|
536
|
+
apiVersion: record.apiVersion ?? "2023-06-01",
|
|
537
|
+
body: record.body
|
|
538
|
+
});
|
|
539
|
+
} catch {}
|
|
508
540
|
}).search(request, signal);
|
|
509
541
|
}
|
|
510
542
|
};
|
|
@@ -45,6 +45,8 @@ export declare const en: {
|
|
|
45
45
|
readonly contextLow: "Low";
|
|
46
46
|
readonly contextMedium: "Medium";
|
|
47
47
|
readonly contextHigh: "High";
|
|
48
|
+
readonly commandLabel: "Search settings";
|
|
49
|
+
readonly commandDescription: "Configure enhanced web search model route and parameters";
|
|
48
50
|
readonly save: "Save";
|
|
49
51
|
readonly saving: "Saving…";
|
|
50
52
|
readonly reset: "Reset to Profile";
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
import type { Context } from '@deepseek-ai/cordis';
|
|
6
6
|
import z from '@deepseek-ai/schemastery';
|
|
7
7
|
import { EnhancedSearchProvider } from './provider.ts';
|
|
8
|
-
import type { ChatSearchMode, ModelMode, ResolvedConfig, SearchContextSize, SearchProtocol } from './protocols.ts';
|
|
8
|
+
import type { ChatSearchMode, ModelMode, ResolvedConfig, SearchContextSize, SearchProtocol, SearchWireRecord } from './protocols.ts';
|
|
9
9
|
/** Provider id selected by the bundled Web Profile patch. */
|
|
10
10
|
export declare const DEFAULT_PROVIDER_ID = "enhanced-search";
|
|
11
11
|
/** Default Anthropic-compatible search endpoint. */
|
|
12
12
|
export declare const DEFAULT_BASE_URL = "https://api.deepseek.com/anthropic/v1";
|
|
13
13
|
/** Default model aligned with DSH's built-in search provider. */
|
|
14
|
-
export declare const DEFAULT_MODEL = "deepseek-
|
|
14
|
+
export declare const DEFAULT_MODEL = "deepseek-flash";
|
|
15
15
|
/** Credential reference owned by this plugin's fixed and fallback routes. */
|
|
16
16
|
export declare const DEFAULT_API_KEY_ENV = "WEB_SEARCH_ENHANCED_API";
|
|
17
17
|
/** Settings namespace paired with the plugin configuration card. */
|
|
@@ -47,8 +47,8 @@ export declare function resolveConfig(config: Config): ResolvedConfig;
|
|
|
47
47
|
*/
|
|
48
48
|
export declare function resolveRuntimeConfig(ctx: Context, config: Config): Promise<ResolvedConfig>;
|
|
49
49
|
/** Construct a fixed-config provider for tests or custom compositions. */
|
|
50
|
-
export declare function createProvider(config?: Config, fetcher?: typeof fetch): EnhancedSearchProvider;
|
|
50
|
+
export declare function createProvider(config?: Config, fetcher?: typeof fetch, recordRequest?: (record: SearchWireRecord) => void): EnhancedSearchProvider;
|
|
51
51
|
/** Register the provider and its live Web Profile settings section. */
|
|
52
52
|
export declare function apply(ctx: Context, config: Config): void;
|
|
53
53
|
export { EnhancedSearchProvider } from './provider.ts';
|
|
54
|
-
export type { ChatSearchMode, ModelMode, ResolvedConfig, SearchContextSize, SearchProtocol } from './protocols.ts';
|
|
54
|
+
export type { ChatSearchMode, ModelMode, ResolvedConfig, SearchContextSize, SearchProtocol, SearchWireRecord } from './protocols.ts';
|
package/lib/types/protocols.d.ts
CHANGED
|
@@ -7,6 +7,13 @@ export type ModelMode = 'configured' | 'current-session';
|
|
|
7
7
|
export type SearchContextSize = 'low' | 'medium' | 'high';
|
|
8
8
|
/** Chat Completions search activation: official search-model semantics or an explicit vendor extension. */
|
|
9
9
|
export type ChatSearchMode = 'search-model' | 'vendor-options';
|
|
10
|
+
/** Audit record capturing one outbound search request immediately before dispatch. */
|
|
11
|
+
export interface SearchWireRecord {
|
|
12
|
+
readonly endpoint: string;
|
|
13
|
+
readonly protocol: SearchProtocol;
|
|
14
|
+
readonly apiVersion?: string;
|
|
15
|
+
readonly body: Readonly<Record<string, unknown>>;
|
|
16
|
+
}
|
|
10
17
|
/** Fully resolved settings captured at one search operation's entry. */
|
|
11
18
|
export interface ResolvedConfig {
|
|
12
19
|
readonly providerId: string;
|
|
@@ -42,4 +49,5 @@ export declare function parseAnthropicResponse(payload: unknown): WebSearchResul
|
|
|
42
49
|
export declare function parseResponsesResponse(payload: unknown): WebSearchResult;
|
|
43
50
|
/** Parse Chat Completions message text and URL citation annotations. */
|
|
44
51
|
export declare function parseChatCompletionsResponse(payload: unknown): WebSearchResult;
|
|
52
|
+
export declare function appendEndpoint(baseURL: string, protocol: SearchProtocol): string;
|
|
45
53
|
export {};
|
package/lib/types/provider.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { WebSearchProvider, WebSearchRequest, WebSearchResult } from '@deepseek-ai/dsh-web';
|
|
2
|
-
import type { ResolvedConfig } from './protocols.ts';
|
|
2
|
+
import type { ResolvedConfig, SearchWireRecord } from './protocols.ts';
|
|
3
3
|
/** Multi-protocol search provider. One resolved settings snapshot serves each operation. */
|
|
4
4
|
export declare class EnhancedSearchProvider implements WebSearchProvider {
|
|
5
5
|
private readonly resolveConfig;
|
|
6
6
|
private readonly fetcher;
|
|
7
7
|
private readonly resolveApiKey;
|
|
8
|
+
private readonly recordRequest?;
|
|
8
9
|
/** Stable provider id selected by ctx.web. */
|
|
9
10
|
readonly id: string;
|
|
10
11
|
/** Create a provider backed by a dynamic settings resolver. */
|
|
11
|
-
constructor(resolveConfig: () => ResolvedConfig, fetcher?: typeof fetch, resolveApiKey?: (config: ResolvedConfig) => Promise<string | undefined
|
|
12
|
+
constructor(resolveConfig: () => ResolvedConfig, fetcher?: typeof fetch, resolveApiKey?: (config: ResolvedConfig) => Promise<string | undefined>, recordRequest?: ((record: SearchWireRecord) => void) | undefined);
|
|
12
13
|
/** Report whether local configuration can attempt a request without network I/O. */
|
|
13
14
|
available(): boolean;
|
|
14
15
|
/** Execute one server-side web search and normalize its citations. */
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-web-search-enhanced",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Multi-protocol web_search provider for DeepSeek Harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": "^22.19.0 || >=24.0.0",
|
|
9
|
-
"dsh": ">=0.1.
|
|
9
|
+
"dsh": ">=0.1.5-rc.2"
|
|
10
10
|
},
|
|
11
11
|
"main": "./lib/index.js",
|
|
12
12
|
"types": "./lib/types/index.d.ts",
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/Yurzi/dsh-web-search-enhanced.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/Yurzi/dsh-web-search-enhanced#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/Yurzi/dsh-web-search-enhanced/issues"
|
|
46
|
+
},
|
|
39
47
|
"dsh": {
|
|
40
48
|
"bundle": {
|
|
41
49
|
"patch": "./cordis.patch.yml"
|
|
@@ -47,33 +55,31 @@
|
|
|
47
55
|
"@deepseek-ai/dsh-client-ui-renderer",
|
|
48
56
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
49
57
|
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
50
|
-
"@deepseek-ai/dsh-api-remotes"
|
|
58
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-commands"
|
|
51
60
|
]
|
|
52
61
|
}
|
|
53
62
|
},
|
|
54
63
|
"peerDependencies": {
|
|
55
64
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
56
|
-
"@deepseek-ai/dsh-credentials": "^0.1.
|
|
57
|
-
"@deepseek-ai/dsh-launch-environment": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-client-
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-
|
|
63
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
64
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
65
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
66
|
-
"@deepseek-ai/dsh-web": "^0.1.
|
|
67
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
65
|
+
"@deepseek-ai/dsh-credentials": "^0.1.5-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-launch-environment": "^0.1.5-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
|
|
71
|
+
"@deepseek-ai/dsh-client-ui-commands": "^0.1.5-rc.2",
|
|
72
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-web": "^0.1.5-rc.2",
|
|
76
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
68
77
|
"react": "^18.2.0"
|
|
69
78
|
},
|
|
70
79
|
"peerDependenciesMeta": {
|
|
71
80
|
"@deepseek-ai/dsh-client-locale": {
|
|
72
81
|
"optional": true
|
|
73
82
|
},
|
|
74
|
-
"@deepseek-ai/dsh-client-runtime": {
|
|
75
|
-
"optional": true
|
|
76
|
-
},
|
|
77
83
|
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
78
84
|
"optional": true
|
|
79
85
|
},
|
|
@@ -83,6 +89,9 @@
|
|
|
83
89
|
"@deepseek-ai/dsh-client-ui-settings-plugins": {
|
|
84
90
|
"optional": true
|
|
85
91
|
},
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-commands": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
86
95
|
"@deepseek-ai/dsh-client-ui-slots": {
|
|
87
96
|
"optional": true
|
|
88
97
|
},
|
|
@@ -92,18 +101,18 @@
|
|
|
92
101
|
},
|
|
93
102
|
"devDependencies": {
|
|
94
103
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
95
|
-
"@deepseek-ai/dsh-credentials": "^0.1.
|
|
96
|
-
"@deepseek-ai/dsh-launch-environment": "^0.1.
|
|
97
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
98
|
-
"@deepseek-ai/dsh-client-
|
|
99
|
-
"@deepseek-ai/dsh-client-ui-
|
|
100
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
101
|
-
"@deepseek-ai/dsh-client-ui-
|
|
102
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
103
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
104
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
105
|
-
"@deepseek-ai/dsh-web": "^0.1.
|
|
106
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
104
|
+
"@deepseek-ai/dsh-credentials": "^0.1.5-rc.2",
|
|
105
|
+
"@deepseek-ai/dsh-launch-environment": "^0.1.5-rc.2",
|
|
106
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
|
|
107
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
|
|
108
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
|
|
109
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
|
|
110
|
+
"@deepseek-ai/dsh-client-ui-commands": "^0.1.5-rc.2",
|
|
111
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
|
|
112
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
|
|
113
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
|
|
114
|
+
"@deepseek-ai/dsh-web": "^0.1.5-rc.2",
|
|
115
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
107
116
|
"@types/node": "^22.20.0",
|
|
108
117
|
"@types/react": "~18.3.1",
|
|
109
118
|
"react": "^18.2.0",
|