Bundle
@jpa957/dsh-web-search-anysearch
AnySearch-backed search provider plugin for DeepSeek Harness (ctx.web) with round-robin API-key rotation, per-key cooldown failover, and a Settings-page card
- Source
- JPA957
- License
- MIT
- Updated
- Updated 4 days ago
Readme
# @jpa957/dsh-web-search-anysearch
[English] | [中文](README.zh.md)
An [AnySearch](https://api.anysearch.com)-backed search provider plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), registered on the `ctx.web` seam (`web-search-anysearch`).
The plugin ships two halves, like the built-in providers:
- **Host half**: registers the `anysearch` search provider with round-robin key rotation and per-key cooldown failover, and installs a settings section so the configuration is editable in the web app.
- **Browser half**: its own tab in the Settings navigation (**Settings → AnySearch**) where the API key, endpoint, and result count are filled in the UI — no patch-file editing.
## Install
```sh
dsh plugin --profile web add @jpa957/dsh-web-search-anysearch
```
(or from a local checkout: `dsh plugin --profile web add /path/to/dsh-web-search-anysearch`)
The bundle's patch layer registers the provider automatically. To make it the web seam's **default** search backend, add the pin to your own patch layer, applied after the bundle's layer:
```yaml
- id: web
config:
searchProvider: anysearch
fetchProvider: http
```
> **Migrating from a hand-mounted copy?** Remove the old `- insert:` row that pointed at the plugin file from your profile patch first — the bundle now supplies its own registration, and a duplicate id would fail the composition. Keep the `searchProvider: anysearch` pin.
## Configuration in the Settings tab
Open the web app's **Settings → AnySearch** tab: the page lets you set
- **API key** — stored in the Host's credentials store under the key reference below; the literal is never sent back to the browser (the card only reports configured / unset).
- **Key reference** — the credential/environment name the provider resolves per search; defaults to `ANYSEARCH_API_KEY`. A comma-separated value forms a key pool.
- **Endpoint base** — defaults to `https://api.anysearch.com` when blank.
- **Max results** — result count requested per search (1-10).
Edits are staged and written on **Save**; **Discard** reverts. The settings namespace is `web-search-anysearch` (the section the card edits), so the same configuration can also be managed through the settings document.
### Key-pool behavior
Keys are gathered from the credentials reference (comma-split), plus any composition-layer `apiKey`/`apiKeys` literals, deduplicated. Searches rotate round-robin; a key-scoped failure — rate limiting (HTTP 429 / "retry after N seconds", honoring the API's `retry-after` header), quota exhaustion, or auth rejection (401/403) — puts that key on cooldown (60s default, 10min for auth/quota) and retries the next key **within the same search**, up to six attempts. An exhausted pool aggregates one `WEB_PROVIDER_ERROR` naming the pool size, masked key, and the API's `request_id`. An empty pool searches anonymously.
## Key safety
Keys enter only through the credentials domain (Settings page or `$ANYSEARCH_API_KEY`), or through the composition config if you explicitly put them there. This repository contains no secrets; tests use obviously fake `as_sk_unit-*` keys. Diagnostics mask keys to their first ten characters.
## How it behaves
- **Rotation**: round-robin cursor advances on every successful search.
- **Failover**: key-scoped failures cool the key down and retry the next key inside the same search; non-key-scoped failures (network, 5xx, unprocessable bodies) surface immediately.
- **Self-correction**: if every key is cooling down, the earliest-deadline key is retried anyway.
- **Aborts** surface as `WEB_ABORTED` from the fetch, body-parse, and signal phases.
- **Mapping**: entries without a URL are dropped, duplicate URLs collapse, `content` is preferred over `snippet`; `truncated` is always `false` because the web service owns the final `maxResults` bound (clamped to the API's documented 1-10).
## Development
```sh
node tests/unit.mjs # offline unit tests (plain node, no framework)
node tests/client-bundle.mjs # client-bundle contract smoke test
node tests/live.mjs # live anonymous smoke test against the real endpoint
```
The browser half (`lib/client.js`) is a prebuilt, hand-maintained bundle in the client-module factory format — no build step is required. When installing from a git URL or npm, the committed `lib/client.js` is what the web app serves.
## License
[MIT](LICENSE)
Install
dsh plugin --profile web add github:JPA957/dsh-web-search-anysearch
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install jpa957-dsh-web-search-anysearch from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.