dsh-web-search-duckduckgo
English | 中文
Free DuckDuckGo search for the DeepSeek Harness — no API key, no credentials, no per-search cost. This repository publishes two npm packages:
packages/dsh-web-search-duckduckgo/ — the implementation package: a WebSearchProvider (id duckduckgo) registered into ctx.web, plus the model-facing web_search_ddg tool that calls the provider directly. It declares no dsh.bundle, so it is not installed as a composition layer by itself.
packages/dsh-web-search-duckduckgo-bundle/ — the installable bundle: it declares dsh.bundle.patch, and its cordis.patch.yml inserts one web-search-duckduckgo row. The row points at the bundle's own entry (the bundle entry re-exports the embedded implementation package), so the packed artifact is self-contained — bundleDependencies packs the implementation package (with schemastery/cosmokit) into the tarball.
dist/ — the built tarballs. Distribution needs only the single bundle file.
Install
The packed artifact embeds the implementation package, so one command installs and mounts it into a profile:
dsh plugin --profile <name> add ./dist/dsh-web-search-duckduckgo-bundle.tgz
Restart that profile afterwards (a new bundle layer / new package needs a restart; cordis.patch.yml and settings edits take effect live), then confirm the composed tree carries the web-search-duckduckgo row with dsh --profile <name> --dump-config.
Once the package reaches a registry, install by bundle name:
dsh plugin --profile <name> add @deepseek-ai/dsh-web-search-duckduckgo-bundle
Installing only the implementation package (./dist/dsh-web-search-duckduckgo.tgz) lands it as a plain dependency with no composition layer; that artifact is for deployments that mount the row manually in their own cordis.patch.yml.
Hot-plug and dynamic loading
- Loading:
dsh plugin add is pnpm profile management plus dsh.profile.bundles reconciliation; the bundle patch composes the plugin tree over an empty root, so the web-search-duckduckgo row is an ordinary peer of every other DSH row.
- Unloading:
dsh plugin --profile <name> remove @deepseek-ai/dsh-web-search-duckduckgo-bundle removes both the dependency and the bundle layer; in the profile's own cordis.patch.yml, { id: web-search-duckduckgo, disabled: true } temporarily unloads it without touching the package, and removing that patch mounts it again.
- Runtime cleanup: the plugin is a named-export function plugin (
name/inject/Config/apply, no default export); the provider, the web_search_ddg tool, the system-prompt section, and the settings section are all fiber-scoped effects, so HMR replacement or row removal unwinds them with the fiber. Unit tests and the installed-artifact verification both cover disappearance of the tool and provider after dispose.
- Live config: the
web-search-duckduckgo settings section is re-resolved on every search; a committed settings change applies without re-mounting the plugin or restarting.
Configuration
All options are optional; a deployment that pins another provider on the web seam (the shipped searchProvider: deepseek-official) can offer this free route side by side and let the model pick per turn.
| Key |
Default |
Meaning |
proxy |
(unset) |
host:port or http://host:port CONNECT proxy for endpoints a direct connection cannot reach. Unset = direct; a failing tunnel falls back to a direct fetch. |
region |
us-en |
Region code for the HTML form's l field (e.g. us-en, uk-en, de-de). |
baseURL |
https://html.duckduckgo.com/html/ |
HTML search endpoint, searched with a POST q/b/l form. |
fallbackBaseURL |
https://api.duckduckgo.com/ |
Instant Answer JSON endpoint used when HTML yields no sources. |
userAgent |
browser UA |
User-Agent header sent on every request. |
timeoutMs |
30000 |
Per-request timeout budget in ms. |
maxResults |
8 |
Source cap when a request carries no maxResults. |
Configure through the web-search-duckduckgo settings namespace or a composition overlay:
- id: web-search-duckduckgo
name: '@deepseek-ai/dsh-web-search-duckduckgo-bundle'
config:
proxy: http://127.0.0.1:8080
How it works
The HTML search POSTs a q/b/l form (query, bang placeholder, region) to html.duckduckgo.com/html/, so result__a hrefs come back as direct links instead of the advertising uddg wrapper a GET returns. Results are parsed from the current results_links layout, normalized (Unicode NFC, control characters removed), and y.js advertising trackers are dropped. When HTML yields no sources, the Instant Answer JSON endpoint is the fallback. An optional zero-dependency CONNECT tunnel (node:net + node:tls) reaches endpoints a direct connection cannot.
Build from source
This repository carries its own pnpm workspace, so it installs, builds, tests, and packs independently of a DeepSeek Harness checkout:
pnpm install
pnpm build
pnpm test
pnpm pack
pnpm-workspace.yaml uses nodeLinker: hoisted, which bundleDependencies packing requires. The rebuilt tarballs land in dist/; distribution needs only dsh-web-search-duckduckgo-bundle.tgz.
License
MIT