DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

leoporoluo /

leoporoluo/dsh-web-access

Verified

DeepSeek Harness plugin: routes native web_search through the keyless pi-web-access search engines (Exa, Keenable, DuckDuckGo, AnySearch) with ordered failover. Adapter only, no search logic.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@41de6e1c

leo-web-access

A DeepSeek Harness (dsh) plugin that routes the harness's native web_search through the keyless search engines that ship inside pi-web-access.

It is an adapter, not a search engine. No search logic is re-implemented here: the engines are pi-web-access's own provider modules, compiled and vendored at build time, and this plugin only picks one, runs it under a time budget, and maps the result into dsh's web seam.

web_search (native dsh tool)
        │
        ▼
  ctx.web.search()          ← dsh web seam, provider id "webaccess"
        │
        ▼
  leo-web-access/index.js   ← this plugin: order, budget, mapping
        │
        ├── exa          (MCP, keyless)    ~0.9 s
        ├── keenable     (MCP, keyless)    ~0.6 s
        ├── duckduckgo   (HTML, keyless)   ~1.1 s
        └── anysearch    (anonymous quota) ~1.8 s

The npm package is leo-web-access; the GitHub repository is still named dsh-web-access. The package name was chosen because dsh-web-access on npm already belongs to an unrelated plugin — installing that one by mistake would be worse than an unmemorable name.

Why the engines can be transplanted

pi-web-access is a Pi extension, so dsh plugin add pi-web-access cannot work directly: it has no dsh.bundle. Its dist/ is a single esbuild file exporting only four symbols, and the search internals are unreachable from there.

Its source .ts files are published alongside dist/, and the provider modules do not depend on the Pi runtime on the search path. Node refuses to strip types under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so the sources are compiled once, at build time, into dist/providers/*.mjs.

Two rules keep that honest:

  • No vendored source. pi-web-access is a pinned dependency ("pi-web-access": "0.46.0"); npm run build reads node_modules and recompiles. Bumping the pin is the whole upgrade.
  • No Pi packages. build.mjs scans every produced bundle and deletes any provider that reaches an @earendil-works/pi-* runtime import, reporting it instead of shipping something broken.

Install

The desktop profile is managed by the Electron app, so install through the UI: 插件 → 添加插件, point it at this directory (or a packed tarball), then restart dsh.

Then confirm the plugin row is present and enabled on the Plugins page.

Configuration

The bundle patch pins the web row and mounts the plugin:

- id: web
  config:
    searchProvider: webaccess
    fetchProvider: http      # restated: a patch row replaces the whole config

- insert:
    - id: webaccess
      name: leo-web-access

Row config (all optional, every value falls back to a working default):

key default meaning
providers ["exa","keenable","duckduckgo","anysearch"] failover order
budgetMs 15000 wall-clock budget for one search() call
attemptTimeoutMs 7000 per-engine timeout
snippetMaxChars 300 snippet characters kept per source

degoog is not in the default chain: it measured ~4 s per query, which would consume most of the budget before the first fallback. Add it to providers if you want it.

API keys are optional

All four default engines work with no key. Keys only upgrade an engine to its keyed tier, and this plugin feeds them in from either source, at call time:

  1. dsh credentials — ctx.credentials.resolve('EXA_API_KEY') and friends. A resolved value is placed in process.env right before the attempt, which is where the upstream modules read it from.
  2. Pi's own config — ~/.pi/agent/web-search.json, read by the upstream modules themselves, so a user who already set up Pi does not re-enter keys.

Recognised names: EXA_API_KEY, KEENABLE_API_KEY, ANYSEARCH_API_KEY.

Behaviour

  • Ordered failover. The first engine that is available and returns at least one result wins; anything else moves to the next entry.
  • Honest trails. content names the engine that answered, its latency, the failover position, and the credential source. A total failure throws with one line per attempt — which engine, why, and how long it took.
  • Snippets are recovered, not invented. Exa's keyless MCP route returns snippet: "" and puts the readable excerpt in its answer text as <excerpt>\nSource: <title> (<url>). Those excerpts are lifted back onto the matching source, so citation cards and the model both get text. Nothing extra is fetched.
  • fetchProvider: http stays pinned so web_fetch never becomes ambiguous (WEB_PROVIDER_AMBIGUOUS) once the host also registers a fetch provider.

Development

npm install
npm run build          # compile dist/providers/*.mjs from node_modules, then check deps
npm run smoke          # mount the plugin against a stub ctx + live search
node scripts/smoke.mjs --offline

--offline mounts the adapter without touching the network.

dist/providers.json is the build manifest and is committed. It carries no timestamp, so rebuilding an unchanged pin produces byte-identical output and no spurious diff.

A warn install-scripts line about esbuild's postinstall is expected under a package manager with script approval; esbuild still works.

Verification status

Checked against the live desktop profile:

  • leo-web-access appears in the profile's bundle list and its webaccess row mounts (cordis_inspect_query → include:webaccess).
  • The native web_search tool answers through the adapter, reporting Engine: Exa (exa) · 8 results in its content.
  • Disabling the bundle does not strand the setting. With the bundle off, web_search still answered (via the profile's own modsearch layer) instead of failing with configured web provider "webaccess" is not registered; re-enabling restored the adapter.

Not verified in this environment: the rendered Plugins-page row and its icon (no browser control from here), and dsh --dump-config (dsh is not on PATH — the app is launched from the Electron bundle). The patch contents are as shown above; both provider pins are present in cordis.patch.yml.

Uninstall safety

Disabling or removing this bundle removes its patch row, so the profile returns to its own searchProvider setting instead of leaving webaccess configured but unregistered (the configured web provider "webaccess" is not registered failure).

License

MIT. Vendored build products derive from pi-web-access (MIT); its source and license remain in node_modules and are not committed here.

—/ 5

No ratings yet

Verified DSH bundle

Commit 41de6e1cf1a2

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout