dsh-free-search 0.4.39 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,7 +29,7 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
29
29
  ### 特性
30
30
 
31
31
  - **零成本** —— 多个免费引擎,无需 key、无需注册
32
- - **多引擎可选**:DuckDuckGo(html/lite)、Bing、SearXNG(元搜索,支持自定义实例)、AnySearch、Exa、Tavily、Keenable、Firecrawl、Parallel、Perplexity、SerpBase、DeepSeek 官方
32
+ - **多引擎可选**:DuckDuckGo(html/lite)、Bing、SearXNG(元搜索,支持自定义实例)、AnySearch、Exa、Tavily、Keenable、Firecrawl、Parallel、Perplexity、SerpBase、DeepSeek 官方、You.com
33
33
  - **网页设置页** —— 引擎切换 + API key 配置(UI 中 key 脱敏显示"已配置")+ 中英文切换;入口在左侧「插件」页的组件行配置(`plugins.row.config`,DSH 0.1.7-rc.1+)
34
34
  - **弹出式切换命令** —— 聊天框输入 `/free-search-engine`,弹出引擎选择窗口,点选即切换(等效设置页 + 保存)
35
35
  - **引擎测试** —— `free_search_test` 工具让 agent 一键测试所有引擎;设置页也有"测试引擎"按钮(直测当前引擎,不走回退链,付费引擎无 key 会明确报错)
@@ -50,6 +50,7 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
50
50
 
51
51
  | id | 引擎 | 费用 | 说明 |
52
52
  |---|---|---|---|
53
+ | `auto` | Auto 智能路由 | 动态 | **根据查询语言/时间条件自动选路**(含中文优先 Bing/Baidu/Aliyun/AnySearch,英文优先 Bing/Exa/Tavily;时间过滤优先支持引擎),末尾全量回退 |
53
54
  | `ddg` | DuckDuckGo HTML | 免费 | 偶发限流(反爬),解封自动恢复 |
54
55
  | `ddg-lite` | DuckDuckGo Lite | 免费 | 轻量版,同上 |
55
56
  | `bing` | Bing | 免费 | **默认引擎**,最稳定,中文优化(zh-CN) |
@@ -63,6 +64,10 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
63
64
  | `perplexity` | Perplexity | 付费 | 需 `PERPLEXITY_API_KEY` |
64
65
  | `serpbase` | SerpBase | 付费 | 需 `SERPBASE_API_KEY`(serpbase.dev,注册送 100 次免费额度) |
65
66
  | `deepseek-official` | DeepSeek 官方 | 付费 | 需 `DEEPSEEK_API_KEY` |
67
+ | `you` | You.com | 付费 | 需 `YOUCOM_API_KEY`(you.com/platform/api-keys,注册即送免费额度) |
68
+ | `baidu` | 百度千帆 AI 搜索 | 额度 | 需 `BAIDU_API_KEY`(千帆 AI 搜索,每日赠 50 次,超额按量后付费);中文全网,支持时间过滤 |
69
+ | `kimi` | Kimi(Moonshot)联网搜索 | 付费 | 需 `MOONSHOT_API_KEY`(basic 约 ¥0.01/次),返回带正文 chunks 的中文结果 |
70
+ | `aliyun` | 阿里云百炼 EnhancedSearch | 付费 | 需 `DASHSCOPE_API_KEY`(MCP search_pro,约 ¥0.03/次,新用户 200 次免费包);中文全网带来源 hostname |
66
71
 
67
72
  - **默认引擎为 `bing`**(免费且最稳定),安装后开箱即用。
68
73
  - **自动回退**:任何引擎失败(免费限流/反爬,付费缺 key/无效/网络错误)都会自动轮流尝试下一个引擎——先试其他已配 key 的付费引擎,再试免费引擎(Bing/AnySearch 等),并在结果中附带回退提示——搜索不会因引擎问题直接失败。
@@ -74,6 +79,7 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
74
79
  - Perplexity:<https://www.perplexity.ai/settings/api>
75
80
  - SerpBase:<https://serpbase.dev>
76
81
  - DeepSeek:<https://platform.deepseek.com/api_keys>
82
+ - You.com:<https://you.com/platform/api-keys>
77
83
 
78
84
  #### 为什么免费引擎不需要 key?
79
85
 
@@ -96,6 +102,31 @@ dsh plugin --profile web add /path/to/dsh-free-search
96
102
  dsh web
97
103
  ```
98
104
 
105
+ #### 接管行为与验证
106
+
107
+ 插件加载后**自动接管搜索**:
108
+
109
+ - `web.searchProvider` 未设置,或仍是 DSH 出厂默认的官方搜索 `deepseek-official` 时,自动切换为本插件;
110
+ - 如果(你或别的插件)已显式选择其他 provider,本插件不抢占,只在启动日志输出 WARN 与切换用的 YAML。
111
+
112
+ 正常安装(`dsh plugin add` / 插件管理器)时,插件自带的 **bundle patch**(`cordis.patch.yml`)还会在配置层显式写入 `searchProvider: ddg`;即使这层没生效(例如把插件作为普通依赖手工安装、或 profile patch 整体覆盖了 `web` 条目),上面的运行时兜底也会接管官方默认,不会再静默退回官方搜索。
113
+
114
+ 需要显式声明、或从其他 provider 切过来时(`profiles/<profile>/cordis.patch.yml`):
115
+
116
+ ```yaml
117
+ # 让 harness 的 web_search 使用本插件。
118
+ # DSH 0.1.2+ 的 patch 是"整段覆盖 config"而不是深度合并:
119
+ # 已有 web 条目里的字段(如 fetchProvider)必须在这里一并重述,否则会被抹掉。
120
+ - id: web
121
+ config:
122
+ searchProvider: ddg
123
+ fetchProvider: http
124
+ ```
125
+
126
+ - `searchProvider: ddg` —— `ddg` 是**本插件注册的 provider id(固定值)**,不是"使用 DuckDuckGo 引擎"的意思;具体用哪个引擎由设置页的 `provider` 字段决定(可填 `bing`/`baidu`/`auto` 等)。
127
+ - `fetchProvider: http` —— 官方网页抓取(web-fetch-http),请保留;漏掉会导致网页抓取失效或重复注册。
128
+ - 想改回官方搜索:在插件管理器里停用本插件条目即可。
129
+
99
130
  #### 姊妹插件:dsh-preset-workbench(预设工作台)
100
131
 
101
132
  同作者的**姊妹插件**:在设置页里可视化创建/编辑 Agent 预设——分段提示词、15 项能力开关、内置「鲸鱼娘 / 梁神模式」模板,不用手写 YAML。两者搭配:**free-search 解决"AI 联网搜索"、preset-workbench 解决"AI 人设能力编排"**,都是纯免费、开箱即用。
@@ -121,7 +152,7 @@ dsh web
121
152
  配置页提供:
122
153
 
123
154
  - **Search engine**:下拉框切换引擎,保存即生效
124
- - **API keys**:为 Exa / Tavily / Keenable / Firecrawl / Parallel / Perplexity / DeepSeek 填写 key(密码框,保存后只显示"已配置";Exa / Tavily / Keenable / Firecrawl / Parallel 不填也可免 key 使用)
155
+ - **API keys**:为 Exa / Tavily / Keenable / Firecrawl / Parallel / Perplexity / DeepSeek / SerpBase / You.com 填写 key(密码框,保存后只显示"已配置";Exa / Tavily / Keenable / Firecrawl / Parallel 不填也可免 key 使用)
125
156
  - **推荐**:付费引擎 key 建议写入 harness 凭据中心 `~/.dsh/.credentials.yaml`(如 `DEEPSEEK_API_KEY: sk-...`,与官方 LLM provider 一致,一处管理所有 key)。插件读取优先级:凭据中心 > 设置页 > 环境变量,设置页填的 key 仅作为遗留兼容。
126
157
  - **Test engine**:直测当前引擎可用性(不走回退链,付费引擎无 key 会明确报错)
127
158
  - **Use Bing default**:把当前搜索引擎切回稳定的免费 Bing;`Discard` 只撤销尚未保存的编辑
@@ -159,7 +190,7 @@ DSH 0.1.7-rc.1 起,配置跟随 profile 的插件条目保存:设置页与 `
159
190
 
160
191
  ```yaml
161
192
  # profiles/<profile>/cordis.patch.yml 中该条目的 config:
162
- provider: bing # ddg / ddg-lite / bing / searxng / anysearch / exa / tavily / keenable / firecrawl / parallel / perplexity / serpbase / deepseek-official
193
+ provider: bing # ddg / ddg-lite / bing / searxng / anysearch / exa / tavily / keenable / firecrawl / parallel / perplexity / serpbase / deepseek-official / you
163
194
  lang: zh # 设置页界面语言(zh / en)
164
195
  bingMarket: zh-CN # Bing 市场
165
196
  region: cn-zh # DuckDuckGo 区域(可选)
@@ -217,6 +248,15 @@ Search engine test:
217
248
 
218
249
  示例对话:*"帮我搜最近 3 天关于 DSH 的新闻"* → agent 调用 `advanced_search`,`timeRange: "3d"`。
219
250
 
251
+ #### 多源并发合并搜索(multi_search)
252
+
253
+ 当需要对重要问题做**多源交叉验证**、避免单一引擎偏差或单源死锁时,可以让 agent 调用 `multi_search` 工具:
254
+
255
+ - **并发请求**:默认基于当前查询类型并发请求前 3 个优选引擎(或显式传入 `engines` 列表),各引擎独立解析 API Key 与容错(缺 key 引擎自动跳过,不阻断其他引擎)。
256
+ - **去重与合并**:按规范化 URL 去除结尾斜杠并合并结果,多引擎共同命中的条目优先置顶排在最前,并在结果附带 `seenIn` 命中来源清单(如 `[seen in: bing, exa]`)。
257
+ - **不可信边界与清洗**:严格遵守 `<untrusted-web-content>` 数据边界,正文 snippet 统一清洗。
258
+ - ⚠️ 注:多源并发会消耗更多 API 配额,建议在需要多角度核验时按需使用。
259
+
220
260
  #### 抓取网页内容(web_fetch)
221
261
 
222
262
  搜索到 URL 后,可以让 agent **读取网页全文**(如"打开第一个链接看看内容")。`web_fetch` 工具已启用(官方 `dsh-web-fetch-http` provider):
@@ -298,7 +338,7 @@ This plugin provides multiple free search engines with automatic fallback, compl
298
338
  ### Features
299
339
 
300
340
  - **Zero Cost** — Multiple free engines with no API key or registration required
301
- - **Multi-Engine Support** — DuckDuckGo (HTML / Lite), Bing, AnySearch AI, SearXNG (meta-search with custom instances), Exa, Tavily, Keenable, Firecrawl, Parallel, Perplexity, SerpBase, and DeepSeek Official
341
+ - **Multi-Engine Support** — DuckDuckGo (HTML / Lite), Bing, AnySearch AI, SearXNG (meta-search with custom instances), Exa, Tavily, Keenable, Firecrawl, Parallel, Perplexity, SerpBase, DeepSeek Official, and You.com
302
342
  - **Web Settings UI** — Engine switching, API key configuration (keys masked as "configured" in the UI), and a Chinese/English toggle; the entry is the component-row config (`plugins.row.config`) on the sidebar Plugins page (DSH 0.1.7-rc.1+)
303
343
  - **Popup Switch Command** — Type `/free-search-engine` in the chat: a picker opens with all engines; click one to switch (equivalent to the settings page + save)
304
344
  - **Engine Testing** — `free_search_test` for the agent to check all engines in one call; the settings UI also has a "Test engine" button that tests the selected engine directly (no fallback chain; paid engines without a key report an explicit error)
@@ -319,6 +359,7 @@ If this plugin has been helpful, a ⭐ on [GitHub](https://github.com/DDDMUC/dsh
319
359
 
320
360
  | id | Engine | Cost | Description |
321
361
  |---|---|---|---|
362
+ | `auto` | Auto Smart Routing | Dynamic | **Smartly routes engines based on query language/time filter** (Chinese queries prioritize Bing/Baidu/Aliyun/AnySearch, English queries prioritize Bing/Exa/Tavily; time filters prioritize time-capable engines), with full fallback |
322
363
  | `ddg` | DuckDuckGo HTML | Free | Occasional rate limits (anti-bot challenges); recovers automatically |
323
364
  | `ddg-lite` | DuckDuckGo Lite | Free | Lightweight version; same rate-limit behavior as above |
324
365
  | `bing` | Bing | Free | **Default engine**, most stable, optimized for Chinese (`zh-CN`) |
@@ -332,6 +373,10 @@ If this plugin has been helpful, a ⭐ on [GitHub](https://github.com/DDDMUC/dsh
332
373
  | `perplexity` | Perplexity | Paid | Requires `PERPLEXITY_API_KEY` |
333
374
  | `serpbase` | SerpBase | Paid | Requires `SERPBASE_API_KEY` (serpbase.dev, 100 free queries on signup) |
334
375
  | `deepseek-official` | DeepSeek Official | Paid | Requires `DEEPSEEK_API_KEY` |
376
+ | `you` | You.com | Paid | Requires `YOUCOM_API_KEY` (you.com/platform/api-keys, free tier on signup) |
377
+ | `baidu` | Baidu Qianfan AI Search | Quota | Requires `BAIDU_API_KEY` (Qianfan AI Search, 50 free calls/day then pay-as-you-go); Chinese web-wide, supports time filtering |
378
+ | `kimi` | Kimi (Moonshot) Web Search | Paid | Requires `MOONSHOT_API_KEY` (basic ~¥0.01/call); returns Chinese results with body chunks |
379
+ | `aliyun` | Aliyun Bailian EnhancedSearch | Paid | Requires `DASHSCOPE_API_KEY` (MCP search_pro, ~¥0.03/call, 200 free calls for new users); Chinese web-wide with source hostnames |
335
380
 
336
381
  - **Default engine is `bing`** (free and most stable), ready to use out of the box after installation.
337
382
  - **Auto-failover**: any engine failure (rate-limited free engine, or missing/invalid paid key, network error) automatically tries the next engine — the configured engine first, then other engines (exa/tavily/keenable/firecrawl/parallel are tried even without a key because they have built-in keyless quota), then the remaining free engines (Bing/AnySearch etc.) — with a note attached to the results naming the engine that actually served them (e.g. `Note: perplexity unavailable or failed, using exa.`). Search never fails outright because of engine issues.
@@ -343,6 +388,7 @@ If this plugin has been helpful, a ⭐ on [GitHub](https://github.com/DDDMUC/dsh
343
388
  - Perplexity: <https://www.perplexity.ai/settings/api>
344
389
  - SerpBase: <https://serpbase.dev>
345
390
  - DeepSeek: <https://platform.deepseek.com/api_keys>
391
+ - You.com: <https://you.com/platform/api-keys>
346
392
 
347
393
  #### Why are some engines free?
348
394
 
@@ -365,6 +411,31 @@ Then restart:
365
411
  dsh web
366
412
  ```
367
413
 
414
+ #### Takeover behavior and verification
415
+
416
+ The plugin **takes over search automatically** once loaded:
417
+
418
+ - When `web.searchProvider` is unset, or still the shipped default `deepseek-official`, it switches to this plugin;
419
+ - If you (or another plugin) explicitly selected a different provider, it does not steal it — it only logs a WARN with a copy-pasteable YAML snippet.
420
+
421
+ With a normal install (`dsh plugin add` / plugin manager) the bundled **bundle patch** (`cordis.patch.yml`) additionally writes `searchProvider: ddg` into the config layer. Even if that layer does not apply (e.g. the plugin was added as a plain dependency, or a profile patch replaced the whole `web` entry), the runtime fallback above still takes over from the official default — search no longer silently falls back to the official provider.
422
+
423
+ To declare it explicitly, or to switch over from another provider (`profiles/<profile>/cordis.patch.yml`):
424
+
425
+ ```yaml
426
+ # Route the harness web_search through this plugin.
427
+ # Since DSH 0.1.2 a patch REPLACES the whole entry config (no deep merge):
428
+ # restate any existing web.* fields (e.g. fetchProvider) or they are dropped.
429
+ - id: web
430
+ config:
431
+ searchProvider: ddg
432
+ fetchProvider: http
433
+ ```
434
+
435
+ - `searchProvider: ddg` — `ddg` is this plugin's **fixed provider id**, not "use the DuckDuckGo engine". The engine is chosen by the `provider` field in the settings page (`bing` / `baidu` / `auto` / …).
436
+ - `fetchProvider: http` — the official web-fetch provider; keep it, or page fetching breaks or double-registers.
437
+ - To go back to official search: disable this plugin's entry in the plugin manager.
438
+
368
439
  #### Sister Plugin: dsh-preset-workbench
369
440
 
370
441
  A **sister plugin** by the same author: a **visual workbench for creating/editing agent presets** right inside Settings — sectioned prompts, 15 capability toggles, and built-in "Whale Girl / Liangshen Mode" templates, no YAML needed. Pair them up: **free-search gives your AI web search, preset-workbench shapes its persona & capabilities** — both free and zero-config.
@@ -428,7 +499,7 @@ Since DSH 0.1.7-rc.1 the configuration is stored with the profile's plugin entry
428
499
 
429
500
  ```yaml
430
501
  # config of that entry in profiles/<profile>/cordis.patch.yml:
431
- provider: bing # ddg / ddg-lite / bing / searxng / anysearch / exa / tavily / keenable / firecrawl / parallel / perplexity / serpbase / deepseek-official
502
+ provider: bing # ddg / ddg-lite / bing / searxng / anysearch / exa / tavily / keenable / firecrawl / parallel / perplexity / serpbase / deepseek-official / you
432
503
  lang: zh # settings UI language (zh / en)
433
504
  bingMarket: zh-CN # Bing market
434
505
  region: cn-zh # DuckDuckGo region (optional)
@@ -486,6 +557,15 @@ Ask the agent for *"news from the last week"*, *"releases this month"*, *"update
486
557
 
487
558
  Example: *"Find DSH news from the last 3 days"* → agent calls `advanced_search` with `timeRange: "3d"`.
488
559
 
560
+ #### Multi-Engine Concurrent Search (`multi_search`)
561
+
562
+ When you need **cross-source verification** to prevent biases or fail-safes from a single engine, the agent can call `multi_search`:
563
+
564
+ - **Concurrent Execution**: Defaults to querying the top 3 recommended engines for the query type (or an explicit list passed via `engines`). Each engine independently resolves API keys and handles errors (missing keys are skipped without failing the batch).
565
+ - **Deduplication & Merge**: Normalizes URLs and ranks items by the number of engines that found them (`seenIn` counts), with cross-hit results placed at the top.
566
+ - **Untrusted Boundary**: Enforces the `<untrusted-web-content>` safety wrapper and trims snippets.
567
+ - ⚠️ Note: Running multiple engines in parallel consumes more search quota; use on demand when high source diversity is needed.
568
+
489
569
  #### Fetch Webpage Content (`web_fetch`)
490
570
 
491
571
  After searching, the agent can **read full webpage content** (e.g., *"Open the first link and summarize it"*). The `web_fetch` tool is enabled by default (official `dsh-web-fetch-http` provider):
package/lib/client.js CHANGED
@@ -9,68 +9,96 @@ window.__ModuleLoader__.load({
9
9
 
10
10
  //#region css
11
11
  const css = [
12
- ".dshfs-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;min-width:0;list-style:none;transition:border-color .16s,background .16s;overflow:hidden;margin-bottom:8px}",
13
- ".dshfs-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}",
14
- ".dshfs-header{width:100%;color:inherit;cursor:pointer;text-align:left;font:inherit;background:0 0;border:0;align-items:center;gap:8px;padding:10px 14px;display:flex}",
15
- ".dshfs-header:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
16
- // 插件页 page 模式:页面自带标题,卡片固定展开、表头不可折叠
12
+ ".dshfs-card{font-family:-apple-system, \"SF Pro Text\", \"PingFang SC\", \"Segoe UI\", system-ui, sans-serif;border:1px solid rgba(0,0,0,.06);background:rgba(255,255,255,.72);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-radius:14px;min-width:0;list-style:none;transition:all 180ms cubic-bezier(.4,0,.2,1);overflow:hidden;margin-bottom:12px;box-shadow:0 1px 2px rgba(0,0,0,.04), 0 10px 30px rgba(0,0,0,.06);color:#1d1d1f}",
13
+ "@media (prefers-color-scheme: dark){.dshfs-card{background:rgba(30,30,32,.7);border:1px solid rgba(255,255,255,.08);color:#f5f5f7;box-shadow:0 1px 2px rgba(0,0,0,.2), 0 10px 30px rgba(0,0,0,.3)}}",
14
+ ".dshfs-cardOpen{}",
15
+ ".dshfs-header{width:100%;color:inherit;cursor:pointer;text-align:left;font:inherit;background:0 0;border:0;align-items:center;gap:12px;padding:16px 20px;display:flex;transition:background 180ms cubic-bezier(.4,0,.2,1)}",
16
+ ".dshfs-header:hover:not(:disabled){background:rgba(0,0,0,.03)}",
17
+ "@media (prefers-color-scheme: dark){.dshfs-header:hover:not(:disabled){background:rgba(255,255,255,.04)}}",
17
18
  ".dshfs-pageMode>.dshfs-header{cursor:default}",
18
19
  ".dshfs-pageMode>.dshfs-header:hover{background:0 0}",
19
20
  ".dshfs-pageMode .dshfs-chevron{display:none}",
20
- ".dshfs-headText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex;overflow:hidden}",
21
- ".dshfs-name{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;font-weight:600;overflow:hidden}",
22
- ".dshfs-description{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;font-size:12px;overflow:hidden}",
23
- ".dshfs-pending{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;flex:none;font-size:12px}",
24
- ".dshfs-chevron{color:var(--dsw-alias-label-tertiary);flex:none;font-size:13px;transition:transform .12s}",
21
+ ".dshfs-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex;overflow:hidden}",
22
+ ".dshfs-name{color:inherit;white-space:nowrap;text-overflow:ellipsis;font-weight:600;font-size:15px;overflow:hidden}",
23
+ ".dshfs-description{color:#6e6e73;white-space:nowrap;text-overflow:ellipsis;font-size:13px;overflow:hidden}",
24
+ "@media (prefers-color-scheme: dark){.dshfs-description{color:#a1a1a6}}",
25
+ ".dshfs-pending{color:#e5a720;white-space:nowrap;flex:none;font-size:12px;font-weight:500}",
26
+ ".dshfs-chevron{color:#6e6e73;flex:none;font-size:14px;transition:transform 180ms cubic-bezier(.4,0,.2,1)}",
25
27
  ".dshfs-chevronOpen{transform:rotate(180deg)}",
26
- ".dshfs-body{flex-direction:column;gap:14px;padding:0 14px 14px;display:flex}",
27
- ".dshfs-footer{justify-content:space-between;align-items:center;gap:8px;display:flex;flex-wrap:wrap}",
28
- ".dshfs-footerLeft{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}",
29
- ".dshfs-footerRight{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
30
- ".dshfs-failed{color:var(--dsw-alias-state-error-primary);font-size:12px}",
31
- ".dshfs-testOk{color:#7ddb9c;font-size:12px;line-height:1.5}",
32
- ".dshfs-resultRow{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-width:0;margin-top:2px}",
33
- ".dshfs-field{flex-direction:column;gap:4px;min-width:0;display:flex}",
34
- ".dshfs-label{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}",
35
- ".dshfs-select{border:1px solid var(--dsw-alias-border-l2);font:inherit;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border-radius:6px;padding:6px 8px;font-size:13px;transition:border-color .13s,box-shadow .13s;width:100%}",
36
- ".dshfs-select:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}",
37
- // 下拉选项列表配色锁死:不随皮肤变量变化(皮肤只影响 select 框体本身)
38
- // color-scheme 让浏览器原生下拉按主题渲染;option 显式固定底色/文字色兜底
28
+ "@media (prefers-color-scheme: dark){.dshfs-chevron{color:#a1a1a6}}",
29
+ "@keyframes dshfsFadeIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}",
30
+ ".dshfs-body{flex-direction:column;gap:20px;padding:0 20px 20px;display:flex;animation:dshfsFadeIn 240ms cubic-bezier(.4,0,.2,1) forwards}",
31
+ "@media (prefers-reduced-motion: reduce){.dshfs-body{animation:none}}",
32
+ ".dshfs-footer{justify-content:space-between;align-items:center;gap:12px;display:flex;flex-wrap:wrap;padding-top:4px}",
33
+ ".dshfs-footerLeft{display:flex;align-items:center;gap:12px;flex-wrap:wrap;min-width:0}",
34
+ ".dshfs-footerRight{display:flex;align-items:center;gap:10px;flex-wrap:wrap}",
35
+ ".dshfs-failed{color:#ff3b30;font-size:13px}",
36
+ "@media (prefers-color-scheme: dark){.dshfs-failed{color:#ff453a}}",
37
+ ".dshfs-testOk{color:#34c759;font-size:13px;line-height:1.5}",
38
+ "@media (prefers-color-scheme: dark){.dshfs-testOk{color:#30d158}}",
39
+ ".dshfs-resultRow{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0;margin-top:-4px}",
40
+ ".dshfs-field{flex-direction:column;gap:8px;min-width:0;display:flex}",
41
+ ".dshfs-label{color:inherit;font-size:14px;font-weight:500;display:flex;align-items:center;gap:8px}",
42
+ ".dshfs-select, .dshfs-input{border:1px solid rgba(0,0,0,.1);font:inherit;font-variant-numeric:tabular-nums;color:inherit;background:rgba(0,0,0,.03);border-radius:10px;padding:0 12px;height:38px;font-size:14px;transition:all 160ms cubic-bezier(.4,0,.2,1);width:100%;box-sizing:border-box}",
43
+ "@media (prefers-color-scheme: dark){.dshfs-select, .dshfs-input{border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.05)}}",
44
+ ".dshfs-select{appearance:none;background-image:url(\"data:image/svg+xml;charset=US-ASCII,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%236e6e73' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 12px center;padding-right:32px}",
45
+ "@media (prefers-color-scheme: dark){.dshfs-select{background-image:url(\"data:image/svg+xml;charset=US-ASCII,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%23a1a1a6' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")}}",
46
+ ".dshfs-select:hover:not(:disabled), .dshfs-input:hover:not(:disabled){border-color:rgba(0,0,0,.2);background:rgba(0,0,0,.05)}",
47
+ "@media (prefers-color-scheme: dark){.dshfs-select:hover:not(:disabled), .dshfs-input:hover:not(:disabled){border-color:rgba(255,255,255,.2);background:rgba(255,255,255,.08)}}",
48
+ ".dshfs-select:focus-visible, .dshfs-input:focus-visible{outline:none;border-color:#0071e3;box-shadow:0 0 0 3px rgba(0,113,227,.25);background:#ffffff}",
49
+ "@media (prefers-color-scheme: dark){.dshfs-select:focus-visible, .dshfs-input:focus-visible{border-color:#0a84ff;box-shadow:0 0 0 3px rgba(10,132,255,.25);background:#1c1c1e}}",
50
+ ".dshfs-input:disabled, .dshfs-select:disabled{opacity:.5;cursor:default}",
39
51
  ".dshfs-select{color-scheme:light dark}",
40
- ".dshfs-select option,.dshfs-select optgroup{background-color:#ffffff;color:#1f2328}",
41
- "@media (prefers-color-scheme:dark){.dshfs-select{color-scheme:dark}.dshfs-select option,.dshfs-select optgroup{background-color:#1e1f24;color:#e8e8ea}}",
42
- ".dshfs-input{border:1px solid var(--dsw-alias-border-l2);font:inherit;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border-radius:6px;padding:6px 8px;font-size:13px;transition:border-color .13s,box-shadow .13s;width:100%}",
43
- ".dshfs-ttl{width:88px}",
44
- ".dshfs-fieldRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
45
- ".dshfs-keyStorage{width:auto;min-width:180px}",
46
- ".dshfs-input:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}",
47
- ".dshfs-input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}",
48
- ".dshfs-input:disabled{opacity:.6;cursor:default}",
49
- ".dshfs-hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px}",
50
- ".dshfs-platforms{display:flex;gap:10px;flex-wrap:wrap}",
51
- ".dshfs-platform{display:flex;align-items:center;gap:5px;color:var(--dsw-alias-label-primary);font-size:13px;cursor:pointer}",
52
- ".dshfs-platform input{accent-color:var(--dsw-alias-state-business-primary)}",
53
- ".dshfs-link{color:var(--dsw-alias-state-business-primary);font-size:12px;text-decoration:none;align-self:flex-start;padding:2px 0}",
52
+ ".dshfs-select option, .dshfs-select optgroup{background-color:#ffffff;color:#1d1d1f}",
53
+ "@media (prefers-color-scheme: dark){.dshfs-select{color-scheme:dark}.dshfs-select option, .dshfs-select optgroup{background-color:#1e1f24;color:#f5f5f7}}",
54
+ ".dshfs-ttl{width:100px}",
55
+ ".dshfs-fieldRow{display:flex;align-items:center;gap:12px;flex-wrap:wrap}",
56
+ ".dshfs-keyStorage{width:auto;min-width:200px}",
57
+ ".dshfs-hint{color:#6e6e73;margin:0;font-size:13px;line-height:1.4}",
58
+ "@media (prefers-color-scheme: dark){.dshfs-hint{color:#a1a1a6}}",
59
+ ".dshfs-platforms{display:flex;gap:12px 16px;flex-wrap:wrap}",
60
+ ".dshfs-platform{display:flex;align-items:center;gap:6px;color:inherit;font-size:14px;cursor:pointer}",
61
+ ".dshfs-platform input{accent-color:#0071e3;width:16px;height:16px}",
62
+ "@media (prefers-color-scheme: dark){.dshfs-platform input{accent-color:#0a84ff}}",
63
+ ".dshfs-link{color:#0071e3;font-size:13px;text-decoration:none;align-self:flex-start;padding:2px 0;font-weight:500}",
54
64
  ".dshfs-link:hover{text-decoration:underline}",
55
- ".dshfs-btn{font:inherit;cursor:pointer;border-radius:6px;padding:5px 12px;font-size:13px;transition:background-color .13s,border-color .13s,color .13s}",
56
- ".dshfs-save{border:1px solid var(--dsw-alias-button-info-fill);background:var(--dsw-alias-button-info-fill);color:var(--dsw-alias-label-primary-foreground)}",
57
- ".dshfs-save:hover:not(:disabled){border-color:var(--dsw-alias-button-info-hover);background:var(--dsw-alias-button-info-hover)}",
58
- ".dshfs-save:disabled{opacity:.5;cursor:default}",
59
- ".dshfs-upgrade{border:1px solid rgba(80,200,120,.4);background:rgba(80,200,120,.15);color:#7ddb9c}",
60
- ".dshfs-upgrade:hover:not(:disabled){background:rgba(80,200,120,.28)}",
61
- ".dshfs-upgrade:disabled{opacity:.5;cursor:default}",
62
- ".dshfs-badge{background:var(--dsw-alias-interactive-bg-hover-accent);color:var(--dsw-alias-state-business-primary);white-space:nowrap;border-radius:999px;flex:none;padding:1px 6px;font-size:11px}",
63
- ".dshfs-badgeFree{background:rgba(80,200,120,.15);color:#7ddb9c;border:1px solid rgba(80,200,120,.3);white-space:nowrap;border-radius:999px;flex:none;padding:1px 6px;font-size:11px}",
64
- ".dshfs-badgeKey{background:rgba(240,170,80,.15);color:#f0b060;border:1px solid rgba(240,170,80,.3);white-space:nowrap;border-radius:999px;flex:none;padding:1px 6px;font-size:11px}",
65
- ".dshfs-langToggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;flex:none;padding:2px 8px;font-size:11px;border-radius:6px}",
66
- ".dshfs-update{color:var(--dsw-alias-state-warn-primary);flex:none;font-size:11px;white-space:nowrap;border:1px solid rgba(240,170,80,.3);background:rgba(240,170,80,.12);border-radius:999px;padding:1px 6px}",
67
- ".dshfs-updateOk{color:#7ddb9c;flex:none;font-size:11px;white-space:nowrap;border:1px solid rgba(80,200,120,.3);background:rgba(80,200,120,.12);border-radius:999px;padding:1px 6px}",
68
- ".dshfs-updateLine{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
69
- ".dshfs-updatePill{display:inline-flex;align-items:center;gap:5px;border:1px solid rgba(76,110,245,.35);background:rgba(76,110,245,.10);color:var(--dsw-alias-state-business-primary);font:inherit;font-size:12px;font-weight:600;line-height:1;cursor:pointer;padding:4px 10px;border-radius:999px;text-decoration:none;white-space:nowrap;transition:background-color .13s,border-color .13s}",
70
- ".dshfs-updatePill:hover:not(:disabled){background:rgba(76,110,245,.20);border-color:rgba(76,110,245,.55)}",
71
- ".dshfs-updatePill:disabled{opacity:.6;cursor:default}",
65
+ "@media (prefers-color-scheme: dark){.dshfs-link{color:#0a84ff}}",
66
+ ".dshfs-btn{font:inherit;cursor:pointer;border-radius:10px;padding:0 14px;height:32px;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:500;transition:all 180ms cubic-bezier(.4,0,.2,1);border:1px solid rgba(0,0,0,.1);background:transparent;color:inherit}",
67
+ "@media (prefers-color-scheme: dark){.dshfs-btn{border-color:rgba(255,255,255,.15)}}",
68
+ ".dshfs-btn:hover:not(:disabled){background:rgba(0,0,0,.04)}",
69
+ "@media (prefers-color-scheme: dark){.dshfs-btn:hover:not(:disabled){background:rgba(255,255,255,.06)}}",
70
+ ".dshfs-btn:active:not(:disabled){transform:scale(0.98)}",
71
+ ".dshfs-btn:disabled{opacity:.5;cursor:default}",
72
+ ".dshfs-save{border:none;background:#0071e3;color:#ffffff;box-shadow:0 1px 2px rgba(0,0,0,.1)}",
73
+ ".dshfs-save:hover:not(:disabled){background:#0077ed;transform:translateY(-1px);box-shadow:0 2px 4px rgba(0,0,0,.15)}",
74
+ ".dshfs-save:active:not(:disabled){transform:scale(0.98)}",
75
+ "@media (prefers-color-scheme: dark){.dshfs-save{background:#0a84ff;color:#ffffff}.dshfs-save:hover:not(:disabled){background:#1e8eff}}",
76
+ ".dshfs-upgrade{border:1px solid rgba(52,199,89,.4);background:rgba(52,199,89,.15);color:#248a3d}",
77
+ "@media (prefers-color-scheme: dark){.dshfs-upgrade{color:#30d158}}",
78
+ ".dshfs-upgrade:hover:not(:disabled){background:rgba(52,199,89,.25)}",
79
+ ".dshfs-badge{background:rgba(0,0,0,.05);color:#1d1d1f;white-space:nowrap;border-radius:999px;flex:none;padding:2px 8px;font-size:11px;font-weight:600;letter-spacing:0.3px}",
80
+ "@media (prefers-color-scheme: dark){.dshfs-badge{background:rgba(255,255,255,.1);color:#f5f5f7}}",
81
+ ".dshfs-badgeAuto{background:rgba(175,82,222,.15);color:#af52de;border:1px solid rgba(175,82,222,.2)}",
82
+ "@media (prefers-color-scheme: dark){.dshfs-badgeAuto{background:rgba(191,90,242,.15);color:#bf5af2;border-color:rgba(191,90,242,.2)}}",
83
+ ".dshfs-badgeFree{background:rgba(52,199,89,.15);color:#248a3d;border:1px solid rgba(52,199,89,.2)}",
84
+ "@media (prefers-color-scheme: dark){.dshfs-badgeFree{background:rgba(48,209,88,.15);color:#30d158;border-color:rgba(48,209,88,.2)}}",
85
+ ".dshfs-badgeKey{background:rgba(255,149,0,.15);color:#cc7a00;border:1px solid rgba(255,149,0,.2)}",
86
+ "@media (prefers-color-scheme: dark){.dshfs-badgeKey{background:rgba(255,159,10,.15);color:#ff9f0a;border-color:rgba(255,159,10,.2)}}",
87
+ ".dshfs-langToggle{padding:0 8px;height:24px;font-size:11px;border-radius:8px;color:#6e6e73}",
88
+ "@media (prefers-color-scheme: dark){.dshfs-langToggle{color:#a1a1a6}}",
89
+ ".dshfs-update{color:#cc7a00;flex:none;font-size:12px;white-space:nowrap;border:1px solid rgba(255,149,0,.3);background:rgba(255,149,0,.12);border-radius:999px;padding:2px 8px}",
90
+ "@media (prefers-color-scheme: dark){.dshfs-update{color:#ff9f0a}}",
91
+ ".dshfs-updateOk{color:#248a3d;flex:none;font-size:12px;white-space:nowrap;border:1px solid rgba(52,199,89,.3);background:rgba(52,199,89,.12);border-radius:999px;padding:2px 8px}",
92
+ "@media (prefers-color-scheme: dark){.dshfs-updateOk{color:#30d158}}",
93
+ ".dshfs-updateLine{display:flex;align-items:center;gap:10px;flex-wrap:wrap}",
94
+ ".dshfs-updatePill{display:inline-flex;align-items:center;gap:6px;border:1px solid rgba(0,113,227,.2);background:rgba(0,113,227,.1);color:#0071e3;font:inherit;font-size:13px;font-weight:500;line-height:1;cursor:pointer;padding:6px 12px;border-radius:999px;text-decoration:none;white-space:nowrap;transition:all 180ms cubic-bezier(.4,0,.2,1)}",
95
+ "@media (prefers-color-scheme: dark){.dshfs-updatePill{color:#0a84ff;background:rgba(10,132,255,.15);border-color:rgba(10,132,255,.25)}}",
96
+ ".dshfs-updatePill:hover:not(:disabled){background:rgba(0,113,227,.15);border-color:rgba(0,113,227,.3)}",
97
+ "@media (prefers-color-scheme: dark){.dshfs-updatePill:hover:not(:disabled){background:rgba(10,132,255,.2);border-color:rgba(10,132,255,.4)}}",
98
+ ".dshfs-updatePill:disabled{opacity:.5;cursor:default}",
72
99
  ".dshfs-updateIcon{flex:none;display:block}",
73
- ".dshfs-version{color:var(--dsw-alias-label-tertiary);font-size:11px;font-variant-numeric:tabular-nums;white-space:nowrap}",
100
+ ".dshfs-version{color:#6e6e73;font-size:12px;font-variant-numeric:tabular-nums;white-space:nowrap}",
101
+ "@media (prefers-color-scheme: dark){.dshfs-version{color:#a1a1a6}}"
74
102
  ].join("");
75
103
  const tagId = "dsh-free-search/card.css";
76
104
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
@@ -89,12 +117,12 @@ window.__ModuleLoader__.load({
89
117
  const NS = "web-search-free";
90
118
  const I18N = {
91
119
  zh: {
92
- description: "免费搜索 —— 无需 API key(Bing / DuckDuckGo / AnySearch / Exa / Tavily / Keenable / Firecrawl / Parallel)",
120
+ description: "免费搜索 —— 无需 API key(Bing / DuckDuckGo / AnySearch / Exa / Tavily / Keenable / Firecrawl / Parallel),支持 You.com(API KEY)",
93
121
  unsaved: "未保存",
94
122
  searchEngine: "搜索引擎",
95
123
  visit: "访问官网 →",
96
124
  getKey: "获取 API Key →",
97
- engineHint: "Bing 是最稳定的免费引擎。DuckDuckGo 在共享 IP 上可能限流。API KEY 引擎需在下方填写凭据。",
125
+ engineHint: "Auto 会根据查询语言及时间过滤自动优选引擎。Bing 是最稳定的免费引擎。DuckDuckGo 在共享 IP 上可能限流。API KEY 引擎需在下方填写凭据。",
98
126
  apiKeys: "API 密钥(可选)",
99
127
  anysearchPh: (c) => c ? "AnySearch API 密钥(已配置)" : "AnySearch API 密钥(可选,不填免费匿名,填了提额)",
100
128
  exaPh: (c) => c ? "Exa API 密钥(已配置)" : "Exa API 密钥(可选,不填也可免费使用)",
@@ -105,11 +133,15 @@ window.__ModuleLoader__.load({
105
133
  perplexityPh: (c) => c ? "Perplexity API 密钥(已配置)" : "Perplexity API 密钥(pplx-...)",
106
134
  deepseekPh: (c) => c ? "DeepSeek API 密钥(已配置)" : "DeepSeek API 密钥(sk-...)",
107
135
  serpbasePh: (c) => c ? "SerpBase API 密钥(已配置)" : "SerpBase API 密钥(serpbase.dev 获取)",
136
+ youcomPh: (c) => c ? "You.com API 密钥(已配置)" : "You.com API 密钥(you.com/platform/api-keys 获取)",
137
+ baiduPh: (c) => c ? "Baidu API 密钥(已配置)" : "百度千帆 API 密钥(BAIDU_API_KEY)",
138
+ kimiPh: (c) => c ? "Kimi API 密钥(已配置)" : "Kimi API 密钥(MOONSHOT_API_KEY)",
139
+ aliyunPh: (c) => c ? "Aliyun API 密钥(已配置)" : "阿里云 DashScope 密钥(DASHSCOPE_API_KEY)",
108
140
  keysHint: "密钥读取优先级:.credentials.yaml 凭据中心 > 这里 > 环境变量。推荐把 key 写进凭据中心(与官方 LLM 一致,一处管理)。",
109
141
  keyStorage: "Key 存储位置",
110
142
  keyStorageCred: "凭据中心(推荐)",
111
143
  keyStorageSettings: "设置页(兼容)",
112
- keyStorageCredHint: (c) => `保存后写入 ~/.dsh/.credentials.yaml(最高优先级)。当前已配置:${["anysearch", "exa", "tavily", "keenable", "firecrawl", "parallel", "perplexity", "serpbase", "deepseek"].filter((k) => c[k]).map((k) => k.toUpperCase()).join(", ") || "无"}`,
144
+ keyStorageCredHint: (c) => `保存后写入 ~/.dsh/.credentials.yaml(最高优先级)。当前已配置:${["anysearch", "exa", "tavily", "keenable", "firecrawl", "parallel", "perplexity", "serpbase", "deepseek", "youcom", "baidu", "kimi", "aliyun"].filter((k) => c[k]).map((k) => k.toUpperCase()).join(", ") || "无"}`,
113
145
  keyStorageSettingsHint: "保存后写入当前 profile 的插件条目 config(cordis.patch.yml,优先级低于凭据中心)。",
114
146
  platformSearch: "平台搜索(platform_search 工具)",
115
147
  platformHint: "为 agent 的 platform_search 工具启用平台。禁用的平台会被跳过。",
@@ -158,12 +190,12 @@ window.__ModuleLoader__.load({
158
190
  marketKoKR: "ko-KR —— 韩国",
159
191
  },
160
192
  en: {
161
- description: "Free web search — no API key needed (Bing / DuckDuckGo / AnySearch / Exa / Tavily / Keenable / Firecrawl / Parallel)",
193
+ description: "Free web search — no API key needed (Bing / DuckDuckGo / AnySearch / Exa / Tavily / Keenable / Firecrawl / Parallel), plus You.com (API KEY)",
162
194
  unsaved: "unsaved",
163
195
  searchEngine: "Search engine",
164
196
  visit: "Visit website →",
165
197
  getKey: "Get API Key →",
166
- engineHint: "Bing is the most stable FREE engine. DuckDuckGo may rate-limit on shared IPs. API KEY engines need credentials below.",
198
+ engineHint: "Auto smartly routes engines based on query language and time filter. Bing is the most stable FREE engine. DuckDuckGo may rate-limit on shared IPs. API KEY engines need credentials below.",
167
199
  apiKeys: "API keys (optional)",
168
200
  anysearchPh: (c) => c ? "AnySearch API key (configured)" : "AnySearch API key (optional, free anonymous without; key raises quota)",
169
201
  exaPh: (c) => c ? "Exa API key (configured)" : "Exa API key (optional, free without)",
@@ -174,11 +206,15 @@ window.__ModuleLoader__.load({
174
206
  perplexityPh: (c) => c ? "Perplexity API key (configured)" : "Perplexity API key (pplx-...)",
175
207
  deepseekPh: (c) => c ? "DeepSeek API key (configured)" : "DeepSeek API key (sk-...)",
176
208
  serpbasePh: (c) => c ? "SerpBase API key (configured)" : "SerpBase API key (from serpbase.dev)",
209
+ youcomPh: (c) => c ? "You.com API key (configured)" : "You.com API key (from you.com/platform/api-keys)",
210
+ baiduPh: (c) => c ? "Baidu API key (configured)" : "Baidu Qianfan API key (BAIDU_API_KEY)",
211
+ kimiPh: (c) => c ? "Kimi API key (configured)" : "Kimi API key (MOONSHOT_API_KEY)",
212
+ aliyunPh: (c) => c ? "Aliyun API key (configured)" : "Aliyun DashScope API key (DASHSCOPE_API_KEY)",
177
213
  keysHint: "Key resolution: .credentials.yaml credential center > here > environment variables. Recommended: store keys in the credential center (same as official LLM providers, one place for all).",
178
214
  keyStorage: "Key storage",
179
215
  keyStorageCred: "Credential center (recommended)",
180
216
  keyStorageSettings: "Settings page (legacy)",
181
- keyStorageCredHint: (c) => `Saved to ~/.dsh/.credentials.yaml (highest priority). Currently configured: ${["anysearch", "exa", "tavily", "keenable", "firecrawl", "parallel", "perplexity", "serpbase", "deepseek"].filter((k) => c[k]).map((k) => k.toUpperCase()).join(", ") || "none"}`,
217
+ keyStorageCredHint: (c) => `Saved to ~/.dsh/.credentials.yaml (highest priority). Currently configured: ${["anysearch", "exa", "tavily", "keenable", "firecrawl", "parallel", "perplexity", "serpbase", "deepseek", "youcom", "baidu", "kimi", "aliyun"].filter((k) => c[k]).map((k) => k.toUpperCase()).join(", ") || "none"}`,
182
218
  keyStorageSettingsHint: "Saved to the active profile plugin entry config (cordis.patch.yml; lower priority than the credential center).",
183
219
  platformSearch: "Platform search (platform_search tool)",
184
220
  platformHint: "Enable platforms for the agent's platform_search tool. Disabled platforms are skipped.",
@@ -229,8 +265,9 @@ window.__ModuleLoader__.load({
229
265
  };
230
266
  const tt = (lang) => I18N[lang === "en" ? "en" : "zh"];
231
267
  // 当前插件版本(与 lib/index.js 的 PLUGIN_VERSION 保持一致)
232
- const PLUGIN_VERSION = "0.4.39";
268
+ const PLUGIN_VERSION = "0.5.0";
233
269
  const ENGINES = [
270
+ { id: "auto", label: "Auto · 智能路由", badge: "AUTO", link: null },
234
271
  { id: "ddg", label: "DuckDuckGo · HTML", badge: "FREE", link: "https://duckduckgo.com" },
235
272
  { id: "ddg-lite", label: "DuckDuckGo · Lite", badge: "FREE", link: "https://duckduckgo.com" },
236
273
  { id: "bing", label: "Bing", badge: "FREE", link: "https://www.bing.com" },
@@ -244,6 +281,10 @@ window.__ModuleLoader__.load({
244
281
  { id: "perplexity", label: "Perplexity", badge: "API KEY", link: "https://www.perplexity.ai/settings/api" },
245
282
  { id: "serpbase", label: "SerpBase · Google", badge: "API KEY", link: "https://serpbase.dev" },
246
283
  { id: "deepseek-official", label: "DeepSeek Official", badge: "API KEY", link: "https://platform.deepseek.com/api_keys" },
284
+ { id: "you", label: "You.com", badge: "API KEY", link: "https://you.com/platform/api-keys" },
285
+ { id: "baidu", label: "Baidu · 百度千帆", badge: "API KEY", link: "https://console.bce.baidu.com/qianfan" },
286
+ { id: "kimi", label: "Kimi · Moonshot", badge: "API KEY", link: "https://platform.moonshot.cn" },
287
+ { id: "aliyun", label: "Aliyun · 百炼", badge: "API KEY", link: "https://bailian.console.aliyun.com" },
247
288
  ];
248
289
 
249
290
  async function bridgeDescribe() {
@@ -355,6 +396,10 @@ window.__ModuleLoader__.load({
355
396
  const [perplexityKey, setPerplexityKey] = react.useState("");
356
397
  const [deepseekKey, setDeepseekKey] = react.useState("");
357
398
  const [serpbaseKey, setSerpbaseKey] = react.useState("");
399
+ const [youcomKey, setYoucomKey] = react.useState("");
400
+ const [baiduKey, setBaiduKey] = react.useState("");
401
+ const [kimiKey, setKimiKey] = react.useState("");
402
+ const [aliyunKey, setAliyunKey] = react.useState("");
358
403
  const [platforms, setPlatforms] = react.useState(["github", "v2ex", "bilibili", "reddit", "hn", "stackoverflow", "wikipedia", "npm"]);
359
404
  const [cacheTtl, setCacheTtl] = react.useState(5);
360
405
  const [keysConfigured, setKeysConfigured] = react.useState({});
@@ -394,6 +439,10 @@ window.__ModuleLoader__.load({
394
439
  setPerplexityKey(v.perplexityApiKey ?? "");
395
440
  setDeepseekKey(v.deepseekApiKey ?? "");
396
441
  setSerpbaseKey(v.serpbaseApiKey ?? "");
442
+ setYoucomKey(v.youcomApiKey ?? "");
443
+ setBaiduKey(v.baiduApiKey ?? "");
444
+ setKimiKey(v.kimiApiKey ?? "");
445
+ setAliyunKey(v.aliyunApiKey ?? "");
397
446
  setPlatforms(Array.isArray(v.platforms) && v.platforms.length > 0 ? v.platforms : ["github", "v2ex", "bilibili", "reddit", "hn", "stackoverflow", "wikipedia", "npm"]);
398
447
  setCacheTtl(v.cacheTtl === undefined ? 5 : Math.min(Math.max(Number(v.cacheTtl) ?? 5, 0), 5));
399
448
  // secrets 字段标记哪些 key 已配置(值被脱敏,仅显示"已配置")
@@ -410,6 +459,10 @@ window.__ModuleLoader__.load({
410
459
  if (path === "perplexityApiKey") configured.perplexity = true;
411
460
  if (path === "deepseekApiKey") configured.deepseek = true;
412
461
  if (path === "serpbaseApiKey") configured.serpbase = true;
462
+ if (path === "youcomApiKey") configured.youcom = true;
463
+ if (path === "baiduApiKey") configured.baidu = true;
464
+ if (path === "kimiApiKey") configured.kimi = true;
465
+ if (path === "aliyunApiKey") configured.aliyun = true;
413
466
  }
414
467
  }
415
468
  setKeysConfigured(configured);
@@ -421,7 +474,7 @@ window.__ModuleLoader__.load({
421
474
  const cred = await bridgeCredentialsStatus();
422
475
  if (cred.ok) {
423
476
  const cc = {};
424
- const map = { anysearchApiKey: "anysearch", exaApiKey: "exa", tavilyApiKey: "tavily", keenableApiKey: "keenable", firecrawlApiKey: "firecrawl", parallelApiKey: "parallel", perplexityApiKey: "perplexity", deepseekApiKey: "deepseek", serpbaseApiKey: "serpbase" };
477
+ const map = { anysearchApiKey: "anysearch", exaApiKey: "exa", tavilyApiKey: "tavily", keenableApiKey: "keenable", firecrawlApiKey: "firecrawl", parallelApiKey: "parallel", perplexityApiKey: "perplexity", deepseekApiKey: "deepseek", serpbaseApiKey: "serpbase", youcomApiKey: "youcom", baiduApiKey: "baidu", kimiApiKey: "kimi", aliyunApiKey: "aliyun" };
425
478
  for (const [k, v] of Object.entries(cred.value.configured ?? {})) {
426
479
  if (map[k]) cc[map[k]] = v;
427
480
  }
@@ -467,6 +520,10 @@ window.__ModuleLoader__.load({
467
520
  ["perplexityApiKey", perplexityKey],
468
521
  ["deepseekApiKey", deepseekKey],
469
522
  ["serpbaseApiKey", serpbaseKey],
523
+ ["youcomApiKey", youcomKey],
524
+ ["baiduApiKey", baiduKey],
525
+ ["kimiApiKey", kimiKey],
526
+ ["aliyunApiKey", aliyunKey],
470
527
  ];
471
528
  if (keyStorage === "credentials") {
472
529
  for (const [field, value] of keyFields) {
@@ -605,9 +662,13 @@ window.__ModuleLoader__.load({
605
662
  const t = tt(lang);
606
663
  const title = "Free Search";
607
664
  const description = t.description;
608
- const currentEngine = ENGINES.find((e) => e.id === provider) ?? ENGINES[0];
665
+ const currentEngine = ENGINES.find((e) => e.id === provider) ?? ENGINES.find((e) => e.id === "bing") ?? ENGINES[0];
609
666
  const badgeClass =
610
- currentEngine.badge === "FREE" ? "dshfs-badge dshfs-badgeFree" : "dshfs-badge dshfs-badgeKey";
667
+ currentEngine.badge === "AUTO"
668
+ ? "dshfs-badge dshfs-badgeAuto"
669
+ : currentEngine.badge === "FREE"
670
+ ? "dshfs-badge dshfs-badgeFree"
671
+ : "dshfs-badge dshfs-badgeKey";
611
672
 
612
673
  const toggleLang = () => {
613
674
  setLang((prev) => (prev === "en" ? "zh" : "en"));
@@ -856,6 +917,19 @@ react_jsx_runtime.jsx("div", {
856
917
  setSaveError("");
857
918
  },
858
919
  }),
920
+ react_jsx_runtime.jsx("input", {
921
+ className: "dshfs-input",
922
+ type: "password",
923
+ placeholder: t.youcomPh(keysConfigured.youcom),
924
+ value: youcomKey,
925
+ disabled: !ready || saving,
926
+ onChange: (e) => {
927
+ setYoucomKey(e.target.value);
928
+ setDirty(true);
929
+ setFailed(false);
930
+ setSaveError("");
931
+ },
932
+ }),
859
933
  react_jsx_runtime.jsx("input", {
860
934
  className: "dshfs-input",
861
935
  type: "password",
@@ -869,6 +943,45 @@ react_jsx_runtime.jsx("div", {
869
943
  setSaveError("");
870
944
  },
871
945
  }),
946
+ react_jsx_runtime.jsx("input", {
947
+ className: "dshfs-input",
948
+ type: "password",
949
+ placeholder: t.baiduPh(keysConfigured.baidu),
950
+ value: baiduKey,
951
+ disabled: !ready || saving,
952
+ onChange: (e) => {
953
+ setBaiduKey(e.target.value);
954
+ setDirty(true);
955
+ setFailed(false);
956
+ setSaveError("");
957
+ },
958
+ }),
959
+ react_jsx_runtime.jsx("input", {
960
+ className: "dshfs-input",
961
+ type: "password",
962
+ placeholder: t.kimiPh(keysConfigured.kimi),
963
+ value: kimiKey,
964
+ disabled: !ready || saving,
965
+ onChange: (e) => {
966
+ setKimiKey(e.target.value);
967
+ setDirty(true);
968
+ setFailed(false);
969
+ setSaveError("");
970
+ },
971
+ }),
972
+ react_jsx_runtime.jsx("input", {
973
+ className: "dshfs-input",
974
+ type: "password",
975
+ placeholder: t.aliyunPh(keysConfigured.aliyun),
976
+ value: aliyunKey,
977
+ disabled: !ready || saving,
978
+ onChange: (e) => {
979
+ setAliyunKey(e.target.value);
980
+ setDirty(true);
981
+ setFailed(false);
982
+ setSaveError("");
983
+ },
984
+ }),
872
985
  react_jsx_runtime.jsx("p", {
873
986
  className: "dshfs-hint",
874
987
  children: t.keysHint,
@@ -1172,7 +1285,7 @@ react_jsx_runtime.jsx("div", {
1172
1285
  const current = view?.value?.provider ?? "bing";
1173
1286
  return ENGINES.map((e) => ({
1174
1287
  id: e.id,
1175
- label: `${e.label}${e.badge === "FREE" ? " · 免费" : " · API Key"}`,
1288
+ label: `${e.label}${e.badge === "AUTO" ? " · 智能路由" : e.badge === "FREE" ? " · 免费" : " · API Key"}`,
1176
1289
  detail: e.id === current ? (view?.value?.lang === "en" ? "current" : "当前") : undefined,
1177
1290
  active: e.id === current,
1178
1291
  }));