# dsh-safe-install > **One-shot safe installation for DeepSeek Harness plugins**: verify → install → restart. ![version](https://img.shields.io/badge/version-0.1.0-blue) ![license](https://img.shields.io/badge/license-MIT-green) ![dsh](https://img.shields.io/badge/DSH-0.1.0--rc.8-orange) ![tests](https://img.shields.io/badge/tests-33%20unit%20%2B%204%20integration%20passing-brightgreen) Installing DSH plugins is risky (duplicate loader crashes, tool-name collisions, version mismatches) and requires a manual restart afterwards. This plugin turns "install a plugin" into a single sentence: **safety checks pass → snapshot → install → auto-restart → plugin live**, with zero manual steps. > 中文说明见 [README.md](./README.md) · Chinese docs in [README.md](./README.md) --- ## Features - 🔍 **Four safety checks** (before install): version compatibility / patch collision / tool-name collision / supply chain - ⛔ **Blocking gate**: any `block` verdict refuses the install with reasons; `warn` only flags risk - 📸 **Automatic snapshot** before install (integrated with dsh-undo-savepoint — always reversible) - 🔄 **Automatic DSH restart** after install (detached Node spawn, waits for port release) - 📋 **Status tracking**: last check/install/restart records survive restarts - 👁 **Change detection**: polls the profile `package.json` bundles to notice external installs/removals ## Flow ``` User says "install plugin XX" │ ├─ ① Four safety checks │ ├─ Version @deepseek-ai/* peer requirement (minor,patch,rc) ≤ current DSH │ ├─ Patch cordis.patch.yml insert id vs core/reserved + installed ids │ ├─ Tools registered tool names vs installed/builtin tools │ └─ Supply chain npm published + active (<30d) + clear license │ └─ any block? → refuse, return report │ ├─ ② Snapshot (undo CLI, non-blocking on failure) ├─ ③ dsh plugin --profile web add │ └─ failure → return error ├─ ④ Verify bundle entry (read profile package.json) ├─ ⑤ Auto-restart (when restart=true) │ ├─ spawn detached helper (detached + unref) │ ├─ current process exits after a delay (flush tool result) │ └─ helper starts a fresh DSH once the port is free └─ ⑥ Return report: check + install + restart status ``` ## Tools | Tool | Purpose | Parameters | |---|---|---| | `safe_install_check` | Run only the four safety checks | `{ pkg, source? }` | | `safe_install` | Check → install when safe → (optional) auto-restart | `{ pkg, source?, restart? }` | | `safe_install_status` | Last check/install/restart state & history | `{}` | - `pkg` — npm package name, e.g. `dsh-startup-guard` - `source` — GitHub source (recommended: enables the patch & tool checks), e.g. `github:lire1131/dsh-undo-savepoint#master` ## The Four Checks | # | Check | Data source | Verdict | |---|---|---|---| | ① | **Version**: plugin's `@deepseek-ai/*` peer requirement `(minor, patch, rc)` must be ≤ current DSH (`0.1.0-rc.8`) | npm registry / GitHub raw `package.json` | `block` if exceeded | | ② | **Patch**: insert ids must not collide with core reserved ids (`storage` / `storage-json` / `storage-domain` …) or installed plugins — prevents `duplicate loader entry id` crashes | GitHub raw `cordis.patch.yml` + local scan | `block` on duplicate | | ③ | **Tools**: registered tool names must not collide with installed/builtin tools — prevents tool-name crash | GitHub raw `lib/index.js` + local scan | `block` on collision | | ④ | **Supply chain**: npm published + active (`pushed_at` < 30 days) + clear license (MIT/Apache/BSD/ISC…) | npm registry + GitHub API | `warn` otherwise | > ⚠️ Without `source`, checks ②③ degrade to `warn` (non-blocking). **Always provide the GitHub `source`** for the full check. ## Architecture ``` dsh-safe-install/ ├── package.json # plugin manifest (dsh.bundle.patch) ├── cordis.patch.yml # mount entry (insert id: safe-install, unique) ├── lib/ │ ├── index.js # entry: 3 tools + event listeners + polling │ ├── env.js # environment facts (profile path / rc detection / known tools) │ ├── net.js # network (Node https + TLS bypass + retry/backoff) │ ├── check.js # four-check aggregator (parallel) │ ├── checkVersion.js # ① version compatibility │ ├── checkPatch.js # ② patch collision │ ├── checkTools.js # ③ tool-name collision │ ├── checkSupply.js # ④ supply chain │ ├── install.js # install (snapshot + dsh plugin add + verify) │ ├── restart.js # auto-restart (detached Node spawn) │ ├── detect.js # poll profile package.json bundles │ └── state.js # state (memory + disk) └── tests/ ├── run-tests.mjs # offline unit tests (pure functions, no network) └── integration.mjs # live integration tests (real npm/GitHub data) ``` ## Quick Start ### Install ```powershell # From GitHub dsh plugin --profile web add github:goodaiaiai/dsh-safe-install#master # Or from a local checkout dsh plugin --profile web add ``` > Restart DSH after installing (or let the AI install it via `safe_install`, which restarts automatically). ### Use in conversation ``` safe_install_check { pkg: "dsh-startup-guard" } # check only safe_install { pkg: "dsh-better-sidebar", source: "github:omdsh-dev/DSH-better-sidebar" } # check + install (auto-restart by default) safe_install_status # status ``` > ⚠️ `safe_install` defaults to `restart: true` and will restart the DSH process (the current process exits ~2.5s after the response; the helper starts a fresh process 3s later). Make sure you have a recovery path before testing the full flow. ## Development & Testing ```powershell # Unit tests (offline): rc verdict / patch extraction / tool names / license node tests\run-tests.mjs # Integration tests (network): four checks against real plugins node tests\integration.mjs ``` Current status: - Unit **33/33 passing** (rc.6/rc.8/rc.9, cross-patch `0.1.1-rc.1`, cross-minor `0.2.0-rc.3`, storage-collision patch, undo_* tool collision, license verdicts) - Integration **4/4 passing** (dsh-startup-guard allow / installed-plugin duplicate-install block / dsh-better-sidebar 0.16.1 compatible / dsh-plugin-notify allow) - Security case: unknown supply-chain package → `warn`, non-blocking ## Compatibility | Item | Value | |---|---| | DSH | **0.1.0-rc.8** (higher minor/patch/rc series are rejected) | | profile | `web` (GUI http://127.0.0.1:3080) | | Node | ≥ 18 (`node:https`, `node:child_process`) | | deps | peer only: `@deepseek-ai/cordis`, `@deepseek-ai/dsh-tools` | > Other rc versions can be probed via env `DSH_VERSION` / `DSH_RC` (debugging). ## Limitations - Checks ②③ depend on a GitHub `source`; npm packages cannot be inspected before download - The known-tool list is a snapshot + startup scan of installed plugins; refresh after new installs - `dsh plugin add` may hit EPERM under a workspace-write sandbox (temp-file permissions); needs full access - Version check covers `(minor, patch, rc)`; missing core modules (e.g. `dsh-client-ui-slots`) are a runtime dependency concern (pair with dsh-startup-guard) ## Security - Plugins run with your privileges — only install what passes all four checks - Automatic snapshot before install; roll back anytime (dsh-undo-savepoint) - Built-in `NODE_TLS_REJECT_UNAUTHORIZED=0` (incomplete local CA chain); requests retry (up to 3×, exponential backoff) with a 20s timeout ## Contributing 1. Fork the repository 2. Run `node tests\run-tests.mjs` and `node tests\integration.mjs` after changes 3. Open a PR with the change and test results ## License [MIT](./LICENSE)