WeKnora
Tencent
Open-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.
stelmakhdigital/dsh-web-automation
Local-first web stack plugin for DeepSeek Harness: multi-engine keyless web search (DuckDuckGo/Bing, optional Exa/DeepSeek/Jina/SearXNG), SQLite-cached fetch, platform search (GitHub, Reddit, YouTube, …), history & stats, and optional Playwright browser automation. No paid search API, no cloud.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:stelmakhdigital/dsh-web-automation
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
🇬🇧 English | 🇷🇺 Русский
A DeepSeek Harness plugin that gives a local model a local-first web stack — no paid search API, no third-party data broker, no cloud required for the keyless engines.
It bundles four capabilities into one installable plugin:
| Capability | What it does | Keyless? |
|---|---|---|
| Multi-engine search | The web_search tool, routed across DuckDuckGo + Bing (keyless) and Exa / DeepSeek / Jina (opt-in, when their API keys are present). Fallback or fuse (parallel + RRF) routing, cooldowns, BM25 enrichment. |
✅ DDG + Bing |
| Cached fetch | The web_fetch tool backed by a SQLite page cache with ETag/Last-Modified revalidation. Repeats within the TTL make no network request. |
✅ |
| Web platforms | The web_platform_search tool: search a specific platform (GitHub, Reddit, YouTube, Bilibili, V2EX, RSS, …) via its own public endpoint. New platforms are added without code via config or versioned rule packs (hot-reloaded). |
✅ |
| History / stats / cache | web_history, web_search_stats, web_cache_clear — read the shared local store; no network. |
✅ |
All state is local: the store is $DSH_HOME/web.db. Outbound traffic for the keyless engines is limited to DuckDuckGo and Bing.
Prerequisites:
node:sqlite store.npx @deepseek-ai/dsh (no install) or a source checkout (pnpm install && pnpm run build, then pnpm dsh ...).Install the plugin into a profile (the bundle overlay is applied automatically):
dsh plugin --profile web add git+https://github.com/stelmakhdigital/dsh-web-automation.git
Boot the profile:
dsh web # Web UI at http://127.0.0.1:3080
dsh --profile tui # TUI (if you installed into `tui` instead)
At boot the plugin loads with the host: the web seam is pinned to the multi search / cached-http fetch providers, web_fetch is enabled, and web_platform_search plus the history tools are registered. No extra configuration is needed — every field is defaulted (keyless DuckDuckGo + Bing).
Verify — run the smoke test from a DSH session (e.g. web_search "hello world").
Replace web with your profile name. To inspect the composed profile tree: dsh --profile web --dump-config.
Plugins are installed into a DSH profile with dsh plugin — each profile is its own pnpm workspace under $DSH_HOME/profiles/<name>. At boot, DSH symlinks the host's @deepseek-ai/* packages into the profile's node_modules, so the plugin's peer dependencies resolve to the host's own copies.
This package is a DSH bundle: its package.json declares dsh.bundle, so installing it automatically applies the shipped local-web.cordis.yml overlay — it pins the web seam to the plugin's providers, enables web_fetch in the host tool-web row, and registers the plugin. No manual --patch needed:
dsh plugin --profile web add git+https://github.com/stelmakhdigital/dsh-web-automation.git
(Replace web with your profile name. The seam pin is required: without it the seam sees two usable search providers (the deployment default plus multi) and fails with WEB_PROVIDER_AMBIGUOUS.)
Manual alternative — if you want to tweak the config before applying, apply the overlay yourself:
dsh --profile web --patch "$PWD/local-web.cordis.yml"
The dsh-web-browser sub-package adds local Chromium (Playwright) automation behind the browser_* tools (browser_open, browser_navigate, browser_snapshot, browser_click, browser_type, browser_screenshot). It is separate because it pulls in Playwright + a Chromium download.
It is a sub-directory of this repo, and pnpm cannot install a sub-directory of a git repo directly. Install it from a local clone as a tarball (npm pack): unlike a link: install, the tarball is unpacked into the profile's node_modules, so the package's own dependencies (playwright) are installed and its @deepseek-ai/* imports resolve to the host's packages:
git clone --depth 1 https://github.com/stelmakhdigital/dsh-web-automation.git ~/dsh-plugins/dsh-web-automation
cd ~/dsh-plugins/dsh-web-automation
npm pack ./browser # → dsh-web-browser-0.3.0.tgz (lib/ is prebuilt in the repo)
dsh plugin --profile web add ./dsh-web-browser-0.3.0.tgz
# one-time: install the Chromium binary
dsh plugin --profile web exec playwright install chromium
The browser package is a bundle too — its patch (cordis.patch.yml in the tarball) registers the browser plugin row automatically. To update: git pull in the clone, npm pack ./browser again, and dsh plugin add the new tarball.
Add a row to your deployment's cordis.yml (or an overlay applied with dsh --patch ...). See cordis.yml.example for the full reference.
- id: web-automation
name: 'dsh-web-automation'
config:
search:
engines: [ddg, bing, exa, deepseek, jina] # tried in order
mode: fallback # fallback | fuse
region: '' # region/market hint (DDG kl, Bing setmkt)
# exa: { apiKeyEnv: EXA_API_KEY } # or apiKey: '...'
# deepseek: { apiKeyEnv: DEEPSEEK_API_KEY }
# jina: { apiKeyEnv: JINA_API_KEY }
fetch:
revalidate: true # conditional revalidation for fresh-but-expired pages
platforms:
tool: true # register web_platform_search
maxResults: 20
# platforms: # override built-ins by id, or add new platforms
# - id: my-site
# name: My Site
# format: json
# searchUrl: 'https://my-site.example/search?q={query}'
# fields: { items: 'data.results', url: 'link', title: 'title' }
history:
history: true # web_history
cacheClear: true # web_cache_clear
stats: true # web_search_stats
Every field is defaulted, so an empty config: {} (or no config at all) enables the full local web stack with the keyless engines.
This plugin is an externalized, standalone copy of DSH's internal web packages (web-search-multi, web-fetch-cached, web-platforms, web-store, web-browser, tool-web-history) and is currently ahead of upstream (SearXNG engine, news-mode freshness, embedding re-rank, LRU eviction, SSRF guard, inline screenshots).
multi, cached-http) and tool names. A deployment that loads both fails at startup with WEB_DUPLICATE_PROVIDER — keep one. If you use this plugin, do not apply DSH's examples/web-local overlay (or its preset copies), and vice versa.web_search and web_fetch are registered by the host's tool-web plugin; this plugin registers the providers behind them (plus web_platform_search and the history tools). The overlay above enables web_fetch in the tool-web row.src/ mark the upstream package each module mirrors.The keyless engines (DuckDuckGo, Bing) work with no configuration. To opt in to Exa / DeepSeek / Jina, provide their API keys either:
EXA_API_KEY, DEEPSEEK_API_KEY, JINA_API_KEY), orsearch.exa.apiKey, etc.), orOnce installed and configured, the model can:
web_search "query" (multi-engine, enriched snippets).web_fetch <url> (cached; repeats within the TTL make no network request).web_platform_search { platform: "github", query: "..." }.web_history, web_search_stats, web_cache_clear.dsh-web-browser) — browser_open, browser_navigate, browser_snapshot, browser_click, browser_type, browser_screenshot.$DSH_HOME/web.db, nothing is sent anywhere else.User-Agent, rate-limits itself (1 req/s per engine by default), and cools down blocked engines. Use responsibly.SSRF guard (on by default): requests to loopback, private, link-local, and otherwise reserved network targets (IPv4 0/8, 10/8, 127/8, 172.16/12, 169.254/16, 192.168/16; IPv6 ::1, ::/128, fe80::/10, fc00::/7) are blocked. The check runs on the literal host and after DNS resolution (against rebinding), and for web_fetch/enrichment it re-checks every redirect hop (max 5). Guarded paths and their flags:
| Path | Flag |
|---|---|
| web_fetch (cached fetch provider) | fetch.allowPrivateNetworks |
| search enrichment (page fetches for snippets) | search.allowPrivateNetworks |
| web_platform_search fetches (incl. RSS feed URLs) | platforms.allowPrivateNetworks |
| browser_navigate (Playwright) | allowPrivateNetworks in the dsh-web-browser config |
Set the relevant flag to true only in a trusted, network-isolated environment.
Browser approval (fail-closed): browser_open/browser_navigate require approval per the dsh-web-browser approval setting (never | navigate | all — navigate gates navigation and script evaluation, all adds clicks and typing). If the approval service is unavailable or the call has no agent to route it through, the action is denied, not silently allowed.
Cache eviction (LRU by usage): the store keeps at most fetch.cacheMaxPages page records (default 500) and search.cacheMaxSearches search records (default 1000), evicting the least-recently-accessed beyond the cap after each write. This keeps web.db bounded over time.
inline: true to browser_screenshot to get base64 in the model context.dsh-web-automation:
search:
engines: [ddg, bing] # keyless only
enrich: true
dsh-web-automation:
search:
engines: [ddg, bing, exa, deepseek, jina, searxng]
searxng:
endpoint: http://localhost:8080 # your SearXNG instance
embedding:
endpoint: http://localhost:11434 # Ollama (or any /embeddings server)
model: nomic-embed-text
dsh-web-automation:
search:
engines: [bing] # Bing supports the freshness filter
freshness: 24h # 24h | week | month | year
After installing and applying the overlay, verify the stack end to end (in a DSH session):
web_search "hello world" — returns sources (DDG/Bing keyless).web_fetch https://example.com twice — the second call is a cache hit (no network; check web_search_stats).web_platform_search { platform: "github", query: "schemastery" } — returns GitHub sources.web_history — shows the searches/fetches above.web_fetch http://127.0.0.1/ — fails with WEB_SSRF_BLOCKED (the SSRF guard).dsh-web-browser) browser_open → browser_navigate https://example.com → browser_screenshot → browser_close — the screenshot file appears in the temp dir.For working on the plugin itself (build, test, typecheck, local install):
git clone https://github.com/stelmakhdigital/dsh-web-automation.git
cd dsh-web-automation
npm install --legacy-peer-deps # @deepseek-ai/* peers are provided by the host DSH at runtime
npm run build && npm run build --prefix browser # bundle src/ → lib/ (both packages)
npm test # vitest (106 tests)
npm run typecheck # tsc against the local @deepseek-ai/* stubs
DSH_HOST=/path/to/deepseek-harness npm run typecheck:host # strict check against the real host types
lib/ and browser/lib/ are committed and CI enforces they stay in sync with src/, so git/tarball installs work without esbuild. To try a local copy in a profile:
npm pack # → dsh-web-automation-0.3.0.tgz
dsh plugin --profile web add ./dsh-web-automation-0.3.0.tgz
| Symptom | Cause | Fix |
|---|---|---|
ERESOLVE peer conflict on install |
Peer deps absent outside DSH deployment | npm install --legacy-peer-deps |
Cannot find module '@deepseek-ai/...' |
Plugin installed without DSH's packages | Install DSH first (peers resolve to host's versions) |
does not provide an export named 'installSettingsSection' |
Plugin built against an older DSH settings API | Update the plugin (current DSH uses ctx.settings.installSection) and reinstall |
WEB_PROVIDER_AMBIGUOUS at startup |
The web seam sees two usable search providers |
Add the web seam pin row (searchProvider: multi, fetchProvider: cached-http) — see the overlay |
WEB_DUPLICATE_PROVIDER at startup |
Both the plugin and DSH's built-in web packages are loaded | Keep one — remove the built-in rows (or the plugin row); see "Relationship to DSH's built-in web packages" |
web_fetch blocked (SSRF) |
Target is loopback/private/link-local | Set fetch.allowPrivateNetworks: true (trusted env only) |
| SearXNG returns non-JSON | JSON API not enabled on the instance | Add search.formats: [html, json] to SearXNG's settings.yml |
| Embedding re-rank falls back to BM25 | Embedding endpoint unreachable | Check the endpoint URL + model name; BM25 is the fallback |
Browser: Chromium not found |
Playwright browser not installed | npx playwright install chromium |
web.db grows large |
Cache eviction caps too high | Lower fetch.cacheMaxPages / search.cacheMaxSearches |
MIT — see LICENSE.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: websearch。