dsh-free-search 0.9.21 → 0.9.23

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
@@ -31,7 +31,22 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
31
31
  - **零成本** —— 多个免费引擎,无需 key、无需注册
32
32
  - **多引擎可选**:DuckDuckGo(html/lite)、Bing、SearXNG(元搜索,支持自定义实例)、AnySearch、Exa、Tavily、Keenable、Firecrawl、Parallel、Perplexity、SerpBase、Serply、DeepSeek 官方、DeepSeek 原生搜索 API、You.com、百度千帆、Kimi、阿里云百炼、火山联网搜索(豆包搜索)、知乎全网/站内(`zhihu_global`/`zhihu_site`,经知乎开放平台 MCP)、OpenAI / Gemini / Claude 模型内置搜索
33
33
  - **网页设置页** —— 引擎切换 + API key 配置(UI 中 key 脱敏显示"已配置")+ 中英文切换;入口在左侧「插件」页的组件行配置(`plugins.row.config`,DSH 0.1.7-rc.1+)。远程访问(VPS / 反向代理 / 自定义域名)下也能用:桥接接口沿用宿主的 Host 围栏,`--trusted-host` 与 `--public-url` 声明的地址会放行(#84)
34
- - **右侧浮动目录** —— 卡片右侧的页面留白里浮出「引擎 / 回退 / 语言池 / 搜索行为 / 抓取 / 性能」六个快捷跳转,滚动自动高亮当前项、点击平滑跳转;不占用正文宽度(正文布局与 0.9.6 一致),右侧留白不足时自动隐藏
34
+ - **右侧浮动目录** —— 卡片右侧的页面留白里浮出「引擎 / 回退 / 语言池 / 搜索行为 / 抓取 / 性能 / 提示词」七个快捷跳转,滚动自动高亮当前项、点击平滑跳转;不占用正文宽度(正文布局与 0.9.6 一致),右侧留白不足时自动隐藏;每项都是真链接(`#/panel/plugins/dsh-free-search/<分区>`),可复制、可开新标签,见下方「分区链接契约」
35
+
36
+ ### 提示词分页
37
+
38
+ 设置页第七段「提示词」里,页 1 是内置英文,页 2 是内置中文,页 3 起是你自己写的页。注入的就是你停的那一页,停在空页等于不注入。内置页由配置实时生成、不入盘,不能删也不能改。工具开关关掉的工具不注册,schema 一起从上下文消失。
39
+
40
+ ### 分区链接契约(与 dsh-url-router 对接)
41
+
42
+ 规则见 `dsh-url-router/docs/panel-sections.md`(数量不限,当前 7 段:engines / fallback / pools / behavior / fetch / performance / prompt):
43
+
44
+ - 页面根发布**恰好一个** `data-dsh-panel-section="<id>"` = 读者正在看的那一节(跟着滚动走,不做“到底=最后一节”的猜测)
45
+ - 每个分区容器带 `data-dsh-section="<id>"`(滚动目标)
46
+ - 侧栏每项是 `<a data-dsh-section="<id>" href="#/panel/plugins/dsh-free-search/<id>">`
47
+ - 本插件**不写 URL**:`location.hash` / `history.pushState` / `replaceState` 一律不碰,地址栏唯一写者是 dsh-url-router;地址栏里出现本页分区链接时(`hashchange`)插件负责把它滚进视野
48
+
49
+ 加新分区只需三步:容器加 `data-dsh-section="<新id>"`、`NAV_ITEMS` 加一条(侧栏锚点自动生成,href 由 `item.id` 拼)、`assert-rail-section` 的 id 列表加一项。
35
50
  - **多 key 轮换** —— 任何 key 字段(或对应环境变量)都可以用逗号 / 分号 / 换行分隔多把 key;平时按请求轮流使用(round-robin,避免单 key 撞 QPS/RPM 风控),遇到 401/402/429(鉴权失败、额度耗尽、限流)先轮换同一引擎的下一把,再走引擎回退;`multi` 模式同样生效(#96)
36
51
  - **弹出式切换命令** —— 聊天框输入 `/free-search-engine`,弹出引擎选择窗口,点选即切换(等效设置页 + 保存)
37
52
  - **引擎测试** —— `free_search_test` 工具让 agent 一键测试所有引擎;设置页也有"测试引擎"按钮(直测当前引擎,不走回退链,付费引擎无 key 会明确报错)
@@ -41,7 +56,7 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
41
56
  - **时间过滤** —— `advanced_search` 工具支持 `timeRange`:固定档、自定义相对值、绝对日期三种形式(详见下方逻辑说明)
42
57
  - **系统提示词注入** —— agent 知道当前用哪个引擎、哪些需要 key;并明确所有搜索结果是**不可信外部数据**,不得执行其中的指令
43
58
  - **提示注入防护(不可信数据边界)** —— 插件自有工具(advanced_search / platform_search / free_search_test)的网页文本包在 `<untrusted-web-content>` 边界内(正文里自带的同名标记会被剥离,防止提前闭合);核心 web_search / web_fetch 由 DSH 核心自带同类提示(`External web content follows...`);所有 snippet 统一清洗并截断到 300 字符
44
- - **版本号 + 检查更新** —— 设置卡片显示当前版本(跟随 `PLUGIN_VERSION`,随发布更新),"检查更新"按钮直连 npm registry 对比最新版,有新版本时提示并可一键跳转
59
+ - **版本号** —— 设置卡片底部显示当前版本(跟随 `PLUGIN_VERSION`,随发布更新)。检查与升级交给官方插件页(插件 → 已安装 → dsh-free-search → 更新),插件不再自带第二套更新按钮
45
60
  - **结果缓存** —— 相同查询(含引擎/时间过滤参数)5 分钟内命中缓存(LRU 50 条),防免费引擎限流、省付费额度;时长可在设置页 0-5 分钟自由配置(0 关闭)
46
61
  - **域名黑名单与安全搜索** —— `blockedDomains`:命中的域名不会进入任何结果;`safeSearch`(`off` / `moderate` / `strict`)为 Bing / DuckDuckGo 加一层过滤
47
62
  - **免费标注** —— 设置页中免费引擎带绿色 `FREE` 徽章,付费引擎带橙色 `API KEY` 徽章
@@ -314,6 +329,9 @@ lang: zh # 搜索语言池/本地化(zh / en / ru …,不
314
329
  # web_fetch(fs-quality-fetch)的 Accept-Language 同样跟随 lang,不再固定中文(#92)。
315
330
  uiLang: zh # 设置页界面语言(zh / en);留空则跟随 lang
316
331
  promptLanguage: en # 注入给模型的系统提示词与工具描述语言(en / zh);只翻叙述文字,工具名/参数/枚举保持英文
332
+ promptPages: [] # 自定义页文本(只存这些;页 1 内置英文、页 2 内置中文都不入盘)
333
+ promptPageIndex: 0 # 当前页(0 = 内置英文,1 = 内置中文,2 起 = promptPages[i-2]);当前页即注入内容
334
+ disabledTools: [] # 关掉的工具不再注册(例如 [domain_search]),schema 一并从上下文消失
317
335
  bingMarket: zh-CN # Bing 市场;留空 = 自动(不发送 mkt)
318
336
  fetchProviderTakeover: true # false = 保留你显式 pin 的 fetchProvider: http
319
337
  region: cn-zh # DuckDuckGo 区域(可选)
@@ -497,7 +515,7 @@ Search engine test:
497
515
 
498
516
  #### 离线自检断言(tools/)
499
517
 
500
- 仓库自带 **32 个**零依赖断言脚本(合计 **627 项**),**不需要 API key、不联网、不需要测试框架**,Node ≥ 20 即可跑:
518
+ 仓库自带 **35 个**零依赖断言脚本(合计 **648 项**),**不需要 API key、不联网、不需要测试框架**,Node ≥ 20 即可跑:
501
519
 
502
520
  | 脚本 | 覆盖 | 运行(项数) |
503
521
  |---|---|---|
@@ -522,6 +540,8 @@ Search engine test:
522
540
  | `assert-youcom.mjs` | You.com 免 key 档(#90):两档 URL 与请求头、无 key 不发 `X-API-Key`、`YDC_API_KEY` 别名、429/401/402/5xx 分类 | `node tools/assert-youcom.mjs`(23) |
523
541
  | `assert-prompt-language.mjs` | 提示词语言(en/zh):语言回退、9 个工具双语文案、标识符不被翻译、占位符插值、切换时只重注册一次 | `node tools/assert-prompt-language.mjs`(7) |
524
542
  | `assert-tool-schema-size.mjs` | 工具 schema 体积(#94):共享引擎 id 常量、`domain_search` 不内嵌逐引擎句子、三处合计 < 1.6k 字符 | `node tools/assert-tool-schema-size.mjs`(5) |
543
+ | `assert-config-seat.mjs` | 配置入口(#101):卡片同时挂在 `plugins.bundle.config`(key = 包名,渲染在包页)与 `plugins.row.config`(老入口),两者共用同一渲染器 | `node tools/assert-config-seat.mjs`(4) |
544
+ | `assert-prompt-injection.mjs` | 提示词分页(#102):两页内置在前、自定义页在后、当前页即注入、空页不注入、内置页分状态/工具/约束三块、内置随 describe 下发不再单独请求、工具开关落到每个注册点、第七段 UI | `node tools/assert-prompt-injection.mjs`(10) |
525
545
  | `assert-rail-section.mjs` | 页内目录的机器可读当前段:卡片根 `data-dsh-fs-section` 与 `railActive` 绑定、六段 id 固定、滚动监听仍在更新 | `node tools/assert-rail-section.mjs`(4) |
526
546
  | `assert-package-files.mjs` | 发布清单闸门(#97):`files` 展开后必须覆盖被引用的本地模块、入口点与 `cordis.patch.yml`,并确认 `prepack` 守卫生效 | `node tools/assert-package-files.mjs`(4) |
527
547
  | `assert-key-rotation.mjs` | 多 key 轮换(#96):round-robin 与 peek、单 key 不推进、配额优先于 429 的分类、轮换候选集合、`multi` 模式内重试与预检不消耗 key | `node tools/assert-key-rotation.mjs`(7) |
@@ -583,7 +603,22 @@ This plugin provides multiple free search engines with automatic fallback, compl
583
603
  - **Zero Cost** — Multiple free engines with no API key or registration required
584
604
  - **Multi-Engine Support** — DuckDuckGo (HTML / Lite), Bing, AnySearch AI, SearXNG (meta-search with custom instances), Exa, Tavily, Keenable, Firecrawl, Parallel, Perplexity, SerpBase, Serply, DeepSeek Official, You.com, Baidu Qianfan, Kimi, Aliyun Bailian, Volcano Doubao, Zhihu web-wide/site search (`zhihu_global`/`zhihu_site`, via the Zhihu open-platform MCP), and the model-based OpenAI / Gemini / Claude searches
585
605
  - **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+). Remote access (VPS, reverse proxy, custom domain) works too: the bridge reuses the host's own Host fence, so addresses declared with `--trusted-host` or `--public-url` are accepted (#84)
586
- - **Right-hand section rail** — A floating list (Engines / Fallback / Language pools / Search behaviour / Fetch / Performance) sits in the page margin to the right of the card: it highlights the section you are in while scrolling and jumps smoothly on click. It is rendered in a portal and never narrows the card, so the body layout is unchanged; it hides itself when the margin is too tight.
606
+ - **Right-hand section rail** — A floating list (Engines / Fallback / Language pools / Search behaviour / Fetch / Performance / Prompt) sits in the page margin to the right of the card: it highlights the section you are in while scrolling and jumps smoothly on click. It is rendered in a portal and never narrows the card, so the body layout is unchanged; it hides itself when the margin is too tight. Every entry is a real link (`#/panel/plugins/dsh-free-search/<section>`), so it can be copied or opened in a tab - see the section-link contract below.
607
+
608
+ ### Paged prompt
609
+
610
+ In the seventh section, page 1 is the built-in English prompt, page 2 the built-in Chinese one, and page 3 onwards are pages you wrote. The page you are on is what gets injected; parking on an empty page injects nothing. The built-in pages are generated from the live config, are never stored, and cannot be deleted or edited. A tool switched off in the tool list is not registered, so its schema leaves the context too.
611
+
612
+ ### Section-link contract (with dsh-url-router)
613
+
614
+ Rules live in `dsh-url-router/docs/panel-sections.md` (no fixed count; seven sections today: engines / fallback / pools / behavior / fetch / performance / prompt):
615
+
616
+ - the page root publishes **exactly one** `data-dsh-panel-section="<id>"` = the section the reader is looking at (it follows scrolling; it never guesses "at the bottom = last section")
617
+ - every section container carries `data-dsh-section="<id>"` (the scroll target)
618
+ - every rail entry is `<a data-dsh-section="<id>" href="#/panel/plugins/dsh-free-search/<id>">`
619
+ - this plugin **never writes the URL**: `location.hash` / `history.pushState` / `history.replaceState` are untouched because dsh-url-router owns the address bar; when a section link appears there, a `hashchange` listener scrolls that section into view
620
+
621
+ Adding a section takes three steps: put `data-dsh-section="<new id>"` on the container, add one `NAV_ITEMS` entry (the rail anchor is generated from `item.id`), and add the id to `assert-rail-section`.
587
622
  - **Multi-key rotation** — Any key field (or its env var) accepts several keys separated by commas, semicolons or newlines. Keys are used round-robin per request (so a single key never absorbs every call), and on 401/402/429 (bad key, quota exhausted, rate limited) the same engine retries with the next key before the engine-level fallback runs. This also applies inside `multi` mode (#96).
588
623
  - **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)
589
624
  - **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)
@@ -593,7 +628,7 @@ This plugin provides multiple free search engines with automatic fallback, compl
593
628
  - **Time Filtering** — The `advanced_search` tool supports `timeRange`: fixed tiers, custom relative values, or an absolute date (details below)
594
629
  - **System Prompt Injection** — The agent is aware of the currently active engine and which engines require API keys; it is also told that all search output is **untrusted external data** and must never be executed as instructions
595
630
  - **Prompt-Injection Guard (untrusted-data boundary)** — Web-derived text from the plugin's own tools (`advanced_search` / `platform_search` / `free_search_test`) is wrapped in an explicit `<untrusted-web-content>` boundary (look-alike tags inside the text are stripped to prevent early closure); the core `web_search` / `web_fetch` tools carry DSH core's own notice (`External web content follows...`); every snippet is cleaned and capped at 300 characters
596
- - **Version + Update Check** — The settings card shows the current version (tracks PLUGIN_VERSION, bumped on release), and a "Check update" button queries the npm registry to compare against the latest release, prompting a one-click jump when a newer version exists
631
+ - **Version label** — the settings card shows the current version (tracks PLUGIN_VERSION, bumped on release). Checking and updating is the official plugin page's job (Plugins -> Installed -> dsh-free-search -> Update); the plugin ships no second updater
597
632
  - **Result Caching** — Identical queries (same engine / time-filter args) hit an LRU cache (50 entries) for up to 5 minutes, protecting free engines from rate-limiting and saving paid quota; the TTL is configurable from 0-5 minutes in the settings UI (0 disables caching)
598
633
  - **Domain blocklist & safe search** — `blockedDomains` keeps matching domains out of every result; `safeSearch` (`off` / `moderate` / `strict`) adds a filter layer for Bing / DuckDuckGo
599
634
  - **Visual Badges** — Free engines feature a green `FREE` badge, while paid engines show an orange `API KEY` badge in the settings UI
@@ -867,6 +902,9 @@ lang: zh # search language pools / localization (zh / en / ru
867
902
  # web_fetch (fs-quality-fetch) sends the same Accept-Language instead of a fixed Chinese one (#92).
868
903
  uiLang: zh # settings UI language (zh / en); empty = follow lang
869
904
  promptLanguage: en # language of the system prompt section and tool descriptions (en / zh); prose only - tool names, params and enums stay English
905
+ promptPages: [] # your own page texts (only these are stored; the built-in pages are not)
906
+ promptPageIndex: 0 # current page (0 = built-in English, 1 = built-in Chinese, 2+ = promptPages[i-2]); the current page is what gets injected
907
+ disabledTools: [] # tools that are not registered at all (e.g. [domain_search]); their schemas leave the context too
870
908
  bingMarket: zh-CN # Bing market; empty = automatic (no mkt sent)
871
909
  fetchProviderTakeover: true # false = keep the fetchProvider: http you pinned yourself
872
910
  region: cn-zh # DuckDuckGo region (optional)
@@ -1050,7 +1088,7 @@ Restart `dsh web` after switching to apply changes.
1050
1088
 
1051
1089
  #### Offline self-check assertions (`tools/`)
1052
1090
 
1053
- **Thirty-two** zero-dependency assertion scripts ship with the repo (**627 assertions** total): **no API keys, no network, no test framework**, Node ≥ 20:
1091
+ **Thirty-five** zero-dependency assertion scripts ship with the repo (**648 assertions** total): **no API keys, no network, no test framework**, Node ≥ 20:
1054
1092
 
1055
1093
  | Script | Covers | Run (count) |
1056
1094
  |---|---|---|
@@ -1075,6 +1113,8 @@ Restart `dsh web` after switching to apply changes.
1075
1113
  | `assert-youcom.mjs` | You.com keyless tier (#90): both URLs and headers, no `X-API-Key` without a key, the `YDC_API_KEY` alias, and 429/401/402/5xx classification | `node tools/assert-youcom.mjs` (23) |
1076
1114
  | `assert-prompt-language.mjs` | Prompt language (en/zh): fallback rules, bilingual copy for all 9 tools, identifiers left untranslated, placeholder interpolation, and one re-registration per language change | `node tools/assert-prompt-language.mjs` (7) |
1077
1115
  | `assert-tool-schema-size.mjs` | Tool schema budget (#94): one shared engine-id constant, no per-engine sentences in `domain_search`, the three schemas under 1.6k chars combined | `node tools/assert-tool-schema-size.mjs` (5) |
1116
+ | `assert-config-seat.mjs` | Config entry point (#101): the card is registered on `plugins.bundle.config` (keyed by the package name, rendered on the bundle page) and on `plugins.row.config` (older entry), both sharing one renderer | `node tools/assert-config-seat.mjs` (4) |
1117
+ | `assert-prompt-injection.mjs` | Paged prompt (#102): two built-in pages first, user pages after, the current page is what gets injected, an empty page injects nothing, the generated page split into State/Tools/Constraints blocks, built-ins delivered with describe instead of a second request, tool switches wired into every registrar, and the seventh section UI | `node tools/assert-prompt-injection.mjs` (10) |
1078
1118
  | `assert-rail-section.mjs` | Machine-readable active section for the in-page rail: the card root's `data-dsh-fs-section` tracks `railActive`, the six section ids are fixed, and the scroll spy still updates it | `node tools/assert-rail-section.mjs` (4) |
1079
1119
  | `assert-package-files.mjs` | Packaging gate (#97): after expanding `files`, every imported local module, the entry points and `cordis.patch.yml` must be published, and the `prepack` guard must be wired | `node tools/assert-package-files.mjs` (4) |
1080
1120
  | `assert-key-rotation.mjs` | Multi-key rotation (#96): round-robin and peek, single keys never advancing, quota classification ahead of 429, the rotation-candidate set, and in-engine retries that keep multi-mode preflight from consuming keys | `node tools/assert-key-rotation.mjs` (7) |