dsh-free-search 0.4.17 → 0.4.19
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 +7 -1
- package/lib/client.js +28 -0
- package/lib/index.js +24 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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/lib/client.js
CHANGED
|
@@ -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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SettingsConflictError,
|
|
1
|
+
import { SettingsConflictError, SettingsProvider } from "@deepseek-ai/dsh-settings";
|
|
2
2
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
4
|
import fs from "node:fs";
|
|
@@ -15,7 +15,7 @@ const USER_AGENT =
|
|
|
15
15
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36";
|
|
16
16
|
const ACCEPT_LANG = "zh-CN,zh;q=0.9,en;q=0.8";
|
|
17
17
|
|
|
18
|
-
const FREE_SEARCH_NS =
|
|
18
|
+
const FREE_SEARCH_NS = "free-search";
|
|
19
19
|
const BRIDGE_PREFIX = "/api/dsh-free-search-settings";
|
|
20
20
|
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"];
|
|
@@ -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 = [];
|
|
@@ -1234,7 +1243,7 @@ function makeBridgeRoutes(settings, search, testEngine, getCredentials) {
|
|
|
1234
1243
|
}
|
|
1235
1244
|
const expectedRevision = typeof body.expectedRevision === "number" ? body.expectedRevision : undefined;
|
|
1236
1245
|
try {
|
|
1237
|
-
await settings.mutate(
|
|
1246
|
+
await settings.mutate(ns, body.ops, expectedRevision);
|
|
1238
1247
|
} catch (error) {
|
|
1239
1248
|
if (error instanceof SettingsConflictError) {
|
|
1240
1249
|
return { ok: false, code: "settings-conflict", message: error.message };
|
|
@@ -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"),
|
|
@@ -1632,14 +1642,16 @@ function apply(ctx, config) {
|
|
|
1632
1642
|
},
|
|
1633
1643
|
};
|
|
1634
1644
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1645
|
+
ctx.inject(["settings"], (sctx) => {
|
|
1646
|
+
sctx.settings.installSection(ctx, FREE_SEARCH_NS, Config, config ?? {}, {
|
|
1647
|
+
setSource: (source) => {
|
|
1648
|
+
current = source;
|
|
1649
|
+
},
|
|
1650
|
+
onChange: () => {
|
|
1651
|
+
// settings 变更时刷新系统提示词(显示最新引擎)
|
|
1652
|
+
if (typeof refreshPrompt === "function") refreshPrompt();
|
|
1653
|
+
},
|
|
1654
|
+
});
|
|
1643
1655
|
});
|
|
1644
1656
|
|
|
1645
1657
|
ctx.inject(["webServer", "settings"], (sctx) => {
|
|
@@ -2011,6 +2023,7 @@ function apply(ctx, config) {
|
|
|
2011
2023
|
"",
|
|
2012
2024
|
"You have the web_search tool. Its backend engine is chosen in Settings > Plugins > Free Search.",
|
|
2013
2025
|
"Current engine: " + (current().provider ?? "bing"),
|
|
2026
|
+
"Safe search filter (Settings > Plugins > Free Search): " + (current().safeSearch ?? "off") + " (off|moderate|strict). Engine default off; applies to bing/ddg/ddg-lite.",
|
|
2014
2027
|
"",
|
|
2015
2028
|
"Available engines and their requirements:",
|
|
2016
2029
|
"- 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.19",
|
|
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",
|