DSH Plugin Manager

August 28, 2026 · View on GitHub

English | 简体中文

A local companion GUI for DeepSeek Harness plugin development — install, update, build, inspect, and run plugin profiles without memorizing CLI verbs.

Electron + React + TypeScript. Runs as a desktop app or in your browser via a built-in web mode.

Features

  • Profile management — list $DSH_HOME/profiles, switch DSH_HOME (isolated test homes supported), one-click launch on any free port with multi-instance tabs.
  • Plugin center — one merged table of installed dependencies × locally scanned packages: real installed version (not just the declared range), local build status, remote registry latest (cached 5 min, red highlight when behind), per-row update / uninstall / build / install / publish actions on a serial operation queue.
  • Source scan — scan any roots for plugin workspaces (two levels deep, dsh manifest or cordis.patch.yml detection), with four built-in manifest-discipline checks (missing dsh.bundle declaration, missing patch file, missing client bundle, unbuildable github: installs).
  • Build & publish — one-click build (install + run build, pnpm/npm auto-selected per lockfile) and publish to a private Gitea registry or public npmjs with scope-hijack-safe inline registry overrides.
  • Doctor — multi-category profile health audit: manifest/BOM, entry points, patch validity, build/pack/install smoke, shadow copies & attribution, peer ranges (OR-range support) with preflight, native bundles, large files, duplicate entries, bundles reconcile, supply-chain security, session tool-call pairing.
  • Run profiles — launch multiple DSH web profiles concurrently, each on its own port/tab; live log streaming with fatal-signature capture (loader tree failures surfaced as startup failures, not connection refused).
  • Multi-node ops — remote machines running the GUI web host appear in one desktop app (health checks, remote start/stop, embedded per-node tabs); or skip the aggregator and open any node directly in a browser at http://host:port/?token=… — identical GUI, identical gates.
  • A2A messaging — send messages to A2A network nodes (hosts running dsh-open-a2a-net), attach launch logs, one-click send plugin health reports from any table row.
  • Serial op queue — every mutating action (install/update/remove/build/publish) flows through one queue: buttons queue instead of freezing the UI, same-label dedupe, cross-label ordering preserved.

Usage

Desktop (Electron)

npm install
npm run dev        # build + open window

Browser (web mode)

npm run build
npm run web -- --port 3090
# stdout prints a one-time URL:
#   open in browser: http://127.0.0.1:3090/?token=<hex>

Web mode binds 127.0.0.1 only, requires a per-launch Bearer token, and serves the same handler registry as the Electron main process (single source of truth). Dangerous channels (port kill, publish, force-start, close-running) stay gated behind confirmation UIs.

Tests

npm test        # unit + IPC/web-channel suites (node:test)

Headless smoke

Runtime mount smoke (both run forms) — catches black-window class regressions that type-checks and unit tests cannot see:

npm run smoke:runtime
# Form A: web host boots, health/token/bundle references verified
# Form B: the real Electron binary mounts the app (CDP asserts the
#         React root, body content, and key cards); a mount crash
#         fails with "black window regression"
npm run smoke                       # install → check → uninstall round-trip
npm run smoke -- web_test file:/path/to/your-plugin

Requirements

  • Node.js ≥ 22
  • A DeepSeek Harness checkout (all dsh CLI calls run with the harness checkout as cwd)
  • pnpm ≥ 10 (profiles) — the GUI pins per-profile store dirs automatically

Effective boundaries

  • Host-side plugin code is parsed at DSH startup: restart the profile after install/uninstall for changes to take effect.
  • file:/link: installs are snapshot copies; re-install to refresh after source changes.

Known limitations

  • Windows-specific port-kill semantics use process-tree termination; Linux/macOS support is being hardened.
  • Private-registry plugins (@your-scope/*) resolve against the registry URL you configure in the GUI (persisted per user data dir); the code ships neutral placeholders only.