dsh-free-search 0.4.16 → 0.4.18
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 +9 -3
- package/cordis.patch.yml +3 -0
- package/lib/client.js +29 -1
- package/lib/index.js +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ dsh 默认的搜索 provider 依赖 DeepSeek 官方 API key(`DEEPSEEK_API_KEY`
|
|
|
36
36
|
- **统一引擎回退** —— 任何引擎失败(付费/免费,缺 key/401/限流/网络)自动轮流尝试下一个引擎:首选引擎 → 其他引擎(exa/tavily/keenable 无 key 也会尝试,因为它们自带 keyless 免费额度)→ 剩余免费引擎,搜索永不直接失败;结果顶部注明实际生效的引擎(如 `Note: perplexity unavailable or failed, using exa.`)
|
|
37
37
|
- **时间过滤** —— `advanced_search` 工具支持 `timeRange`:固定档、自定义相对值、绝对日期三种形式(详见下方逻辑说明)
|
|
38
38
|
- **系统提示词注入** —— agent 知道当前用哪个引擎、哪些需要 key
|
|
39
|
-
- **版本号 + 检查更新** —— 设置卡片显示当前版本(v0.4.
|
|
39
|
+
- **版本号 + 检查更新** —— 设置卡片显示当前版本(v0.4.17),"检查更新"按钮直连 npm registry 对比最新版,有新版本时提示并可一键跳转
|
|
40
40
|
- **结果缓存** —— 相同查询(含引擎/时间过滤参数)5 分钟内命中缓存(LRU 50 条),防免费引擎限流、省付费额度;时长可在设置页 0-5 分钟自由配置(0 关闭)
|
|
41
41
|
- **免费标注** —— 设置页中免费引擎带绿色 `FREE` 徽章,付费引擎带橙色 `API KEY` 徽章
|
|
42
42
|
- **网页抓取(web_fetch)** —— 让 agent 抓取网页内容(官方 `dsh-web-fetch-http` provider,纯 JS,零额外依赖)
|
|
@@ -277,7 +277,7 @@ This plugin provides multiple free search engines with automatic fallback, compl
|
|
|
277
277
|
- **Unified Engine Fallback** — Any engine failure (paid or free, missing key, 401, rate limit, network error) automatically tries the next engine: the configured engine first, then other engines (exa/tavily/keenable 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.
|
|
278
278
|
- **Time Filtering** — The `advanced_search` tool supports `timeRange`: fixed tiers, custom relative values, or an absolute date (details below)
|
|
279
279
|
- **System Prompt Injection** — The agent is aware of the currently active engine and which engines require API keys
|
|
280
|
-
- **Version + Update Check** — The settings card shows the current version (v0.4.
|
|
280
|
+
- **Version + Update Check** — The settings card shows the current version (v0.4.17), 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
|
|
281
281
|
- **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)
|
|
282
282
|
- **Visual Badges** — Free engines feature a green `FREE` badge, while paid engines show an orange `API KEY` badge in the settings UI
|
|
283
283
|
- **Webpage Fetching (`web_fetch`)** — Allows the agent to read full webpage contents (official `dsh-web-fetch-http` provider, pure JS, zero extra dependencies)
|
|
@@ -488,4 +488,10 @@ Windows users: The desktop shortcut already includes this configuration (`set NO
|
|
|
488
488
|
|
|
489
489
|
### License
|
|
490
490
|
|
|
491
|
-
MIT
|
|
491
|
+
MIT
|
|
492
|
+
|
|
493
|
+
## safeSearch 安全搜索过滤
|
|
494
|
+
|
|
495
|
+
- 全新配置项 `safeSearch`:`off`(引擎默认,不加参数)/ `moderate` / `strict`
|
|
496
|
+
- 作用于 Bing(adlt)、DuckDuckGo HTML(adlt)、DuckDuckGo Lite(adlt)
|
|
497
|
+
- 默认 `off`:不额外过滤,保持引擎自身默认行为;需要时在「设置 > 插件 > Free Search」切换
|
package/cordis.patch.yml
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
provider: bing
|
|
17
17
|
bingMarket: zh-CN
|
|
18
18
|
# 自动接管默认搜索:让 harness 的 web_search 直接用本插件的 provider(内部含全部引擎)
|
|
19
|
+
# 注意 patch 语义是"整行替换 config"(DSH 0.1.2+):必须保留 fetchProvider,否则
|
|
20
|
+
# web-fetch-http 会被另一层重新注册,与 base bundle 的注册造成 duplicate loader entry。
|
|
19
21
|
- id: web
|
|
20
22
|
config:
|
|
21
23
|
searchProvider: ddg
|
|
24
|
+
fetchProvider: http
|
package/lib/client.js
CHANGED
|
@@ -172,7 +172,7 @@ window.__ModuleLoader__.load({
|
|
|
172
172
|
};
|
|
173
173
|
const tt = (lang) => I18N[lang === "en" ? "en" : "zh"];
|
|
174
174
|
// 当前插件版本(与 lib/index.js 的 PLUGIN_VERSION 保持一致)
|
|
175
|
-
const PLUGIN_VERSION = "0.4.
|
|
175
|
+
const PLUGIN_VERSION = "0.4.17";
|
|
176
176
|
const ENGINES = [
|
|
177
177
|
{ id: "ddg", label: "DuckDuckGo · HTML", badge: "FREE", link: "https://duckduckgo.com" },
|
|
178
178
|
{ id: "ddg-lite", label: "DuckDuckGo · Lite", badge: "FREE", link: "https://duckduckgo.com" },
|
|
@@ -273,6 +273,7 @@ window.__ModuleLoader__.load({
|
|
|
273
273
|
const [open, setOpen] = react.useState(false);
|
|
274
274
|
const [state, setState] = react.useState({ status: "loading" });
|
|
275
275
|
const [provider, setProvider] = react.useState("bing");
|
|
276
|
+
const [safeSearch, setSafeSearch] = react.useState("off");
|
|
276
277
|
const [exaKey, setExaKey] = react.useState("");
|
|
277
278
|
const [tavilyKey, setTavilyKey] = react.useState("");
|
|
278
279
|
const [keenableKey, setKeenableKey] = react.useState("");
|
|
@@ -303,6 +304,7 @@ window.__ModuleLoader__.load({
|
|
|
303
304
|
if (view) {
|
|
304
305
|
const v = view.value ?? {};
|
|
305
306
|
setProvider(v.provider ?? "ddg");
|
|
307
|
+
setSafeSearch(v.safeSearch === "strict" || v.safeSearch === "moderate" ? v.safeSearch : "off");
|
|
306
308
|
setLang(v.lang === "en" ? "en" : "zh");
|
|
307
309
|
setExaKey(v.exaApiKey ?? "");
|
|
308
310
|
setTavilyKey(v.tavilyApiKey ?? "");
|
|
@@ -383,6 +385,7 @@ window.__ModuleLoader__.load({
|
|
|
383
385
|
const ops = [{ op: "set", path: ["provider"], value: provider }];
|
|
384
386
|
ops.push({ op: "set", path: ["lang"], value: lang });
|
|
385
387
|
ops.push({ op: "set", path: ["keyStorage"], value: keyStorage });
|
|
388
|
+
ops.push({ op: "set", path: ["safeSearch"], value: safeSearch });
|
|
386
389
|
if (keyStorage !== "credentials") {
|
|
387
390
|
// settings 模式:key 仍写 settings.yaml(旧行为)
|
|
388
391
|
for (const [field, value] of keyFields) {
|
|
@@ -570,6 +573,31 @@ react_jsx_runtime.jsx("div", {
|
|
|
570
573
|
}),
|
|
571
574
|
],
|
|
572
575
|
}),
|
|
576
|
+
react_jsx_runtime.jsx("div", {
|
|
577
|
+
className: "dshfs-field",
|
|
578
|
+
children: [
|
|
579
|
+
react_jsx_runtime.jsx("div", {
|
|
580
|
+
className: "dshfs-label",
|
|
581
|
+
children: "Safe search filter (adlt)",
|
|
582
|
+
}),
|
|
583
|
+
react_jsx_runtime.jsx("select", {
|
|
584
|
+
className: "dshfs-select",
|
|
585
|
+
value: safeSearch,
|
|
586
|
+
style: { colorScheme: selectColorScheme },
|
|
587
|
+
disabled: !ready || saving,
|
|
588
|
+
onChange: (e) => setSafeSearch(e.target.value),
|
|
589
|
+
children: [
|
|
590
|
+
react_jsx_runtime.jsx("option", { value: "off", children: "Off - engine default (no filtering)" }, "off"),
|
|
591
|
+
react_jsx_runtime.jsx("option", { value: "moderate", children: "Moderate - Bing default" }, "moderate"),
|
|
592
|
+
react_jsx_runtime.jsx("option", { value: "strict", children: "Strict" }, "strict"),
|
|
593
|
+
],
|
|
594
|
+
}),
|
|
595
|
+
react_jsx_runtime.jsx("p", {
|
|
596
|
+
className: "dshfs-hint",
|
|
597
|
+
children: "Applies to bing (adlt), ddg, ddg-lite (adlt degree). If you see the engine's own filtering, adjust here.",
|
|
598
|
+
}),
|
|
599
|
+
],
|
|
600
|
+
}),
|
|
573
601
|
react_jsx_runtime.jsx("div", {
|
|
574
602
|
className: "dshfs-field",
|
|
575
603
|
children: [
|
package/lib/index.js
CHANGED
|
@@ -21,7 +21,7 @@ const FREE_ENGINES = ["ddg", "ddg-lite", "bing", "searxng", "anysearch"];
|
|
|
21
21
|
const ALL_ENGINES = ["ddg", "ddg-lite", "bing", "searxng", "anysearch", "exa", "tavily", "keenable", "perplexity", "deepseek-official"];
|
|
22
22
|
|
|
23
23
|
// 当前插件版本(发布时与 package.json 同步)
|
|
24
|
-
const PLUGIN_VERSION = "0.4.
|
|
24
|
+
const PLUGIN_VERSION = "0.4.17";
|
|
25
25
|
// 检查更新的 npm registry 元数据地址(dsh-free-search 是 npmjs 上的公开包)
|
|
26
26
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org/dsh-free-search/latest";
|
|
27
27
|
const PLUGIN_NPM_URL = "https://www.npmjs.com/package/dsh-free-search";
|
|
@@ -241,6 +241,9 @@ async function fetchHtmlWithRetry(url, signal) {
|
|
|
241
241
|
async function searchDdgHtml(query, maxResults, options, signal) {
|
|
242
242
|
const params = new URLSearchParams({ q: query });
|
|
243
243
|
if (options?.region) params.set("kl", options.region);
|
|
244
|
+
// DDG 安全搜索:off(adlt=-1) / moderate(adlt=0) / strict(adlt=1)
|
|
245
|
+
const adlt = options?.safeSearch ?? "off";
|
|
246
|
+
params.set("adlt", adlt === "strict" ? "1" : adlt === "moderate" ? "0" : "-1");
|
|
244
247
|
// DDG 时间过滤:df=d/w/m/y(只支持固定档,自定义取近似档)
|
|
245
248
|
if (options?.timeRange) {
|
|
246
249
|
const df = { day: "d", week: "w", month: "m", year: "y" }[approximateTimeRange(options.timeRange.days ?? 7)];
|
|
@@ -268,6 +271,8 @@ async function searchDdgHtml(query, maxResults, options, signal) {
|
|
|
268
271
|
|
|
269
272
|
async function searchDdgLite(query, maxResults, options, signal) {
|
|
270
273
|
const params = new URLSearchParams({ q: query });
|
|
274
|
+
const adlt = options?.safeSearch ?? "off";
|
|
275
|
+
params.set("adlt", adlt === "strict" ? "1" : adlt === "moderate" ? "0" : "-1");
|
|
271
276
|
// DDG Lite 同样支持 df 时间过滤
|
|
272
277
|
if (options?.timeRange) {
|
|
273
278
|
const df = { day: "d", week: "w", month: "m", year: "y" }[approximateTimeRange(options.timeRange.days ?? 7)];
|
|
@@ -296,6 +301,10 @@ async function searchDdgLite(query, maxResults, options, signal) {
|
|
|
296
301
|
|
|
297
302
|
async function searchBing(query, maxResults, options, signal) {
|
|
298
303
|
const params = new URLSearchParams({ q: query, mkt: options?.bingMarket ?? "zh-CN" });
|
|
304
|
+
const adlt = options?.safeSearch ?? "off";
|
|
305
|
+
if (adlt === "off") params.set("adlt", "off");
|
|
306
|
+
else if (adlt === "moderate") params.set("adlt", "moderate");
|
|
307
|
+
else if (adlt === "strict") params.set("adlt", "strict");
|
|
299
308
|
const html = await fetchHtmlWithRetry(`${BING_URL}?${params}`, signal);
|
|
300
309
|
const blocks = html.match(/<li class="b_algo"[\s\S]*?<\/li>/g) ?? [];
|
|
301
310
|
const sources = [];
|
|
@@ -1417,6 +1426,7 @@ const Config = z.object({
|
|
|
1417
1426
|
lang: z.string().default("zh"),
|
|
1418
1427
|
region: z.string(),
|
|
1419
1428
|
bingMarket: z.string().default("zh-CN"),
|
|
1429
|
+
safeSearch: z.string().default("off"),
|
|
1420
1430
|
searxngInstances: z.array(z.string()),
|
|
1421
1431
|
platforms: z.array(z.string()).default(["github", "v2ex", "bilibili", "reddit", "hn", "stackoverflow", "wikipedia", "npm"]),
|
|
1422
1432
|
exaApiKey: z.string().role("secret"),
|
|
@@ -2011,6 +2021,7 @@ function apply(ctx, config) {
|
|
|
2011
2021
|
"",
|
|
2012
2022
|
"You have the web_search tool. Its backend engine is chosen in Settings > Plugins > Free Search.",
|
|
2013
2023
|
"Current engine: " + (current().provider ?? "bing"),
|
|
2024
|
+
"Safe search filter (Settings > Plugins > Free Search): " + (current().safeSearch ?? "off") + " (off|moderate|strict). Engine default off; applies to bing/ddg/ddg-lite.",
|
|
2014
2025
|
"",
|
|
2015
2026
|
"Available engines and their requirements:",
|
|
2016
2027
|
"- ddg (DuckDuckGo HTML) - FREE, no key (may be rate-limited)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-free-search",
|
|
3
|
-
"version":
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"description": "Free web search for DeepSeek Harness: 10 engines (Bing/DuckDuckGo/AnySearch/SearXNG/Exa/Tavily/Keenable keyless) + time filtering + platform search + web_fetch, with web settings UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|