{"_id":"@elves-ai/dsh-web-search-firecrawl","name":"@elves-ai/dsh-web-search-firecrawl","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@elves-ai/dsh-web-search-firecrawl","version":"1.0.0","description":"Firecrawl-backed search provider for the DeepSeek Harness web capability seam (ctx.web)","keywords":["dsh-plugin","firecrawl"],"homepage":"https://github.com/elves-ai/dsh-web-search-firecrawl#readme","bugs":{"url":"https://github.com/elves-ai/dsh-web-search-firecrawl/issues"},"repository":{"type":"git","url":"git+https://github.com/elves-ai/dsh-web-search-firecrawl.git"},"license":"MIT","author":{"name":"yangzhe1003"},"type":"module","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","default":"./lib/invariant.js"},"./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./cordis.patch.yml":"./cordis.patch.yml","./package.json":"./package.json"},"main":"lib/index.js","types":"lib/types/index.d.ts","directories":{"doc":"docs","test":"tests"},"scripts":{"build":"rm -rf lib && npm run build:types && tsdown","build:types":"tsc -p tsconfig.build.json","typecheck":"tsc -p tsconfig.json","bundle":"tsdown","watch":"tsdown --watch","test":"vitest run","test:e2e":"vitest run --config vitest.e2e.config.ts","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"devDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-schema-form":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-web-react":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-web":"^0.1.0-rc.6","@types/node":"^22.0.0","@types/react":"~18.3.1","@types/react-dom":"~18.3.1","lightningcss":"^1.32.0","react":"^18.2.0","react-dom":"18.2.0","tsdown":"^0.22.2","typescript":"^5.6.0","vitest":"^4.1.0","@deepseek-ai/dsh-credentials":"^0.1.0-rc.6","@deepseek-ai/dsh-web-search-deepseek":"^0.1.0-rc.6"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-schema-form":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-primitives":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-client-web-react":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-launch-environment":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-web":"^0.1.0-rc.6","react":"^18.2.0","react-dom":"^18.2.0","@deepseek-ai/dsh-credentials":"^0.1.0-rc.6","@deepseek-ai/dsh-web-search-deepseek":"^0.1.0-rc.6"},"engines":{"node":">=22"},"publishConfig":{"access":"public"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"}},"packageManager":"pnpm@11.7.0+sha512.19cc852c120c7125760f2443ee6be0ca5b40f9f50598de1a09a1f177503e010e57c23c77646e01e761de59bf874fb22a3398c33ab9691fc13eb946b6f0f4d620","gitHead":"96e7fbab40c13b748b57028761ec55de70cdd113","_id":"@elves-ai/dsh-web-search-firecrawl@1.0.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-4DHxdHWxXjzZ0ExErTjpOTT6m17GQSm0FIzD0GJBATTG+YOf0FBA8FVmkCONaH4RyKkymUEAmcd4SPvY7Gv1EQ==","shasum":"9e75880eddc2c63725217a82f6813f33cbcab047","tarball":"https://registry.npmjs.org/@elves-ai/dsh-web-search-firecrawl/-/dsh-web-search-firecrawl-1.0.0.tgz","fileCount":31,"unpackedSize":177009,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFJZuiTyictKijASa+1tdQ78DNAvJpGNNd225HGX7lX/AiEAufIqsShkcwWQTqTxMOlNJoA14NJCJx0oWAeWLcIN4lQ="}]},"_npmUser":{"name":"yangzhe1003","email":"yangzheplus@163.com"},"maintainers":[{"name":"yangzhe1003","email":"yangzheplus@163.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-web-search-firecrawl_1.0.0_1786934639893_0.5298849693295635"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-17T02:43:59.660Z","1.0.0":"2026-08-17T02:44:00.028Z","modified":"2026-08-17T02:44:00.284Z"},"maintainers":[{"name":"yangzhe1003","email":"yangzheplus@163.com"}],"description":"Firecrawl-backed search provider for the DeepSeek Harness web capability seam (ctx.web)","homepage":"https://github.com/elves-ai/dsh-web-search-firecrawl#readme","keywords":["dsh-plugin","firecrawl"],"repository":{"type":"git","url":"git+https://github.com/elves-ai/dsh-web-search-firecrawl.git"},"author":{"name":"yangzhe1003"},"bugs":{"url":"https://github.com/elves-ai/dsh-web-search-firecrawl/issues"},"license":"MIT","readme":"# dsh-web-search-firecrawl\n\n[English](README.md) | 中文\n\n![DSH 设置中的 Firecrawl 插件页面](docs/firecrawl-settings.png)\n\n由 [Firecrawl](https://firecrawl.dev) 支持的搜索提供方，用于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的 web 能力 seam（`ctx.web`）：让内置的 `web_search` 工具走 Firecrawl 搜索 API，而不是随产品附带的 DeepSeek 路由。\n\n本插件是一个 Cordis 函数插件，向 seam 注册 `WebSearchProvider`（`id: firecrawl`）——它不拥有 `ctx.web`，也不注册面向模型的工具（工具 schema 仍属于 `@deepseek-ai/dsh-tool-web`）。它调用 Firecrawl 的 `POST /v1/search` 端点，把扁平 `data[]` 映射为 seam 规范化的 `WebSearchResult`。\n\nAPI 密钥在 **DSH 设置页的「Firecrawl」页面**中配置；同时保留 `$FIRECRAWL_API_KEY` 作为兼容性回退。\n\n## 环境要求\n\n- DeepSeek Harness 安装在官方包 `0.1.0-rc.6` 线上（本插件 peer 依赖 `@deepseek-ai/dsh-web ^0.1.0-rc.6` 等——当前发布线）。更旧线上 pnpm 会在 profile 中嵌套安装第二份 seam 包，错误分类会降级（跨副本 `WebError`）；可在 profile 目录执行 `npm ls @deepseek-ai/dsh-web` 检查。\n- Firecrawl API 密钥。可在 [firecrawl.dev](https://firecrawl.dev) 获取。\n\n## 安装\n\n请从以下两种方式中**二选一**：\n\n**方式一：全局安装的 `dsh` CLI**\n\n```sh\ndsh plugin --profile web add @elves-ai/dsh-web-search-firecrawl@latest\n```\n\n**方式二：`deepseek-harness` 源码检出**（那里的 `dsh` 是 pnpm workspace 脚本，而非全局命令；请在 workspace 根目录下运行）\n\n```sh\npnpm dsh plugin --profile web add @elves-ai/dsh-web-search-firecrawl@latest\n```\n\n`dsh plugin` 会在 profile 目录内转发给 pnpm，并自动把 bundle 追加到 profile 的层叠列表。该 bundle patch 插入 `web-search-firecrawl` 插件行，并把 `web` seam 行切换为 `searchProvider: firecrawl`（替换 base bundle 的 `deepseek-official`）。\n\n安装后**重启 `dsh web`**，然后硬刷新页面（Cmd/Ctrl+Shift+R）加载浏览器半的设置页：\n\n```sh\ndsh web\n```\n\n验证：打开 DSH 设置页应能看到 **Firecrawl** 页面；在会话中让 agent 执行一次网页搜索，请求会走 Firecrawl。密钥缺失时搜索以 `WEB_PROVIDER_CONFIGURED_UNAVAILABLE` 失败。\n\n想保留 DeepSeek 搜索为默认、仅按实例启用 Firecrawl：在你自己的 profile `cordis.patch.yml` 中改写 `web` 行即可——每行最后一次写入生效。base bundle 固定了 `searchProvider: deepseek-official`，因此 `$DSH_WEB_SEARCH_PROVIDER` 只在 `web` 行未设置 `searchProvider` 时生效：用环境变量选择时把 `web` 行改写为空 config（`- id: web` + `config: {}`），要固定 DeepSeek 则写 `searchProvider: deepseek-official`。\n\n## 更新插件\n\n全局安装的 `dsh` CLI：\n\n```sh\ndsh plugin --profile web update --latest @elves-ai/dsh-web-search-firecrawl\n```\n\n`deepseek-harness` 源码检出（请在 workspace 根目录下运行）：\n\n```sh\npnpm dsh plugin --profile web update --latest @elves-ai/dsh-web-search-firecrawl\n```\n\n`dsh plugin update` 会在 profile 目录内转发给 pnpm；`--latest` 表示升级到 npm 上的最新版本，不受当前已安装版本范围限制。更新后请**重启 `dsh web`**，并硬刷新页面（Cmd/Ctrl+Shift+R）。如果插件安装在其他 profile 中，请把 `web` 替换为对应的 profile 名。\n\n## 卸载\n\n全局安装的 `dsh` CLI：\n\n```sh\ndsh plugin --profile web remove @elves-ai/dsh-web-search-firecrawl\n```\n\n`deepseek-harness` 源码检出（请在 workspace 根目录下运行）：\n\n```sh\npnpm dsh plugin --profile web remove @elves-ai/dsh-web-search-firecrawl\n```\n\n`dsh plugin remove` 会在 profile 目录内转发给 pnpm，并自动把包从 `dsh.profile.bundles` 移除。bundle 层移除后，`web` seam 行会恢复为 base bundle 的 `deepseek-official`。卸载后需**重启 `dsh web`**，运行中的进程才会停止加载该插件。\n\n该命令只移除包与挂载，不会删除设置页已保存的值；若希望一并删除已保存的 API Key，请先在 **Firecrawl** 页面点击「清除」。如果插件安装在其他 profile 中，请把 `web` 替换为对应的 profile 名。\n\n## 切换搜索提供方\n\n安装本插件后，`web` seam 的 `searchProvider` 会切到 `firecrawl`，因此默认会替换原版网页搜索。打开 **DSH 设置 → Firecrawl** 后，页面顶部的「**使用 Firecrawl**」开关可以在两者之间实时切换：\n\n- **开（默认）**：使用 Firecrawl `POST /v1/search`。\n- **关**：`firecrawl` provider 会把请求委托给原版 DeepSeek 搜索实现，模型继续走随产品附带的 DeepSeek 搜索路由；DeepSeek 的端点、模型等仍从原版的 `web-search-deepseek` 设置段解析。\n\n切换立即生效，无需重启；Firecrawl 的 API Key 和请求参数在关闭时保留，下次打开开关继续使用。\n\n## 配置 API 密钥\n\n打开 **DSH 设置 → Firecrawl**：\n\n1. 在 **API Key** 输入框粘贴 `fc-...`，点击保存。输入框旁的眼睛按钮可在当前输入过程中切换明文显示/隐藏。\n2. 密钥以 `role('secret')` 写入设置文档：任何接口响应都不会回显已保存的字面值，页面只显示「已配置 / 未配置」。\n3. **留空保存表示保持当前密钥**；点击「清除」可删除已保存的密钥。\n4. 保存后**立即生效**：已注册的 provider 会在下一次搜索使用新配置，无需重启。\n\n如果没有在设置页填写密钥，插件仍按兼容方式回退到 `$FIRECRAWL_API_KEY`：\n\n```sh\nexport FIRECRAWL_API_KEY=fc-...\ndsh --profile web\n```\n\n## 配置\n\n| 配置键 | 默认值 | 含义 |\n|---|---|---|\n| `useFirecrawl` | `true` | 是否使用 Firecrawl；`false` 时切换到原版 DeepSeek 网页搜索。 |\n| `apiKey` | （空）→ `$FIRECRAWL_API_KEY` | Firecrawl API 密钥。设置页写入的 secret 不回显；为空时回退环境变量。两者都缺失时提供方不可用。 |\n| `baseURL` | `https://api.firecrawl.dev` | 端点基址；追加 `/v1/search`。无法解析时提供方不可用。 |\n| `limit` | （未设置） | 请求不含 `maxResults` 时使用的默认结果数。留空时不发送默认值。必须是正整数。 |\n| `maxSnippetChars` | `600` | 将单个 `description` 映射为 `snippet` 时保留的字符上限。必须是正整数。 |\n\n所有字段都可以在设置页修改。插件 bundle 配置仍然是「base 层」：设置页用户层叠加其上，因此 profile 中可以只写需要固定的字段：\n\n```yaml\n# profile cordis.patch.yml（可选，通常无需配置）\n- id: web-search-firecrawl\n  name: '@elves-ai/dsh-web-search-firecrawl'\n  config:\n    limit: 10\n```\n\n## 映射\n\nFirecrawl 返回扁平 `data[]`，不返回生成答案，因此省略 `content`。每项条目映射为 `WebSearchSource`：`url` ← `url`、`title` ← `title`、`snippet` ← 修剪并按 `maxSnippetChars` 限长的 `description`（没有非空 description 的条目仍保留可引用的仅 URL 来源）。请求的 `maxResults` 优先于已配置的默认 `limit`，并作为 Firecrawl `limit` 发送，以优化成本和延迟；最终上限由 seam 强制执行。提供方失败（HTTP 错误、网络失败、响应体无法解析或结构不符、`success: false` 信封）以 `WebError` `WEB_PROVIDER_ERROR` 呈现；中止请求以 `WEB_ABORTED` 呈现。HTTP 重定向会在访问 `Location` 指向的目标之前被拒绝。\n\n## 模型体验\n\n通过 `@deepseek-ai/dsh-tool-web` 间接影响：模型看到经 `maxResults` 限制的 URL、标题与限长后的描述，以及消费方错误包装层内的 `Firecrawl search aborted`、`Firecrawl search request failed: <error>` 和 `Firecrawl returned an unprocessable response body: <error>`。生成答案与提供方私有字段不进入上下文。\n\n## 已知限制\n\n- **snippet 是 Firecrawl 的 `description` 摘录，而非搜索引擎摘要**：可能是较长的页面 markdown，因此提供方将其限制为 `maxSnippetChars`；没有 description 的条目完全不带 snippet（仅 URL）。\n- **只公开 `limit`／`maxSnippetChars`**：Firecrawl 的其他控制项（语言、国家／地区、时效、位置、定向、抓取选项）等待 `@deepseek-ai/dsh-web` 中提供方无关的 Service Definition 字段。\n- **按错误形状分类中止**：只有 `DOMException` 且名为 `AbortError` 时才映射为 `WEB_ABORTED`；携带自定义原因的中止会呈现为 `WEB_PROVIDER_ERROR`。\n\n## 开发\n\n```sh\npnpm install\npnpm run typecheck\npnpm test                            # 单元测试（不联网）\nFIRECRAWL_API_KEY=... pnpm run test:e2e  # 真实 API 冒烟，无密钥时自动跳过\npnpm run build                       # 产出 lib/（ESM host）+ lib/client.js（浏览器半）+ lib/types/（d.ts）\n```\n\n## 许可证\n\nMIT\n","readmeFilename":"README.zh.md","_rev":"1-9daac80eba7d69055a8569e1132ae661"}