dsh-enhanced
Everyday upgrades for DeepSeek Harness in one plugin: vendored multi-engine free web search (with self-hosted SearXNG support), persistent skills and MCP server management, auto-compact tuning, instance controls, and Matrix/Cyberpunk themes
安装
此插件尚未提供可验证的 bundle,或兼容性检查未通过。请先阅读仓库说明。 阅读完整 README ↗
说明文档
阅读完整 README ↗dsh-enhanced
One plugin that bundles the everyday upgrades DeepSeek Harness (DSH) lacks out of the box:
- Free web search — a vendored multi-engine search provider (DuckDuckGo ×2, Bing, AnySearch, SearXNG, Mojeek-ready fallback chain + optional paid engines), configured from a Settings tab, with self-hosted-SearXNG support and live health checks.
- Skills manager — install/edit/remove agent
SKILL.mdskills persistently. Installs accept a single skill or a folder OF skills (disk path or browser folder-picker alike), keep bundledscripts/executable (browsers drop permission bits; shebang files are restored to 0755), and report per-skill results so one bad bundle never blocks the rest. - MCP server manager — manage
@deepseek-ai/dsh-mcp-clientrows across profiles. - Auto-compact tuner — clamp the context-compaction trigger below the harness default.
- Instance controls — clean shutdown/restart of the GUI process via icon buttons beside Settings in the sidebar foot.
- Themes — original ENHANCED theme (phosphor-green terminal look, digital-rain boot intro) and a Cyberpunk 2077 theme ported from the community theme.
Single host composition plugin (cordis.patch.yml row), single model tool surface (manage_skills_mcps), single browser card (Settings → Plugins). No telemetry, no external services beyond the search engines themselves.
Install
dsh plugin add https://github.com/HIT-HTML/dsh-ENHANCED # or clone into a profile and add a composition row
The repo ships prebuilt dist/ and client.js, so no build step is needed to install. To develop, see Development.
Plugin row (usually written for you by dsh plugin add):
- id: dsh-enhanced
name: dsh-enhanced
config:
mcpProfiles: ["default", "web"] # whose cordis.patch.yml receives managed rows
allowRestart: false # opt-in for supervisor-assisted restarts
Architecture
Two halves, standard DSH plugin shape:
┌─ HOST (Node, src/*.ts → dist/) ─────────────────────────────┐
│ index.ts composition root: registers the model tool, │
│ browser RPC channel, settings anchor, boots the │
│ vendored search engine │
│ shared.ts paths, managed-block surgery, Env/Handler types │
│ skills.ts mcp.ts compact.ts instance.ts search.ts │
│ feature modules — each owns its actions end-to-end│
│ free-search-vendor.ts vendored engine (see below) │
└──────────────┬──────────────────────────────────────────────┘
│ package-private JSON RPC (browser → host)
┌─ CLIENT (browser, client/** → client.js bundle) ────────────┐
│ core.js draft/save pipeline, sections registry │
│ main.js boot, saved-theme activation │
│ sections/* one card per feature (skills, mcp, compact, │
│ search, theme) │
│ themes/* matrix, cyberpunk2077 (+ boot intro) │
└─────────────────────────────────────────────────────────────┘
Persistence is deliberately boring: every host feature writes marker-delimited
"managed blocks" into /cordis.patch.yml (and ~/.dsh/skills/ for skills).
No database, no state file — the user's config tree is the state, readable by eye:
# >>> dsh-enhanced:mcp >>> …rows… # >> dsh-enhanced:compact >>> …row… # >> dsh-enhanced:search >>> …row… # .ts` exporting `ACTIONS` + `Handler`, two lines in `index.ts`, one section file under `client/sections/`. That's the whole integration story.
---
## Feature reference
| Module | Actions | Writes to |
|---|---|---|
| skills | `list_skills, read_skill, add_skill, edit_skill, remove_skill, install_skill, install_skill_files` | `~/.dsh/skills//SKILL.md` |
| mcp | `list_mcps, mcp_status, add_mcp, remove_mcp, enable_mcp, disable_mcp` | managed `:mcp:` block |
| compact | `compact_status, set_compact` | managed `:compact:` block |
| instance | `shutdown_instance, restart_instance` | process control only |
| search | `list_search, set_search` | managed `:search:` block |
Client-side, each feature is a *section* plugged into three registries in `core.js`:
`DRAFT_SHAPES` (form state), `DIRTY_CHECKS` (unsaved chip), `SAVE_STEPS` (replay on Save).
Sections self-fetch on expand and stage edits locally; Save replays steps in order and
a mid-batch failure keeps exactly the unapplied part staged.
Secrets (API keys) are **never echoed back**: `list_search` reports `hasKey.` booleans,
key inputs start blank meaning "unchanged", and only non-blank values are written.
---
## Search subsystem
### Provenance
The engine is **vendored** from [`dsh-free-search`](https://github.com/DDDMUC/dsh-free-search)
v0.4.12 (MIT, © DDDMUC) into `src/free-search-vendor.ts`. We vendor rather than depend because
upstream's peerDependencies aren't published to npm — fresh installs of the standalone plugin
can fail outright. Upstream fixes do **not** propagate automatically; re-port when adopting them.
Local adaptations vs upstream (all collision-safety or de-branding):
| What | Upstream | Here |
|---|---|---|
| settings namespace | `free-search` | `enhanced-free-search` |
| bridge prefix | `/api/dsh-free-search-settings` | `/api/dsh-enhanced-free-search` |
| search provider id | `ddg` | `enhanced-free` |
| settings UI section | installed its own card | removed — our Search tab owns config UX |
| self-update machinery | check-update + `pnpm add` upgrade routes | removed — a vendored copy must not reinstall upstream over itself |
| agent-visible strings | "Settings > Plugins > Free Search" | point at this plugin's Search section |
### Boot flow
profile cordis.patch.yml dsh-enhanced host boot :search: managed block ─┐ ├─► index.ts: readSearchConfig(profiles[0]) Settings→Search tab ────┘ │ ▼ ctx.inject(["web"], scope => freeSearch.apply(scope, cfg)) │ registers provider id "enhanced-free", agent tools, system-prompt section, bridge routes │ ▼ harness web_search / advanced_search route through it
Config is read once at boot — after saving in the tab, restart the profile to apply.
If the standalone `dsh-free-search` is also installed somewhere, nothing clashes:
distinct namespace/prefix/provider-id, and each registration is guarded.
### Engines and the fallback chain
Preferred engine = your Provider dropdown. On failure/empty results the chain walks on:
paid (only if keyed): exa → tavily → keenable → perplexity → deepseek-official free, always: bing → anysearch → ddg → ddg-lite → searxng
Time filtering (`advanced_search`) is honored by engines that support it and skips the rest.
Results are cached per query (LRU ~50, TTL ≤5 min, configurable).
### SearXNG instances (self-hosted path)
Public SearXNG instances rate-limit the JSON API to death; run your own:
```bash
mkdir -p ~/.searxng && cat > ~/.searxng/settings.yml /tmp/lab-port.yml
dsh --profile lab --patch /tmp/lab-port.yml
Serves the workspace client live (cache-control: no-cache), so client edits show on refresh;
host edits need a lab restart. Never point this at your main profile.
Adding an engine (search)
src/free-search-vendor.ts:const X_URL,async function searchX(query, maxResults, options, signal)returning{ sources: [{url,title?,snippet?}], truncated:false }— reusefetchHtmlWithRetry,stripTags,uniqueSources.- Add the id to
FREE_ENGINES/ALL_ENGINES, thefreeEngineschain array insideprovider.search, and acaseinrunEngineTest. client/sections/search.js: onePROVIDER_METAentry (free: trueunless it needs a key).- Smoke-test through the bridge:
curl -X POST .../raw-search -d '{"engine":"x","query":"hi"}'.
Adding a feature module
Copy the shape of src/compact.ts (smallest full example): actions const, Handler switch,
managed block via splitBlock/writeFile, guard-rails server-side. Then one handleX line +
X_ACTIONS spread in index.ts, and a section file registering into the client registries.
Release checklist
-
npm run build && npm run selfcheck - Lab bench smoke: bridge query with preferred engine + one fallback
-
dist/andclient.jsare committed artifacts here — make sure they're fresh - No machine-specific paths or secrets in tracked files (
git grep -E "/Users/|sk-[A-Za-z0-9]") - Bump
package.jsonversion
Privacy & security notes
- No telemetry. The only outbound traffic is the searches you (or your agent) run.
- API keys are stored in the profile's
cordis.patch.ymland never sent back to the browser (blank field = unchanged;hasKeybooleans only). - The test bridge accepts loopback requests only (
isLoopbackRequestguard) and redacts secrets. - Strings like
EXA123in tests are deliberate fixtures, not credentials.
License
MIT — see LICENSE. Contains code vendored from dsh-free-search (MIT, © DDDMUC) and theme work originating from the Cyberpunk 2077 community theme; both attributed in-file.