Bundle
@tianjiqx/dsh-wigolo
Full wigolo integration for dsh: WebSearch/WebFetch providers with configurable takeover, 7 wigolo_* agent tools (search/crawl/extract/research/find_similar/cache/watch), a sidebar config panel, and cordis-managed provider routing.
- Source
- tianjiqx
- stars
- 3 stars
- License
- Apache-2.0
- Updated
- Updated 11 days ago
Readme
# dsh-wigolo
[中文文档](./README.zh.md)
**Self-hosted web search for [DSH](https://github.com/deepseek-ai/dsh) (DeepSeek Harness).** This plugin integrates a privately deployed [wigolo](https://github.com/KnockOutEZ/wigolo) meta-search daemon into DSH, replacing the built-in web search with your own self-hosted search infrastructure — full control over search engines, caching, and data privacy.
> **Core purpose:** Proxy and replace DSH's built-in `web_search` / `web_fetch` with a self-hosted wigolo daemon. One toggle switches all agent web searches to your private deployment — no cloud dependencies, no API keys, zero cost per query.
```
dsh web GUI ── sidebar panel ── /api/dsh-wigolo/* ──┐
│
agent tools (wigolo_search, …) ── MCP streamable-http ──► wigolo daemon
web seam (web_search / web_fetch) ───────────────────┘ 18+ engines · RRF · local cache
```
## Features
- **Agent-first design** — seven tools (`wigolo_search`, `wigolo_crawl`, `wigolo_extract`, `wigolo_research`, `wigolo_find_similar`, `wigolo_cache`, `wigolo_watch`) expose trimmed, model-friendly parameter surfaces. Write operations stay agent-only; read-only cache/watch browsing also lives in an opt-in **"Wigolo Cache"** conversation tab (off by default).
- **Provider takeover, configurable** — route the official `web_search` / `web_fetch` through wigolo with one switch (on = wigolo drives both, off = official providers), toggled from the GUI with automatic cordis routing management.
- **Official settings integration** — `enabled`, `announceToAgent`, and `guidance` override live in the official DSH settings UI (dsh-ssh pattern). Hot-reload: change settings, no restart needed.
- **Sidebar panel** (React, i18n) — four tabs: Connection (live test + latency), Takeover & Tools, About, and Help (bundled usage guide rendered in-panel). Connection settings with live test + latency, takeover switch, tool exposure flags, and the cache-tab toggle, all loopback-fenced. Token can be written directly from the panel (no terminal required).
- **Hot-reconfigure** — connection and token edits take effect immediately (MCP client hot-reconfigured); only the takeover switch and tool exposure changes need a restart.
- **Fail-loud config validation** — unknown config keys trigger warnings with "did you mean?" hints instead of being silently dropped.
- **Secret-safe by construction** — the token lives in its own 0600 file, never in the config JSON, never returned to the browser.
- **Timezone-aware cache timestamps** — wigolo daemon stores timestamps in zone-less UTC; the plugin converts them to your configured timezone (`local`, numeric offset like `+8`, or IANA name like `Asia/Shanghai`) so `wigolo_cache` results display in your local time.
## Prerequisites
A running [wigolo daemon](https://knockoutez.github.io/wigolo/docs/) (v0.2+), reachable over HTTP with a bearer token. Local (`127.0.0.1:3333`) is the default and needs zero extra setup.
## Install
### From npm
```bash
dsh plugin --profile web add @tianjiqx/dsh-wigolo
```
### From GitHub
```bash
dsh plugin --profile web add github:tianjiqx/dsh-wigolo
```
Both methods install the plugin and automatically register it in the profile's bundle list (via the plugin's bundled `cordis.patch.yml`). No manual editing required.
### Local development (link mode)
```bash
git clone https://github.com/tianjiqx/dsh-wigolo.git
cd dsh-wigolo
pnpm install
pnpm build
dsh plugin --profile web add link:$PWD
```
This clones the repo, builds it, and registers the plugin in the profile's bundle list automatically via `link:` (no manual `cordis.patch.yml` editing needed).
### Post-install
Put your daemon token into `~/.dsh/wigolo-token` (first line, 0600):
```bash
echo "YOUR_TOKEN" > ~/.dsh/wigolo-token && chmod 600 ~/.dsh/wigolo-token
```
Restart dsh, open the **Wigolo** entry in the sidebar, and hit **Test connection**.
## Uninstall
```bash
dsh plugin --profile web remove @tianjiqx/dsh-wigolo
```
This removes the plugin and its bundle registration. The token file `~/.dsh/wigolo-token` and config `~/.dsh/wigolo.json` are preserved (delete manually if desired).
For detailed usage scenarios and examples, see the [Usage Guide (中文)](./GUIDE.zh.md).
## Configuration
### Via GUI (recommended)
All settings can be configured through the **Wigolo sidebar panel** (click the Wigolo icon in the sidebar):
- **Connection tab**: Host, port, token, hostHeader, test connection
- **Takeover & Tools tab**: Takeover switch, tool enable/disable, cache tab toggle
- **About tab**: Version info, documentation links
Changes take effect immediately (hot-reload), except for the takeover switch which requires a restart.
### Manual configuration file
For advanced settings not exposed in the UI (e.g., per-tool defaults, timeout overrides), edit `~/.dsh/wigolo.json` directly:
```jsonc
{
"version": 2,
"connection": {
"host": "127.0.0.1", // daemon address
"port": 3333,
"hostHeader": "auto", // auto | none | "<literal>"
"tokenFile": "" // "" = ~/.dsh/wigolo-token
},
"takeover": false, // true = wigolo drives web_search + web_fetch; false = official providers
"tools": {
"wigolo_search": { "enabled": true, "defaults": { "max_results": 10, "search_depth": "balanced" } },
"wigolo_crawl": { "enabled": true, "defaults": { "max_pages": 50 }, "timeoutMs": 300000 },
"wigolo_extract": { "enabled": true },
"wigolo_research":{ "enabled": true, "timeoutMs": 600000 },
"wigolo_find_similar": { "enabled": false },
"wigolo_cache": { "enabled": true },
"wigolo_watch": { "enabled": true }
},
"cacheTab": { "enabled": false }, // "Wigolo Cache" read-only GUI tab (default off; hot-apply)
"announceToAgent": true,
"timezone": "local" // cache timestamp timezone: "local" | "+8" | "-5" | "+5.5" | "Asia/Shanghai"
}
```
Per-tool `defaults` merge **under** the model's explicit arguments (the model always wins); `timeoutMs` overrides the built-in per-tool budget.
### Takeover switch
`takeover` is a simple boolean:
| Value | `web_search` | `web_fetch` | Notes |
|------|--------------|--------------|-------|
| `true` | wigolo | wigolo | full replacement |
| `false` | official | official | wigolo_* tools only (default) |
Why a switch at all: when several providers register into the web seam and none is explicitly routed, `web_search` fails with `WEB_PROVIDER_AMBIGUOUS`. Takeover on (`true`) therefore also writes a self-managed block into `~/.dsh/cordis.patch.yml` (`searchProvider: wigolo`, dsh-skin-style managed markers); takeover off (`false`) removes it and registers nothing, so it coexists safely with the official provider. Routing changes need a dsh **restart** — the panel tells you when.
### Timezone
The wigolo daemon persists cache timestamps as zone-less UTC (`"YYYY-MM-DD HH:MM:SS"`). The plugin converts them to the configured timezone before returning to the agent or rendering in the UI.
| Value | Example | Description |
|-------|---------|-------------|
| `"local"` | `"local"` | Use the DSH host's system timezone (default) |
| Numeric offset | `"+8"`, `"-5"`, `"+5.5"` | Fixed offset from UTC; supports half-hour zones |
| IANA name | `"Asia/Shanghai"`, `"America/New_York"` | Full timezone with DST rules |
Changes to `timezone` take effect after a dsh restart.
### The Host header, explained
wigolo guards against DNS-rebinding by allowlisting `Host` values: `localhost`, the loopback literals, and its own bind host. Two consequences:
- A daemon bound to `0.0.0.0` on a LAN machine accepts requests whose `Host` header is `0.0.0.0`.
- `fetch()` refuses to set `Host` (Fetch spec), so the plugin uses `node:http`, which allows it.
`hostHeader: "auto"` (default) sends **no** custom header for local daemons and the bind-host trick for remote ones. Set a literal only if your deployment needs it.
## Agent tools
| Tool | Wigolo capability | Highlights | Timeout |
|------|-------------------|-----------|---------|
| `wigolo_search` | search | category / time_range / domain filters / depth tiers / `"a \| b"` multi-variant queries | 60s |
| `wigolo_crawl` | crawl | site crawl with patterns, strategy, page caps; every page lands in the cache | 300s |
| `wigolo_extract` | extract | CSS selector or field-schema structured extraction | 60s |
| `wigolo_research` | research | decomposes, searches in parallel, synthesizes a cited report | 600s |
| `wigolo_find_similar` | find_similar | related content from URL or concept (default off) | 120s |
| `wigolo_cache` | cache | search the local cache **before** hitting the network; stats / clear | 30s |
| `wigolo_watch` | watch | persistent URL change monitoring; pair with a scheduled agent task for notifications | 120s |
All timeouts are configurable per tool via `timeoutMs` in the config.
## Security notes
- Panel routes are **loopback-only** (remote address + Host + `sec-fetch-site` + origin checks) — they read/write private config and must never be served to LAN-exposed deployments.
- The token sits in `~/.dsh/wigolo-token` (0600); API responses never include it.
- `wigolo_cache clear` is destructive; the tool description tells the model to confirm first.
## Development
```bash
pnpm install
pnpm test # vitest (61 tests)
pnpm typecheck # tsc --noEmit
pnpm build # lib/index.mjs + lib/client.js (CSS inlined)
node test/smoke-real-daemon.mjs # manual smoke against a live daemon
```
## License
Apache-2.0Install
dsh plugin --profile web add github:tianjiqx/dsh-wigolo
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 tianjiqx-dsh-wigolo from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.