Skip to content
dsh.fish
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

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source