DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
W

dsh-web-search-custom

Web Search Custom

DeepSeek Harness Web 配置文件(ctx.web)的搜索提供商:默认使用 AnySearch 统一 API(匿名免费层无需 API 密钥即可使用;添加密钥可使用付费配额),同时支持任何兼容 SearXNG 的 JSON 端点。包含设置内配置卡片。零侵入式捆绑插

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:runfali/dsh-web-search-custom#0d7d8c4b31ed047c7b0b5c6ebc4a5eca7fdc57b6
README兼容性版本

说明

DeepSeek Harness Web 配置文件(ctx.web)的搜索提供商:默认使用 AnySearch 统一 API(匿名免费层无需 API 密钥即可使用;添加密钥可使用付费配额),同时支持任何兼容 SearXNG 的 JSON 端点。包含设置内配置卡片。零侵入式捆绑插件。

兼容性与来源证明

Web Search Custom 以 dsh-web-search-custom 发布,当前版本为 0.3.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/9/26

版本

0.3.0stable
2026/9/26
0.2.0stable
2026/8/20
查看其余 3 个版本收起版本
0.1.5-rc.1prerelease
2026/9/10
0.1.2-rc.1prerelease
2026/9/3
0.1.2-alpha.4prerelease
2026/9/2

相关插件

正在加载相关插件…

最新版
0.3.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/20
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 search-research 分类下经过校验的插件。

MarketdshmarketDeepSeek Harness 内的可视化插件市场——浏览、搜索并一键安装社区插件。· DSH 可视化插件市场:浏览、搜索并一键安装社区插件。Free Searchdsh-free-searchDeepSeek Harness 的免费网页搜索:27 个引擎(Bing/DuckDuckGo/AnySearch/SearXNG/Exa/Tavily/Keenable/Firecrawl/Parallel/You.com 无需密钥;通过 MCP 支持 Zhihu 全局/站内搜索,以及 Perplexity/SerpBase/DeepSeek(官方 + 原生工具/搜索)/Baidu/Kimi/Aliyun/Douba)Ab Wechat Scrapedsh-ab-wechat-scrape--- description: "树外 wechat_scrape 工具:获取微信公众号文章,过滤推广区块,并将每篇文章保存为 Markdown 文件,供此机器的 dsh 配置文件维护者使用。" kind: "package-reference" ---Ab Market Researchdsh-ab-market-research一个树外 DeepSeek Harness 插件:将 trade_search 工具(多轮扩展方面的查询规划、出版商级别排序、来源多样性、JSON 快照)及其浏览器卡片集成在一个包中。

README

dsh-web-search-custom

English | 简体中文

Point the DeepSeek Harness (dsh) web profile at any JSON search API — give it a URL and (optionally) an API key, and it replaces the built-in DeepSeek search. It ships pointed at the AnySearch unified search API, and the keyless path needs no account at all:

POST https://api.anysearch.com/v1/search    # apiKey empty -> anonymous tier, no Authorization header
POST https://api.anysearch.com/v1/search    # apiKey set   -> Authorization: Bearer <key>, that key's quota

[!IMPORTANT] Design intent. The plugin is a thin adapter, nothing more: it owns one search provider behind the ctx.web capability seam and one settings card. It ships no search engine, no index and no cache; the only credential it ever sends is the one you type into the card (or export as an environment variable), and an empty key means no auth header at all. Uninstall it and the stock provider is back with zero residue.


Table of Contents

  • Why
  • Features
  • How it works
  • Requirements
  • Install
  • Configure in the settings page
  • Configuration reference
  • Vertical and parameterized queries
  • Other configuration channels
  • Uninstall
  • Limits
  • Development
  • License

Why

dsh's web profile resolves search through the ctx.web seam. Out of the box that seam is served by a hosted search backend, which is the wrong trade for anyone who runs their own search stack (SearXNG, an internal search gateway, a vendor API), wants queries to stay inside their own network, or wants to bring their own search key.

This plugin registers one additional provider into that seam and lets you select it. Everything else — the web_search tool, result capping, cancellation, error propagation — keeps working unchanged, because it is the same seam the stock provider uses.

Features

  • Standard dsh bundle plugin. Zero intrusion: no dsh source is modified; removing the plugin restores the stock search provider.
  • Zero runtime dependencies. Only dsh-shipped packages are used (@deepseek-ai/dsh-settings, @deepseek-ai/schemastery, plus React on the browser side). Nothing is vendored or bundled.
  • AnySearch by default, keyless out of the box. api: auto detects the AnySearch host and speaks its native POST envelope. Leave apiKey empty and the request carries no Authorization header (anonymous, per-IP rate-limited free tier); set a key and it is sent as Authorization: Bearer <key> against that key's quota. An invalid key becomes a clear error instead of a silent fallback to anonymous.
  • SearXNG and friends still work. Switch api to generic for templated GET/POST endpoints; the shipped mapping matches SearXNG's results[] / url / title / content / publishedDate shape.
  • In-settings configuration card (Settings → Plugins): profile, URL, API key, result cap, timeout and the whole field mapping are editable in the page, with the same overridden/reset affordances as first-party plugin cards.

How it works

web_search tool
      │
      ▼
ctx.web (capability seam)  ──selection──▶  provider id "custom"
      │                                          │
      │                                          ▼
      │                         fetch(your endpoint)  ──▶  your JSON API
      │                                          │
      └◀──── WebSearchResult { sources[], truncated } ◀──┘

The host side registers the namespace web-search-custom (so the settings page can render a card for it) and registers a search provider with the stable id custom. The provider's search(query, signal) reads the live settings value on every call, picks a profile, performs the HTTP request with the caller's cancellation signal plus its own timeout, and maps the JSON payload into sources[] (deduplicated by URL, non-http(s) entries dropped, HTML stripped, entities decoded).

apiRequestResponse mappingFields that apply
anysearch (auto-detected)POST {"query","max_results"}; Authorization: Bearer only when a key is setdata.results[].{title,url,snippet}, falling back to contenturl, apiKey, headers, timeoutMs, maxResults
generictemplated GET/POST ({query} URL-encoded, {queryRaw} verbatim, {apiKey})resultsPath + urlField / titleField / snippetField / publishedFieldall of the above plus method, body, authHeader, authScheme, resultsPath, the four field names

Both a non-2xx status and a non-zero business code fail the search. The vendor's message and request_id are preserved in the error text; credentials that the API returns inside a 402 body are redacted before the message can reach a log or the model.

Provider selection follows the seam's own rules, resolved at call time: a configured id wins; with no id configured, exactly one usable provider is required. The plugin's own cordis.patch.yml therefore also sets web.searchProvider: custom so the choice is explicit rather than accidental.

Requirements

ItemValue
DeepSeek Harness`>=0.1.2-alpha.3 <0.1.8
Node.js`^22.19.0
Runtime dependenciesnone — the peers come from the dsh host install
Networkoutbound access from the dsh host to the endpoint you configure (AnySearch resolves to its mainland-China gateway)
AnySearch API keyoptional — the anonymous tier works without one

The dsh range is declared under dsh.engines.dsh (and mirrored in peerDependencies for the dsh packages the plugin relies on). The disjunction is load-bearing rather than cosmetic: npm semver only satisfies a prerelease from a range group that itself contains a prerelease with the same [major, minor, patch] tuple, so a plain <0.2.0 group does not cover 0.1.5-rc.1 or 0.1.7-rc.1. DSH itself checks the same peers at install and startup preflight with includePrerelease: true; that gate is more permissive, which is precisely why the pnpm-strict side needs its own counter-proof. tests/entry.test.mjs pins this with a decision table, a counter-proof against the old single range, and tests/host-integration.test.mjs runs the host's real evaluatePluginCompatibility and real semver against it.

[!IMPORTANT] dsh 0.1.7 moved the settings model. The plugin was adapted for 0.1.7-rc.1: settings.installSection() is gone (an exported Config schema is the namespace now, and its fields must be .volatile() to be editable live), the browser-side settingsScope service was replaced by configForms, and the card moved from the removed settings.plugin.item slot to plugins.item on the Plugins page. The card renders through the shared official form primitives, so it looks and behaves like the built-in provider cards. Details: docs/DSH-0.1.7-ADAPTATION.md.

Install

dsh plugin --profile web add dsh-web-search-custom

Restart the dsh web instance afterwards. From a local checkout, pass the directory path instead of the package name.

Configure in the settings page

Open Settings → Plugins in the web UI and click into the "Custom search (web-search-custom)" row — its page shows the form:

  • the API profile (auto / anysearch / generic),
  • the search URL (AnySearch by default),
  • the API key — leave it empty for the keyless tier,
  • the result cap (maxResults, 1–10 on AnySearch),
  • request method, POST body template, extra headers (JSON), timeout,
  • the result field mapping (resultsPath / urlField / titleField / snippetField / publishedField; generic profile only).

Saving writes the web-search-custom: entry of your profile patch document ($DSH_HOME/profiles/web/cordis.patch.yml) and takes effect immediately — the next search uses the new value, no restart. The form, its overridden/reset badges and its read-only notice are the same shared components the first-party provider cards use.

If an older release pinned a different URL there, clear that field (Reset → Save) so the shipped AnySearch default applies again.

Configuration reference

FieldDefaultDescription
apiautoProfile: auto (decided by the URL host), anysearch (AnySearch POST API), generic (any templated JSON endpoint)
urlhttps://api.anysearch.com/v1/searchSearch endpoint. The AnySearch profile POSTs here; the generic profile supports {query} (URL-encoded), {queryRaw} (verbatim) and {apiKey} placeholders and appends q= for GET without a {query}
apiKeyemptyOptional. Empty in the AnySearch profile sends no Authorization header (anonymous free tier); set it to send Authorization: Bearer <key>. In the generic profile the header name/scheme fields apply, and a {apiKey} in the URL or body suppresses the extra header
maxResults10AnySearch profile only: sent as max_results, computed as min(this, caller limit) and clamped to 1–10
methodGETGeneric profile only: GET or POST; anything else degrades to GET
body{"query":"{query}"}Generic profile only: POST body template, supports {query} / {queryRaw} / {apiKey}
headers{}Extra request headers as a JSON string
authHeaderAuthorizationGeneric profile only: header name used for the API key
authSchemeBearerGeneric profile only: auth scheme; leave empty to send the bare key
timeoutMs30000Per-request timeout
resultsPathresultsGeneric profile only: dotted path to the result array, e.g. data.results

In the generic profile each field also falls back to a wider list of common aliases at mapping time (for example a missing titleField value still tries title then name). The AnySearch profile always reads data.results[].title / .url / .snippet and falls back to .content.

Vertical and parameterized queries

The AnySearch profile sends a plain general-web query. For vertical search (tag / params / zone / language — see the API docs) switch to the generic profile and write the body yourself; the response envelope stays the same, so only the field mapping changes:

- id: web-search-custom
  config:
    api: generic
    url: 'https://api.anysearch.com/v1/search'
    method: POST
    body: '{"query":"{queryRaw}","tag":"code.doc","params":{"library":"golang"},"max_results":5}'
    resultsPath: data.results
    urlField: url
    titleField: title
    snippetField: snippet
    publishedField: ''

Other configuration channels

Profile patch layer (settings document alternative)

- id: web-search-custom
  config:
    api: auto
    url: 'https://api.anysearch.com/v1/search'
    apiKey: ''
    maxResults: 10
    method: GET
    body: '{"query":"{query}"}'
    headers: '{}'
    authHeader: Authorization
    authScheme: Bearer
    timeoutMs: 30000
    resultsPath: results
    urlField: url
    titleField: title
    snippetField: content
    publishedField: publishedDate

Note: a patch replaces the whole config block — write every key when overriding.

Environment variables

Environment overrides win over the settings page and require no file edit:

VariableDescription
WEB_SEARCH_CUSTOM_APIOverride the profile (auto / anysearch / generic)
WEB_SEARCH_CUSTOM_URLOverride the search URL
WEB_SEARCH_CUSTOM_API_KEYOverride the API key (recommended for secrets); empty keeps the keyless tier
WEB_SEARCH_CUSTOM_MAX_RESULTSOverride the AnySearch result cap
WEB_SEARCH_CUSTOM_HEADERSExtra headers, JSON string
WEB_SEARCH_CUSTOM_TIMEOUT_MSTimeout in milliseconds

Uninstall

dsh plugin --profile web remove dsh-web-search-custom

Restart dsh and the stock search provider takes over again.

Limits

  • The API key is stored as an ordinary settings field, not in a credential store. For sensitive deployments prefer WEB_SEARCH_CUSTOM_API_KEY.
  • AnySearch caps a single request at 10 results (max_results 1–10); the tool layer truncates further to the caller's maxResults.
  • AnySearch's anonymous tier is rate-limited per client IP and metered against a daily free quota. When it runs out the API answers 402 and may return auto-generated credentials; the plugin surfaces that as an error and redacts those credentials instead of storing them. Add a key to keep searching.
  • Result mapping is field-based, not query-language aware: the generic profile needs the field mapping (and, if necessary, resultsPath) adjusted for exotic payload shapes.

Development

pnpm install         # dev dependencies (@deepseek-ai/dsh + dsh-settings 0.1.7-rc.1,
                     # schemastery 3.18.4 — the host-contract test loads the real packages)
pnpm test            # node --test tests/*.test.mjs && node tests/client-smoke.mjs
pnpm run test:host   # host-contract subset, on its own

pnpm is required for the dev install: the dsh devDependency pulls native subprocess helpers whose build scripts must be explicitly declined (see allowBuilds in pnpm-workspace.yaml) — nothing here needs a native build.

TestWhat it proves
tests/entry.test.mjsReal entry load (import('../src/index.js')), manifest declarations, the 0.1.7 settings-namespace source (Config exported, every field .volatile(), {get()} live references, configure({ auto: false })), the engines decision table (hand-rolled comparator + counter-proof + cross-check against the host's real semver.satisfies), four-way key parity (host schema ↔ client fields ↔ client views ↔ patch config), locale key parity, static guards against the removed client APIs, dependency hygiene
tests/anysearch.test.mjsAnySearch profile: auto-detection, keyless vs Bearer headers, max_results arithmetic, vendor envelope mapping, the error envelope (message + request_id + 402 credential redaction), and generic-profile non-regression
tests/host-integration.test.mjsDriven by the real host packages: real Cordis + WebRuntime + the real @deepseek-ai/cosmokit volatile protocol (a _commitVolatile-style update is visible to the next search), the real dsh-settings schema helpers (volatileForm / isVolatilePath / projectForm / plainConfig), real source-level assertions on the client bundles this card depends on, and the host's real evaluatePluginCompatibility + semver against the declared ranges
tests/client-smoke.mjsBrowser half: bundle id, short service names, locale parity, configForms.whileServed gating, the plugins.item registration contract (id/order/label thunk), both views (summary one-liner vs page form), save/reset write path, invalid-input blocking, read-only disabling and the read-only write refusal
tests/live-search.mjsOpt-in live check against a real endpoint: DSH_WSC_LIVE_URL='https://api.anysearch.com/v1/search' node tests/live-search.mjs (add DSH_WSC_LIVE_BAD_KEY=1 to also assert the invalid-key error path)

Live checks (opt-in, they hit the network)

# end-to-end through the real seam against a live endpoint
DSH_WSC_LIVE_URL='https://api.anysearch.com/v1/search' node tests/live-search.mjs 'your query'
# the same, plus the invalid-key error path
DSH_WSC_LIVE_URL='https://api.anysearch.com/v1/search' DSH_WSC_LIVE_BAD_KEY=1 node tests/live-search.mjs 'your query'
# two-arm control: prove the user-layer url is what decides, and that the new default returns results
DSH_WSC_LIVE_PARITY=1 node tests/live-settings-parity.mjs

The default pnpm test suite stays offline-runnable; these scripts print SKIP unless the environment variable above is set.

src/index.js          host half — settings namespace, profile dispatch, result mapping
lib/client.js         browser half — settings card (hand-written bundle, no build step;
                      renders through the shared official form primitives)
cordis.patch.yml      bundle patch: select provider "custom" and mount the plugin
tests/                entry + anysearch + host-contract + client-smoke + opt-in live check

License

MIT

urlField / titleField / snippetField / publishedFieldurl / title / content / publishedDateGeneric profile only: result field-name mapping (SearXNG's defaults)