dsh-plugin-manager demo

๐Ÿš€ The plugin butler for DeepSeek Harness
Install ยท Uninstall ยท Start ยท Stop โ€” manage your whole DSH plugin ecosystem in one place

Features ยท Compatibility ยท Installation ยท Configuration ยท Development ยท ไธญๆ–‡

--- # dsh-plugin-manager Manage DeepSeek Harness plugins **as easily as ordering takeout**. No more hunting through GitHub, typing CLI commands, or hand-editing configs. Open ่ฎพ็ฝฎ โ†’ ๆ’ไปถ โ†’ **Plugin Manager** and every built-in / third-party plugin is right there โ€” install, uninstall, start, stop, and GitHub deep-links all on one page. ![Plugin list](assets/plugin-list.png) ![Install dialog](assets/install-dialog.png) ## โœจ Features ### ๐Ÿงญ Full visibility at a glance - **Built-in vs third-party**: enumerates 150+ built-in plugins from the live Loader (consistent with DSH's own list); packages in profile `dependencies` are auto-flagged as manageable third-party - **Agent-preset awareness** (dsh โ‰ฅ 0.1.2): plugins mounted by an agent preset composition carry a `Preset โ€ฆ` tag, so a disabled root Loader row no longer makes them look stopped - **State at a glance**: running โ— / stopped / failed-to-load, source badges (npm / GitHub / local), version, and GitHub entry right on each card ### ๐Ÿงฉ Smarter than you'd expect - **Multiple install sources**: npm names, `github:owner/repo#main`, local paths, tarball URLs โ€” all supported - **Bare-name smart resolve**: type a bare name like `dsh-paste-input` and it auto-detects whether it's an npm package or a GitHub repo โ€” GitHub hits show candidate repos for one-click install - **GitHub marketplace search**: search `topic:dsh-plugin` by keyword to discover hidden gems in the ecosystem ### ๐ŸŽ› Precision under the hood - **Stop / start**: resolves a third-party bundle's loader entry ids and writes / removes the `disabled` flag in the profile `cordis.patch.yml` โ€” reconcile-proof - **Uninstall**: clean and thorough, even sweeping leftover stop entries - **GitHub deep-link**: click the version number (โ†—) on a card to jump straight to the repo ### โšก Automation to the last mile - **Auto-restart**: optionally auto-restart dsh web after install/uninstall (off by default); a 10s countdown auto-refreshes, then a top-right toast offers one-click jump back - **GitHub token auto-discovery**: probes env / gh CLI / shell rc / git config for `GH_TOKEN`, and kindly explains the rate limit when unset - **Filters & search**: status tabs + source filter + fuzzy search, collapsible built-in / third-party groups ### ๐ŸŒ Polished for everyone - Full ไธญๆ–‡ / English UI, following the DSH language preference > โš ๏ธ Install / uninstall / stop / start take effect **after a restart** (stop/start may apply immediately when HMR is active). ## ๐Ÿ”– Version Compatibility Check your dsh version first (`dsh --version`), then pick the plugin version from this table: | Plugin version | Targets dsh | 0.1.1-rc.2 | 0.1.2-alpha.3 | Notes | | --- | --- | :---: | :---: | --- | | **0.7.x** (current) | **0.1.1-rc.2 โ€ฆ 0.1.2-alpha.x** | โœ… verified | โœ… verified | One codebase for both generations: it handles the sync *and* async signatures of `pluginInventory.list()`, and tags agent-preset composition rows on dsh 0.1.2 | | 0.6.x | 0.1.1-rc.2 โ€ฆ 0.1.2-alpha.x | โœ… | โš ๏ธ degraded | On dsh โ‰ฅ 0.1.2 `list()` became async; reading it synchronously makes the inventory **silently collapse** to the profile's `bundles`/`dependencies` rows, all shown as not mounted โ€” with no error at all | | โ‰ค 0.5.x | โ‰ค 0.1.1-rc.x | โœ… | โŒ broken | The client `inject` list still references `dsh-client-runtime`, removed in dsh 0.1.2, so the client never loads (the settings tab never appears) | > Bottom line: **use 0.7.x on any dsh generation** โ€” it stays fully backward compatible with 0.1.1, so there is no reason to stay on an older release.
v0.7.0 measured results | dsh version | `/pmgr/list` inventory | The plugin itself | | --- | --- | --- | | 0.1.1-rc.2 | 142 rows (141 mounted) | `entryIds:["pmgr"]`, `fiberPhase:"active"` | | 0.1.2-alpha.3 | 160 rows (159 mounted, 32 of them mounted by agent-preset compositions) | same | (For comparison: 0.6.x on dsh 0.1.2 enumerates only 4 rows, all `mounted:false`.)
## ๐Ÿ“ฆ Installation ```bash # 0. Know your dsh version and the target profile dsh --version # 1. From GitHub (recommended; main is the latest) dsh plugin --profile web add github:webkong/dsh-plugin-manager#main # Or pin a release tag dsh plugin --profile web add github:webkong/dsh-plugin-manager#v0.7.0 # Or from a local path (development / offline) dsh plugin --profile web add /path/to/dsh-plugin-manager # 2. Restart dsh web ``` Then open ่ฎพ็ฝฎ โ†’ ๆ’ไปถ โ†’ **Plugin Manager**. ### ๐Ÿ’ก Installation tips - **Match the profile**: `--profile ` must be the profile you actually boot (`dsh web` boots `web`). To manage a *different* profile, set `config.profile` on the load row (see Configuration). - **Restart is required**: install / uninstall / stop / start all rewrite profile configuration and take effect **after restarting dsh web**. Enable "auto-restart after install" in the UI to let the plugin do it for you (10s countdown, page auto-refresh). - **No build step**: `lib/` build artifacts are committed, so `add` is enough โ€” you do not need to run `pnpm build` in the plugin directory. - **Upgrading**: re-run the same `add` command (GitHub sources are re-fetched), then restart. For a local `link:` install, run `pnpm build` in the plugin directory and restart. - **Uninstalling**: click Uninstall in the UI, or `dsh plugin --profile web remove @webkong/dsh-plugin-manager`, then restart. - **Never load it twice**: after `add`, the plugin already sits in `dsh.profile.bundles` โ€” do **not** also `insert` an `id: pmgr` row in the profile's `cordis.patch.yml`. dsh โ‰ฅ 0.1.2 refuses to boot (`duplicate loader entry id: pmgr`). Use the override form below instead. - **GitHub rate limits**: set `GH_TOKEN` (or run `gh auth login`) before using "Search GitHub", otherwise you get roughly 10 requests/min. ## โš™๏ธ Configuration The `web` profile is managed by default. After `dsh plugin add` the plugin already sits in `dsh.profile.bundles` (its bundle patch inserts the `pmgr` row), so configure it by **overriding that row** in the profile's `cordis.patch.yml` โ€” a patch entry with `id` and without `insert` is an override: ```yaml - id: pmgr config: profile: web # profile to manage (default: web) ``` > โš ๏ธ dsh โ‰ฅ 0.1.2 fails to boot on duplicate Loader row ids (`duplicate loader entry id: pmgr`). > If a profile both loads the bundle and re-inserts `- id: pmgr`, convert the latter into the override form above. The auto-restart toggle is also switchable in the UI anytime (persisted to `~/.dsh/dsh-plugin-manager.json`). ## ๐Ÿ”ง Development ```bash pnpm install pnpm build # esbuild: src/ โ†’ lib/ (Host ESM + Client __ModuleLoader__ bundle) pnpm typecheck # strict type-checking under dual tsconfig (node / DOM+React) pnpm test # node --test pure-function unit tests (9 cases) pnpm check # typecheck + artifact syntax check ``` ### Structure (TypeScript, modular) **v0.5.0 migrated the whole codebase to TypeScript modules** (following the official ui-plugin layout); build artifacts live in `lib/`: ``` dsh-plugin-manager/ โ”œโ”€โ”€ package.json # dsh.bundle / dsh.client declarations, scripts โ”œโ”€โ”€ cordis.patch.yml # bundle patch: mounts the pmgr row โ”œโ”€โ”€ build.mjs # esbuild 3-stage build (Host / Client / pure-function submodules) โ”œโ”€โ”€ tsconfig.json # Host type-check (node env) โ”œโ”€โ”€ tsconfig.client.json # Client type-check (DOM + React env) โ”œโ”€โ”€ lib/ # build artifacts (committed; zero-config after install) โ”‚ โ”œโ”€โ”€ index.js # Host single-file ESM bundle โ”‚ โ”œโ”€โ”€ client.js # Client __ModuleLoader__ bundle โ”‚ โ”œโ”€โ”€ entryIds.js # pure-function submodules (imported by unit tests) โ”‚ โ”œโ”€โ”€ github.js / patch.js / spec.js โ””โ”€โ”€ src/ โ”œโ”€โ”€ host/ # Host source (TypeScript, node env) โ”‚ โ”œโ”€โ”€ index.ts # entry: name/inject/apply + webServer route registration โ”‚ โ”œโ”€โ”€ handlers.ts # HTTP dispatch (list/install/uninstall/stop/start/search/resolve/settings/restart) โ”‚ โ”œโ”€โ”€ manager.ts # business layer: inventory, CRUD, search/resolve (DI of fs tools) โ”‚ โ”œโ”€โ”€ fsutil.ts # node:fs + child_process + settings persistence โ”‚ โ”œโ”€โ”€ resolve.ts # dual-anchor package resolution / metadata reading โ”‚ โ”œโ”€โ”€ entryIds.ts # bundle patch loader-entry id discovery โ”‚ โ”œโ”€โ”€ github.ts # GitHub URL extraction + GH_TOKEN reading โ”‚ โ”œโ”€โ”€ spec.ts # install-spec validation โ”‚ โ”œโ”€โ”€ patch.ts # cordis.patch.yml stop/start text ops โ”‚ โ””โ”€โ”€ http.ts # JSON responses / loopback guard โ””โ”€โ”€ client/ # Client source (TypeScript, DOM + React env) โ”œโ”€โ”€ index.ts # apply entry + settings tab / shell.overlay registration โ”œโ”€โ”€ api.ts # fetch wrapper + pmgr method table โ”œโ”€โ”€ components.tsx # status tabs / toolbar / install dialog / plugin card โ”œโ”€โ”€ i18n.ts # zh / en dictionary (key-typed) โ”œโ”€โ”€ spec.ts # client-side spec validation + bare-name detection โ”œโ”€โ”€ styles.ts # CSS injection โ”œโ”€โ”€ toast.tsx # restart toast + one-click jump โ”œโ”€โ”€ types.ts # contract types (wire shapes / dialog state) โ””โ”€โ”€ ui.tsx # page orchestration / grouping / action dialogs โ”œโ”€โ”€ assets/ # demo GIF + screenshots โ””โ”€โ”€ test/pure.test.mjs # unit tests (node --test) ``` ### Wire contract The Host exposes an HTTP API on the `webServer` prefix route `/pmgr/*` (loopback-only); the client calls it via browser `fetch`: | Route | Description | | --- | --- | | `GET /pmgr/list` | plugin inventory + settings | | `POST /pmgr/install` | install (`{spec}`) | | `POST /pmgr/uninstall` | uninstall (`{name}`) | | `POST /pmgr/stop` / `POST /pmgr/start` | stop / start (`{name}`) | | `POST /pmgr/search` | GitHub plugin search (`{q}`) | | `POST /pmgr/resolve` | resolve a bare name (npm check / GitHub candidates) | | `POST /pmgr/settings` | update settings (`{autoRestart}`) | | `POST /pmgr/restart` | trigger an auto-restart | ## ๐Ÿ“„ License [MIT](LICENSE)