🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

聚合网页搜索 Luca4Don3/dsh-web-search

DeepSeek Harness
Luca4Don311 天前有提交已提供中文文档扫描:含敏感能力

按匿名优先顺序聚合多个搜索源并抓取网页正文

30.8/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/20 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Luca4Don3/dsh-web-search
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · ui
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 68 处,展示前 50 条)
  • 写入本地文件
    lib/hosts/pi.js:107
    mkdirSync(path, { recursive: true, mode: 0o700 });
  • 发起网络请求
    lib/hosts/dsh.js:98
    async fetch(request, signal) {
  • 执行外部命令来自配置/变量
    lib/auth/credentials.js:93
    const child = spawn(bin, args, { stdio: ["pipe", "pipe", "pipe"], detached: process.platform !== "win32" });
  • 执行外部命令固定命令
    lib/auth/credentials.js:122
    const killer = spawn("taskkill", ["/T", "/F", "/PID", String(child.pid)], { stdio: "ignore" });
  • 删除本地文件
    lib/quota/state-store.js:155
    await unlink(path);
  • 读取本地文件
    lib/quota/state-store.js:175
    stat = await lstat(current);
  • 写入本地文件
    lib/quota/state-store.js:183
    await mkdir(current, { mode: 0o700 });
  • 读取本地文件
    lib/quota/state-store.js:189
    stat = await lstat(current);
  • 读取本地文件
    lib/quota/state-store.js:267
    const stat = await lstat(temporary);
  • 写入本地文件
    lib/quota/state-store.js:270
    await rename(temporary, path);
  • 读取本地文件
    lib/quota/state-store.js:303
    const current = await lstat(path);
  • 读取本地文件
    lib/quota/state-store.js:315
    const latest = await lstat(path);
  • 删除本地文件
    lib/quota/state-store.js:318
    await unlink(path);
  • 读取本地文件
    lib/quota/state-store.js:337
    const names = await readdir(directory);
  • 写入本地文件
    lib/quota/state-store.js:450
    await rename(legacyPath, join(base, "state.json.migrated"));
  • 读取本地文件
    lib/search/spill-store.js:42
    const latest = await lstat(file);
  • 删除本地文件
    lib/search/spill-store.js:45
    await unlink(file);
  • 读取本地文件
    lib/search/spill-store.js:63
    const stat = await lstat(path);
  • 删除本地文件
    lib/search/spill-store.js:66
    await unlink(path);
  • 读取本地文件
    lib/search/spill-store.js:76
    const root = await realpath(temporaryRoot);
  • 读取本地文件
    lib/search/spill-store.js:81
    const stat = await lstat(current);
  • 读取本地文件
    lib/search/spill-store.js:88
    const latest = await lstat(anchor.path);
  • 写入本地文件
    lib/search/spill-store.js:96
    await mkdir(directory, { mode: 0o700 });
  • 读取本地文件
    lib/search/spill-store.js:102
    const leaf = await lstat(directory);
  • 删除本地文件
    lib/search/spill-store.js:163
    await unlink(path);
  • 执行外部命令来自配置/变量
    src/auth/credentials.ts:154
    const child = spawn(bin, args, { stdio: ["pipe", "pipe", "pipe"], detached: process.platform !== "win32" });
  • 执行外部命令固定命令
    src/auth/credentials.ts:178
    const killer = spawn("taskkill", ["/T", "/F", "/PID", String(child.pid)], { stdio: "ignore" });
  • 发起网络请求
    src/hosts/dsh.ts:132
    async fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult> {
  • 写入本地文件
    src/hosts/pi.ts:121
    mkdirSync(path, { recursive: true, mode: 0o700 });
  • 删除本地文件
    src/quota/state-store.ts:181
    await unlink(path);
  • 读取本地文件
    src/quota/state-store.ts:199
    stat = await lstat(current);
  • 写入本地文件
    src/quota/state-store.ts:205
    await mkdir(current, { mode: 0o700 });
  • 读取本地文件
    src/quota/state-store.ts:209
    stat = await lstat(current);
  • 读取本地文件
    src/quota/state-store.ts:271
    const stat = await lstat(temporary);
  • 写入本地文件
    src/quota/state-store.ts:273
    await rename(temporary, path);
  • 读取本地文件
    src/quota/state-store.ts:308
    const current = await lstat(path);
  • 读取本地文件
    src/quota/state-store.ts:317
    const latest = await lstat(path);
  • 删除本地文件
    src/quota/state-store.ts:319
    await unlink(path);
  • 读取本地文件
    src/quota/state-store.ts:342
    const names = await readdir(directory);
  • 写入本地文件
    src/quota/state-store.ts:434
    await rename(legacyPath, join(base, "state.json.migrated"));
  • 读取本地文件
    src/search/spill-store.ts:40
    const latest = await lstat(file);
  • 删除本地文件
    src/search/spill-store.ts:42
    await unlink(file);
  • 读取本地文件
    src/search/spill-store.ts:58
    const stat = await lstat(path);
  • 删除本地文件
    src/search/spill-store.ts:60
    await unlink(path);
  • 读取本地文件
    src/search/spill-store.ts:69
    const root = await realpath(temporaryRoot);
  • 读取本地文件
    src/search/spill-store.ts:74
    const stat = await lstat(current);
  • 读取本地文件
    src/search/spill-store.ts:81
    const latest = await lstat(anchor.path);
  • 写入本地文件
    src/search/spill-store.ts:87
    try { await mkdir(directory, { mode: 0o700 }); }
  • 读取本地文件
    src/search/spill-store.ts:89
    const leaf = await lstat(directory);
  • 删除本地文件
    src/search/spill-store.ts:139
    await unlink(path);
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-web
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DSH 独立聚合搜索提供方:匿名优先、费用授权、额度预检与有界取消

README

由 DeepSeek 最新模型翻译生成
dsh-web-search 同时兼容 DSH 与 Pi 的网页搜索 / 网页抓取插件,一个仓库、一份内核、两套宿主适配。 | 能力 | DSH | Pi | | 搜索 | 向 ctx.web 注册提供方 free-first | 注册模型工具 web_search | | 抓取 | 向 ctx.web 注册提供方 local-first | 注册模型工具 web_fetch | | 密钥菜单 | —(配置来自 Cordis 配置) | /web-search-auth 命令 | 匿名顺序为 Exa → Tavily → Parallel → Firecrawl → TinyFish(keyless);计费顺序为 Exa → Tavily → Parallel → Firecrawl → SerpApi。默认禁止计费,只有显式授权且在匿名通道全部失败后才会尝试密钥通道。TinyFish 只有零凭据匿名通道。
展开完整文档(README 剩余 1 节)
dsh-web-search

同时兼容 DSH 与 Pi 的网页搜索 / 网页抓取插件,一个仓库、一份内核、两套宿主适配。

| 能力 | DSH | Pi |
| 搜索 | 向 ctx.web 注册提供方 free-first | 注册模型工具 web_search |
| 抓取 | 向 ctx.web 注册提供方 local-first | 注册模型工具 web_fetch |
| 密钥菜单 | —(配置来自 Cordis 配置) | /web-search-auth 命令 |

匿名顺序为 Exa → Tavily → Parallel → Firecrawl → TinyFish(keyless);计费顺序为 Exa → Tavily → Parallel → Firecrawl → SerpApi。默认禁止计费,只有显式授权且在匿名通道全部失败后才会尝试密钥通道。TinyFish 只有零凭据匿名通道。

结构

src/
├── search/ # 搜索内核:路由、协议、MCP 解析、脱敏、正文落盘 ├── quota/ # 额度预检、冷却状态、私有持久化 ├── auth/ # 凭据解析与系统密钥库(macOS Keychain / Linux secret-tool) ├── fetch/ # 抓取内核:SSRF 校验、有界响应、HTML→Markdown ├── shared/ # 两个内核共用的有界响应体读取 └── hosts/ ├── dsh.ts # Cordis 插件:注册搜索与抓取提供方 ├── pi.ts # Pi:web_search 工具 + /web-search-auth └── pi-fetch.ts # Pi:web_fetch 工具

内核不 import 任何宿主 API,因此两个宿主共用同一套路由、额度、冷却与脱敏行为,也可以离线测试。

安装

DSH

dsh plugin --profile <profile> add link:<server_path>/dsh-web-search

cordis.patch.yml 会同时启用 free-first(搜索)与 local-first(抓取)。抓取提供方使用独立 id,因为官方插件已占用 http;若你已有自定义 fetchProvider,请保留你的选择。

Pi

pi install <server_path>/dsh-web-search

或在 settings.json 中按目录启用:

{
"packages": [
{ "source": "git:github.com/Luca4Don3/dsh-web-search", "extensions": ["pi.ts"] }
]
}

从旧版 pi-extensions/extensions/web-search 迁移

旧版扩展同样注册 web_search,两者同时启用会工具重名冲突。迁移步骤:

1. 确认旧版位置(Pi 会自动加载该目录下的扩展)
ls ~/.pi/agent/extensions/web-search

2. 移走旧版(保留备份便于回滚;不要直接删除)必须移出 ~/.pi/agent/extensions/ —— Pi 会加载该目录下的每个子目录,改名成 web-search.bak 仍在自动发现范围内,重名冲突不会解除。
mv ~/.pi/agent/extensions/web-search ~/.pi/agent/web-search.bak

3. 安装新版 pi install <server_path>/dsh-web-search

4. 重启 Pi,确认只注册一份 web_search

若此前用 pi install 装过旧版,改用 pi remove ~/pi-extensions/extensions/web-search 卸载。
/web-search-auth 命令名保持不变,密钥配置与冷却状态无需迁移。

回滚:mv ~/.pi/agent/web-search.bak ~/.pi/agent/extensions/web-search。

备份目录只要仍在 ~/.pi/agent/extensions/ 之下就会被继续加载;放到该目录的同级位置(如 ~/.pi/agent/)才能真正停用。

如果此前已按旧版说明放在 ~/.pi/agent/extensions/web-search.bak,请补一步:mv ~/.pi/agent/extensions/web-search.bak ~/.pi/agent/web-search.bak。

安装后可用 /web-search-auth 管理密钥。环境变量优先于系统密钥库。

配置

DSH(Cordis 配置)

| 字段 | 默认值 | 含义 |
| allowBillable | false | 显式授权可能计费的通道 |
| timeoutMs | 25000 | 单请求预算,毫秒 |
| totalTimeoutMs | 90000 | 整次调用总预算,毫秒 |
| maxResponseBytes | 5242880 | 解压后响应体上限 |
| maxChars | 24000 | 模型可见正文字符预算 |
| retries | 1 | 网络或服务端错误重试次数 |
| cooldownOverrideMs | 未设置 | 冷却覆盖,0 禁用冷却 |
| stateDirectory | $DSH_HOME/config/web-search | 冷却状态目录 |
| fetchTimeoutMs | 20000 | 抓取总预算,毫秒 |
| fetchMaxResponseBytes | 5242880 | 抓取解压后响应体上限 |
| fetchMaxChars | 24000 | 抓取正文上限 |
| fetchMaxRedirects | 3 | 最大跳转次数 |

Pi(环境变量)

| 字段 | 默认值 | 含义 |
| PI_WEB_SEARCH_ALLOW_BILLABLE | false | 同 allowBillable,只接受 true / false |
| PI_WEB_SEARCH_ALLOW_PAID | 未设置 | 旧开关别名,与上一项冲突时报错 |
| PI_WEB_SEARCH_TIMEOUT_MS | 25000 | 单请求预算 |
| PI_WEB_SEARCH_TOTAL_TIMEOUT_MS | 90000 | 整次调用预算 |
| PI_WEB_SEARCH_MAX_RESPONSE_BYTES | 5242880 | 响应体上限 |
| PI_WEB_SEARCH_RETRIES | 1 | 重试次数,0..3 |
| PI_WEB_SEARCH_FREE_COOLDOWN_MS | 未设置 | 固定冷却覆盖 |
| PI_WEB_FETCH_TIMEOUT_MS | 20000 | 抓取总预算 |
| PI_WEB_FETCH_MAX_RESPONSE_BYTES | 5242880 | 抓取响应体上限 |
| PI_WEB_FETCH_MAX_CHARS | 24000 | 抓取正文上限 |
| PI_WEB_FETCH_MAX_REDIRECTS | 3 | 最大跳转次数 |
| PI_WEB_FETCH_REMOTE_FALLBACK | off | 远程回退;当前只接受 off,设为 jina 会在任何请求前明确报错 |

密钥:EXA_API_KEY、TAVILY_API_KEY、PARALLEL_API_KEY、FIRECRAWL_API_KEY、SERPAPI_API_KEY。TinyFish 始终 keyless,不读取 TINYFISH_API_KEY。

安全与限制

- 未授权计费时不读取任何服务商密钥、凭据库或额度接口。
- 授权后一次性解析全部所需密钥,并在正文、来源、details 与落盘内容中统一脱敏。
- 只能通过环境变量或系统密钥库注入密钥;不提供明文文件回退。
- 响应体按解压后字节计数上限,超限立即中止请求,不返回部分结果。
- 冷却状态只保存 HMAC 作用域与冷却元数据,目录 0700、文件 0600。
- 额度预检只回答「当前是否付得起」,不能证明账户免费,也不提供跨进程账单硬上限。
- 抓取拒绝私有段、回环、链路本地与云元数据地址,不执行网页脚本,不携带 Cookie。
- 两个宿主的单元测试全部离线;这不等于真实桌面安装、真实密钥库写入或服务商网络验收。

开发验证

npm ci --ignore-scripts --no-audit --no-fund
npm run check # 严格类型检查
npm test # DSH 兼容层 + Pi + 抓取全部离线用例
npm run check:artifacts
npm pack --dry-run

Pi 用例通过 jiti 直接加载 src/ 下的源码,因此需要本机装有 Pi(提供 jiti 与 typebox)。CI 会先安装 @earendil-works/pi-coding-agent 再运行。

npm run smoke 是手动匿名网络检查,不进入持续集成,不使用密钥或计费通道。

License

MIT
预览分享卡片长什么样 ↓Luca4Don3/dsh-web-search
分享 · DeepSeek Harness Hub
Luca4Don3/dsh-web-search

按匿名优先顺序聚合多个搜索源并抓取网页正文

回到顶部