Bundle
dsh-plugin-hot-toggle
Hot-toggle any installed plugin in DeepSeek Harness from the Web settings — instant apply, no restart, persisted. 在 DSH 设置页一键热启用/停用任意插件,立即生效、无需重启、状态持久化。
- Source
- 5102a
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# dsh-plugin-hot-toggle
Hot-toggle any installed plugin from the DeepSeek Harness Web settings page (Plugins → 启停管理): stop or start a Cordis Loader entry in-process with instant effect and no DSH restart — the state persists across restarts.
[中文文档](./README.zh.md) | English
<p align="center">
<a href="https://github.com/topics/dsh-plugin"><img alt="dsh-plugin topic" src="https://img.shields.io/badge/topic-dsh--plugin-1f6feb?style=flat-square&logo=github"></a>
<a href="https://www.npmjs.com/package/dsh-plugin-hot-toggle"><img alt="npm" src="https://img.shields.io/npm/v/dsh-plugin-hot-toggle?style=flat-square"></a>
<a href="https://github.com/5102a/dsh-plugin-hot-toggle/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/5102a/dsh-plugin-hot-toggle/ci.yml?style=flat-square"></a>
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square"></a>
</p>
## Features
The 启停管理 (Plugin Toggle) tab in the Web settings:

- **Hot toggle**: calls the official Cordis Loader `Entry.update({ disabled })` — disabling disposes the plugin fiber, enabling re-imports and starts it, all in-process with instant effect. No DSH restart needed.
- **Persistent**: the toggle state is written to the profile patch layer (`$DSH_HOME/profiles/<profile>/cordis.patch.yml`) and hot-applied by the official HMR watcher on save — **survives restarts**.
- **Safety guard**: system-core entries (`include`, `cordis:*`, loader/hmr/timer, the dynamic-plugin host) cannot be toggled and are marked 「系统核心」.
- **Multi-dimensional filters** (combinable):
- State: all / enabled / disabled
- Source: all / official / core / community (community = a matching directory under `$DSH_HOME/plugins`)
- Type: builtin framework / client UI / LLM / tools / session & storage / sandbox & exec / agent & planning / services & integration / other
- Search + live count (filtered / total)
- **Sorting**: recent-first (community plugins ahead) or by name.
- **i18n**: the UI follows the DSH language preference (中文/English) via the official locale service (namespace `pluginHotToggle`, 39 balanced keys).
## Installation
### From a local checkout (development)
```sh
dsh plugin --profile web add ./dsh-plugin-hot-toggle
```
### From npm (after publish)
```sh
dsh plugin --profile web add dsh-plugin-hot-toggle
```
### From git (after publish)
```sh
dsh plugin --profile web add github:5102a/dsh-plugin-hot-toggle
```
After installation, start DSH (or let HMR apply it) and open **Settings → Plugins → 启停管理**.
## Platform support
The plugin runs on **Windows, macOS, and Linux** — the three platforms DeepSeek Harness itself supports.
| Concern | Guarantee |
| --- | --- |
| Host half | All filesystem paths go through `node:path` (`join`/`dirname`) and `fileURLToPath`; no hardcoded separators |
| Persistence | Patch-layer writes use `readFileSync`/`writeFileSync(…, 'utf8')` with paths derived from the Loader include |
| HTTP API | `/plugin-hot-toggle/api/*` is a URL route — platform-independent |
| Client half | Pure browser `fetch` + React; no `process.platform`/`navigator.platform` branches |
| Build | `scripts/build.mjs` resolves paths from `import.meta.url` via `fileURLToPath` + `join` |
| Dev tooling | Chrome/Edge discovery honors `CHROME_PATH`/`CHROME_BIN` or a cross-platform candidate list; `DSH_URL` overrides the target origin |
| CI | GitHub Actions matrix: **ubuntu + windows + macos** × Node 18/20/22 |
## How it works
```
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ Web (client half) │ fetch │ Node (host half) │
│ settings.plugins.tab │ ──────► │ /plugin-hot-toggle/api/* │
│ 「启停管理」tab │ JSON │ ┌──────────────────────────┐ │
│ - list + filters + sort │ ◄────── │ │ ctx.loader.entries() │ │
│ - toggle buttons │ │ │ entry.update({disabled}) │ │
└─────────────────────────────┘ │ │ → fiber dispose / start │ │
│ └──────────────────────────┘ │
│ ┌──────────────────────────┐ │
│ │ persistence: write │ │
│ │ cordis.patch.yml → HMR │ │
│ └──────────────────────────┘ │
└──────────────────────────────┘
```
- **Node half** uses only public seams (`loader`, `webServer`, `dshHomePath`) — zero DSH core changes. `webServer` is injected dynamically, so TUI/headless surfaces never pend.
- **Web half** talks to the Node half over a same-origin HTTP API; mutating requests carry a same-origin check (missing Origin / cross-origin → 403).
- **Client bundle** is generated from `src/client.js` by a zero-dependency build script (`node scripts/build.mjs`) and committed to the repo, so git/npm installs need no build step.
## Project layout
```
dsh-plugin-hot-toggle/
├── package.json # dsh.bundle declaration + peerDependencies + exports
├── index.d.ts # Host half type declarations
├── index.js # Node half: loader list/toggle + webServer HTTP API
├── cordis.patch.yml # bundle layer: inserts the plugin row
├── src/client.js # Web half source (React)
├── lib/client.js # Web half build artifact (generated, do not edit)
├── scripts/build.mjs # zero-dep client bundle build
├── scripts/screenshot.mjs # headless-Chrome UI screenshot (CDP, zero deps)
├── scripts/verify-render.mjs # verify the 启停管理 tab rendered
├── scripts/debug-dom.mjs # DOM/slot debugging helper
├── tests/patch.test.js # unit tests (node:test)
├── docs/DSH-PLUGIN-DEVELOPMENT-SOP.md # end-to-end DSH plugin development SOP (bilingual)
├── .dsh/skills/dsh-plugin-hot-toggle/SKILL.md # project skill: dev/build/publish SOP
└── .github/workflows/ci.yml # CI: build + artifact freshness + tests
```
## API
| Method | Path | Body | Returns |
| --- | --- | --- | --- |
| GET | `/plugin-hot-toggle/api/list` | — | `{ entries: PluginEntry[] }` |
| POST | `/plugin-hot-toggle/api/setEnabled` | `{ entryId, enabled }` | `SetEnabledResponse` |
See [index.d.ts](./index.d.ts) for `PluginEntry` / `SetEnabledResponse`.
## Development
```sh
npm run build # generate lib/client.js from src/client.js
npm test # node:test unit tests
npm run check # build + test in one step
```
## Publishing
**Releases are fully automated via OIDC trusted publishing** — pushing a version tag triggers the Release workflow, which builds, tests, packs, publishes to npm (with provenance), and creates a GitHub Release. No npm token or manual 2FA is needed.
```sh
npm version 0.1.5 --no-git-tag-version # bump version
git commit -am "chore: release v0.1.5"
git tag v0.1.5
git push origin master --tags # CI + Release workflow + npm publish (OIDC)
```
> The name `dsh-plugin-hot-toggle` was verified available with `npm view` before release.
> **Note on peer-dependency warnings**: the package declares `@deepseek-ai/cordis` and `react` as peer dependencies (the same pattern as the official `@deepseek-ai/*` plugins). DSH resolves them from its own installation via the profile's flat fallback, so an "unmet peer" warning during `dsh plugin add` is expected and harmless.
## License
MIT — see [LICENSE](./LICENSE).
Install
dsh plugin --profile web add github:5102a/dsh-plugin-hot-toggle
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 dsh-plugin-hot-toggle 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.