为 DeepSeek Harness 提供免费的网页搜索与抓取。让你的 agent 连上实时网络——每次调用只要 $0。
数据截至 2026/10/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
dsh plugin --profile web add viztor/dsh-tinyfish状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
安装兼容性检查需源码安装engines.node 要求 >=24,不满足 Node 22.19.0
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/16 14:10:19
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
查看扫描证据(共 4 处,展示前 4 条)
- 读取本地文件src/client.ts:545
text = readFileSync(home(path), "utf8"); - 读取本地文件src/client.ts:599
const config: unknown = JSON.parse(readFileSync(home(path), "utf8")); - 发起网络请求src/client.ts:769
response = await fetch(url, { - 发起网络请求src/provider.ts:410
async fetch(
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-client-store@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-credentials@deepseek-ai/dsh-launch-environment@deepseek-ai/dsh-web给这个插件打分还没有人投票↓
功能亮点
- 网页搜索与抓取
- 接入实时网络
- 支持直连或走Monid
为 DeepSeek Harness 的 web 能力接缝(ctx.web)提供基于 TinyFish 的搜索与抓取服务商——$0 的 SERP 与页面提取,可直连或走 Monid。
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
dsh-tinyfish
为 DeepSeek Harness 提供免费的网页搜索与抓取。让你的 agent 连上实时网络——每次调用只要 $0。
你的 agent 早就学会了推理,现在给它一点可供推理的“材料”:实时搜索结果与干净的页面正文,直连 harness 自带的 web_search 与 web_fetch 工具。由 TinyFish 驱动——两个端点都免费,你主机上的网络链路从此不再按次计费。
| 之前 | 之后 |
| 每次搜索调用都要计费 | $0,永久免费 |
| 抓回来的页面是 HTML,转换得磕磕绊绊 | 干净的 Markdown,来自浏览器级提取器,开箱即用 |
| 换提供方就要重装 | 改配置文件的两个单词即可,不用重装 |
| 每个提供方要么接受你的原样查询,要么什么都不接受 | 常驻默认值——域名、语言、时效、日期上下界、缓存 TTL、选择器 |
📊 方案对比
| 适用场景 | 解决方案 | 费用 |
| 在原生工具里免费搜索与抓取 | dsh-tinyfish,任一通道 | $0——直连通道用 TinyFish 密钥,Monid 通道用平台密钥;两种载荷完全一致 |
| 官方默认 | deepseek-official + http | 搜索按次计费;抓回的 HTML 还要再付一道 turndown 转换 |
| 需要提供方特有的 SERP 细节(地理、流量、排名)或批量查询 | 经 monid_run 调用的 Monid SERP 镜像 | 每次约 $0.002–$0.18,随提供方而定——只在任务真需要这些细节时用 |
| fetch 读不了的页面(重 JS、需登录、要交互) | CLI 里的 tinyfish agent / browser | 按量计费($0.016/step,$0.002/min)——只在抓取落空后果断升级 |
🚀 快速上手
方法一:从 Web 界面直接安装(推荐)
DeepSeek Harness 支持直接在 Web 界面安装插件,全程不用碰终端:
1. 打开 DSH Web,在侧边栏选择 Plugins(插件)。
2. 点击 Add plugin(添加插件)。
3. 输入包名 dsh-tinyfish(或 @viztor/dsh-tinyfish)——这是自由输入框,不是仓库搜索。
4. 点击 Install(安装)——DSH 会从 npm 拉取软件包,并加载它声明的 bundle 补丁。live profile(官方 Web profile 挂载了 HMR)会立即生效;否则 DSH 会提示下次启动时生效。
5. 在该 Plugins 页面打开 Tinyfish 卡片:选择通道(direct 或 monid),填入密钥——两个都填也行——然后点击 Save(保存)!
6. 安装到此结束——bundle 已把两条网络链路都指向 TinyFish。若想另行选择,参见选择提供方。
7. 问 agent 一个时效性问题;会返回实时来源。结果本身不会标明提供方——失败信息会,并会写出 tinyfish。
方法二:终端 / Profile 的 package.json
适用于无头环境、服务器或纳入版本管理的 dotfiles:
cd ~/.dsh/profiles/web
npm install dsh-tinyfish # or: npm install @viztor/dsh-tinyfish — same thing
认准一个名字装一次。两个 tarball 打包的是同一份构建产物、读取同一份设置(提供方注册为 tinyfish,配置挂在行 id dsh-tinyfish 下,凭证共用同一对引用)——所以之后改名不丢任何东西,但两个都挂会把 bundle 加载两遍。带 scope 的 tarball 其 manifest 与补丁的 name 不同,好让 loader 在 scope 下解析它。dsh-tinyfish 是 DSH 约定用的名字,本文档也用它。
📦 改从 GitHub Packages 安装
每次发版都会把 @viztor/dsh-tinyfish 镜像到 GitHub Packages——npmjs.org 连不上时的第二来源,也是仓库侧边栏的数据来源。只有带作用域的名字会镜像:GitHub 按所有者作用域把软件包关联到仓库。与 npmjs 不同,GitHub Packages 连公开包也要认证:未认证的请求会 404,且不告诉你这个包是否存在。用带有 read:packages 权限的 token:
project-local .npmrc is better than global for a token
@viztor:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=ghp_xxx
之后 npm install @viztor/dsh-tinyfish 就会从镜像解析。除非 npmjs 宕机,一律优先用它:不用 token,也不用额外配置。
挂载它——加进该 profile 的 package.json,然后重启 DSH:
{
"dependencies": { "dsh-tinyfish": "^0.11.0" },
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-tinyfish",
],
},
},
}
经 package.json 安装的 bundle 在启动时解析,所以这里要重启 DSH——只重载补丁加载不了它们。
配一把密钥——从下面的通道里选一个存好,然后问 agent 一个时效性问题(_who won the last Formula 1 race?_)。能返回实时来源,就证明该行已校验通过、凭证已解析;结果本身不会标明提供方,所以请到 profile 补丁里确认指向(searchProvider / fetchProvider)——或者故意触发一次失败,错误信息里会写出 tinyfish。
选择提供方
默认 bundle 会把两条网络链路都指向 TinyFish,所以全新安装不用改任何补丁就能跑。它通过设置网络 seam 的 searchProvider / fetchProvider 实现,按行 id 匹配。插件只有这一条路:dsh-web 在构造时一次性解析这两个字段,没有给提供方可“自荐”的 API。
若想另行选择,在 profile 的 cordis.patch.yml 里覆盖,它在每个 bundle 补丁之后生效,因此优先级最高:
- id: web
config:
searchProvider: deepseek-official
fetchProvider: http
搜索和抓取是两个独立字段,所以搜索走 TinyFish、抓取留在官方 http 提供方可以,反过来也行。插件保持挂载、静默待命。主机也可以改设 DSH_WEB_SEARCH_PROVIDER / DSH_WEB_FETCH_PROVIDER——它们喂给同一对字段,两边都设时以配置文件为准,因此在官方 profile 上始终是基础补丁胜出。
🔑 双通道,一个插件
| 特性 | 直连 _(默认)_ | 经 Monid |
| 背后是什么 | TinyFish 官方 API | 同样的 TinyFish 端点,经你的 Monid 钱包转发 |
| 需要什么 | tinyfish.ai 的免费密钥 | app.monid.ai 的平台密钥 |
| 最快配法 | tinyfish auth login | monid keys add |
| 花费 | $0 | $0 |
默认是 direct,因为包名就叫 TinyFish——全新安装索取的正是名字里那个凭证。更想用 Monid(你的 Monid MCP 挂载可能已经有一把平台密钥)?在 profile 补丁里钉死:
- id: dsh-tinyfish
config:
channel: monid
两把密钥可以并存——存一个永远不会覆盖另一个,切换通道也不丢任何东西。
⚙️ 插件卡片
Plugins → Tinyfish(插件 → Tinyfish)。GUI 里能改的东西都在这里:TinyFish 是否回答搜索与抓取、通道选择、密钥、搜索排序依据和尝试次数。改动先暂存、一起保存;你输入的密钥由 harness 保管,绝不写入 profile。
两个密钥框同时摆在页面上,各自标明所鉴权的服务;Monid 框的提示里写着它的引用,Direct 框的引用则印在它下方那行说明里——于是可以在 Direct 选中时配 Monid 密钥,不用来回切换也能看清两个密钥各是否存在。
这一行只管 TinyFish 的行为,不管选中它。bundle 已经把 searchProvider/fetchProvider 指向 tinyfish,所以全新安装无需改补丁就在 Web 路径上——想用别的,就在你自己的补丁层里覆盖这两个字段。参见选择提供方。
📖 完整配置参考
全都住在同一行 dsh-tinyfish 里。该行带校验,越界值会被带着报错信息拒绝,而不是悄悄钳制。
| 键 | 默认值 | 含义 |
| channel | direct | monid 或 direct |
| apiKey | _(未设置)_ | 两个通道通用的字面凭证;优先用引用 |
| apiKeyEnv | TINYFISH_API_KEY | direct 的凭证引用或环境变量 |
| monidKeyEnv | MONID_API_KEY | monid 的凭证引用或环境变量 |
| purpose | _(未设置)_ | 随每次搜索与抓取发送的目标说明;TinyFish 据此排序;最多 2000 个字符 |
| attempts | 3 | 瞬时失败或搜索落空时的总尝试次数(1–5);3 即最多重试 2 次 |
| filters.domainType | _(未设置)_ | web \| news \| research_paper——仅补丁文件 |
| filters.language / .location | _(未设置)_ | 地域定向——仅补丁文件 |
| filters.includeDomains / .excludeDomains | _(未设置)_ | 逗号分隔——仅补丁文件 |
| filters.recencyMinutes | _(未设置)_ | 按分钟计的时效窗口(1–5256000);上游与 .afterDate 互斥——仅补丁文件 |
| filters.afterDate | _(未设置)_ | 日期下界,YYYY-MM-DD;不适用于 research_paper——仅补丁文件 |
| filters.pubYearMin | _(未设置)_ | 发表年份下界(0–9999),仅 research_paper——仅补丁文件 |
| fetchOptions.ttl | _(未设置)_ | 缓存容忍秒数;0 强制实时抓取,不设则接受任何缓存——仅补丁文件 |
| fetchOptions.perUrlTimeoutMs | _(未设置)_ | 单 URL 耗时上限,毫秒(1–110000)——仅补丁文件 |
| fetchOptions.excludeSelectors | _(未设置)_ | 提取前剔除的 CSS 选择器,逗号分隔(1–20 个 × 每个 ≤1000 字符);直接下载 PDF/CSV 时会被拒绝——仅补丁文件 |
| monidBase / searchBase / fetchBase | 上游默认 | 端点覆盖,给测试环境用 |
| search / fetch | true | 是否提供该种类;false 不注销注册,只报告不可用 |
关掉一个报的是“不可用”而非“缺失”——harness 分得清这两者,只有后者意味着“安装坏了”。但“不可用”也不会悄悄 fallback:如果 searchProvider/fetchProvider 还写着 TinyFish,调用会失败。想用另一个提供方,就把对应工具指向它。
- id: dsh-tinyfish
config:
search: true
fetch: false # TinyFish stays registered but unavailable for fetch; point fetchProvider elsewhere to use another fetch
Manifest 元数据,而非配置
manifest 里还带着 dsh.compatibility:显式声明的 Node 与 DSH 范围,外加每个发版的实测结论——compatible、incompatible 或 unknown——供目录核验各 DSH 版本。
DSH 本体从不读它。harness 里没有任何代码读 dsh.compatibility,dshReleases 映射也全无引用,所以这些字段改变不了插件的加载、注册与行为。DSH 自己的兼容机制是另一回事:loader 检查的是 peerDependencies,豁免记录在 profile 的 compatibility.json 里。这些字段的存在,是让列表页能如实写清“哪些已被验证”,这里的结论也的确是实测而非期望:0.2.0-rc.2 是本仓库每次构建与测试所跑的版本,0.2.0-rc.1 虽在 peer 范围内但从未实际跑过,0.1.7-rc.2 则低于下限。0.2.0-rc.1 以同样方式验证过(把五个 harness devDependencies 全部钉到该版本并跑门禁);0.2.1-alpha.1 记为 incompatible,因为 peer 范围并不接受它 —— semver 只在范围本身带有同一 major.minor.patch 的预发布版本时才放行预发布版,因此 loader 在该 alpha 主机上会拒绝加载本插件。
过滤器与抓取选项写在补丁文件里
filters 与 fetchOptions 是嵌套对象,而设置表单一个字段只对应一个扁平键——所以搜索与抓取调优留在运维层面:
- id: dsh-tinyfish
config:
channel: monid
filters:
domainType: research_paper
language: zh
includeDomains: arxiv.org,openreview.net
pubYearMin: 2023
fetchOptions:
ttl: 0 # force a live fetch instead of accepting a cached page
excludeSelectors: nav, .cookie-banner
两个分组永远有解析结果:没设就是空分组,不给请求加任何东西;不可用的成员退化为未设置,绝不发往上游。三条上游注意事项按文档行为原样透出,不做强制:
- recencyMinutes 与 afterDate 在 TinyFish API 里互斥;一行里两个都写就两个都发。
- excludeSelectors 对直接下载的 PDF/CSV 无效,带上它会答 selector_unsupported。
- 上游会按 domainType 交叉核验每个日期边界:recencyMinutes 与 afterDate 不接受 research_paper,pubYearMin 只属于它,配错任一一对都会让整个搜索被拒绝。
凭证从哪里来
每次调用现解析——轮换密钥下次搜索即生效,不用重启。先命中先生效:
1. 行里的 apiKey 字面量(写在配置里的秘密;优先用 2–3)
2. 凭证服务——apiKeyEnv(直连)或 monidKeyEnv(Monid),从设置界面保存
3. 启动环境(DSH 启动前 export 的)
4. 实时环境——先读配置的引用名(apiKeyEnv / monidKeyEnv,可以是你自己的变量名),再读 MONID_API_KEY / MONID_MCP_TOKEN / TINYFISH_API_KEY(任一 Monid 变量都覆盖 monid 通道)
5. 各通道的 CLI 存储(monid keys add / tinyfish auth login)
凭证服务挂了就落到下一个来源,而不是让搜索失败。
端点从哪里来
行配置优先,其次环境变量,最后内置默认——测试环境不用改补丁就能换向:
| 设置项 | 环境变量 |
| monidBase | TINYFISH_MONID_BASE_URL |
| searchBase | TINYFISH_SEARCH_BASE_URL |
| fetchBase | TINYFISH_FETCH_BASE_URL |
🔍 值得了解的行为
- 404 是结果,不是错误。单个 URL 抓取失败会带着状态码回来,因为那是模型需要的资源状态。
- publishedAt 诚实。TinyFish 报的是人类日期("Apr 30, 2026"、"1 year ago")。能解析的转成 ISO-8601,解析不了的直接丢掉,绝不编造。只有不含时刻、也不带时区的日期按 UTC 读,所以同一页面在全球报的是同一天;带时刻的值按原样解析。
- 空搜索会重试。上游大约三跑空一——对合法查询也可能什么都不回;在认定之前,会一直重试到用尽尝试预算(attempts,总尝试次数)。
- 被拦的 run 是终态。若 Monid 工作区控制拦停一次运行,错误里会写原因并附充值链接。永不重试。
- 关闭意味着“不可用”,不是“没了”。关掉的种类保持注册、只是谢绝。若 profile 还把对应工具钉在 Tinyfish 上,调用会大声失败,而不会悄悄改道——想用别的提供方就把工具指向它。什么都没钉时,退出的 Tinyfish 只管让路:自动选择用剩下的人选,关掉其中一种正是把“多提供方歧义”收敛到唯一候选的办法。
- “不可用”三种成因、一种面孔。seam 只看到一个布尔值,所以“开关关了”“没凭证”“base URL 写错”在下游读起来一模一样。卡片分得清——去看卡片上的开关和密钥徽标,端点覆盖则在行里或环境里看。
- purpose 是每次请求的一句话。seam 的请求没有目标槽位——搜索只有 {query},抓取只有 {url}——不动 harness 就不可能有按调用的目标。配置里的那句话会逐字跟随每次搜索与抓取:常驻偏置,而非按任务指令。
TinyFish 的 agent 与 browser 界面没有暴露:按量计费、走钱包,既不是搜索也不是抓取。页面真需要开浏览器时,直接用 tinyfish CLI。
🛠 开发
工具链是 Vite+:vp pack 用 tsdown 构建,vp test 跑 Vitest,vp lint / vp fmt 即 Oxlint 与 Oxfmt,且是类型感知的。Lint 与格式配置住在 vite.config.ts 里——Vite+ 会忽略独立配置文件。
pnpm install
pnpm test # hermetic — no network, no credential
pnpm run check # format + lint + types
pnpm run release:gate # build, then the full gate incl. the package checks
pnpm run test:live # the real APIs, still $0, needs credentials
需要 DSH ^0.2.0-rc.1(0.2.0-rc.1 及之后、0.3.0 之前)与 Node 24+。完整流程与不变量:AGENTS.md。参与贡献:CONTRIBUTING.md。
许可证
MIT预览分享卡片长什么样 ↓
为 DeepSeek Harness 提供免费的网页搜索与抓取。让你的 agent 连上实时网络——每次调用只要 $0。