- 홈
- 플러그인
- 개발 및 플러그인 도구
- dsh-safe
dsh-safe
osman3n/dsh-safe
Windows recovery CLI and bundle for a DeepSeek Harness profile that no longer boots: test-boot a copy, disable selected plugins, restore a snapshot. Ships dsh_safe_status and dsh_safe_snapshot tools.
설치
dsh plugin --profile web add github:osman3n/dsh-safeREADME
dsh-safe
An out-of-band safety net for DeepSeek Harness plugins, shipped as both a command-line tool and an installable bundle.
The problem: a plugin can stop the Harness from opening. Sometimes the process crashes and the crash dialog loops; sometimes the window hangs on "loading" forever. The built-in recovery dialog ("Disable third-party plugins, back up the profile patch and restart") fires only on a crash, and it is all-or-nothing: it removes every third-party bundle and detaches the whole patch layer. Usually you only want to switch off the one plugin you were working on.
dsh-safe runs from a terminal while the app is closed and edits the profile
files directly, so it does not depend on the broken Harness booting at all. The
bundle adds two read-mostly tools to a healthy session: inspect the profile and
boot evidence, and snapshot the profile before a change.
Install
As a plugin, which adds the two in-harness tools:
dsh plugin --profile desktop add github:osman3n/dsh-safe
Restart DeepSeek Harness. The plugin manager lists dsh-safe, and the model can
call dsh_safe_status and dsh_safe_snapshot. The CLI files ship inside the same
package, so the recovery commands below keep working.
As a standalone CLI, no install step is required: clone the repo, open a terminal
in tools/dsh-safe, and run dsh-safe.cmd (or node dsh-safe.mjs). The only
dependency is Node.js 22+. The recovery CLI targets Windows (it uses tasklist,
cmd.exe and the desktop executable); the in-harness tools are plain Node and
work wherever the Harness runs.
The plugin half is a convenience, not the recovery path: when the Harness cannot boot, nothing inside it can run. Recovery always goes through the CLI or the recovery page, from outside the app.
In-harness tools
| Tool | What it does |
|---|---|
dsh_safe_status | Active profile, selected bundles (and which are local links), recent snapshots, last known-good snapshot, last test boot, last recovery, newest startup-failure log with the plugins that failed or waited for services |
dsh_safe_snapshot | Copies package.json, cordis.yml and cordis.patch.yml into $DSH_HOME/safety/snapshots/<id>/; optional label and note |
Both tools call the same functions as the CLI, so a snapshot taken by the model is
the same snapshot dsh-safe snapshots lists and dsh-safe restore accepts.
Neither tool changes the plugin selection: disabling and restoring are done from
the CLI while the app is closed.
Snapshots on backup
When the git-autobackup bundle (a local plugin in this setup) runs a backup
the user approved (its Back up now prompt or the header button), this bundle
saves a snapshot labelled auto-backup before the build step. The backup
build copies $DSH_HOME/safety/snapshots into its tree, so that snapshot is part
of the same pushed backup instead of the next one. Nothing happens if dsh-safe or
git-autobackup is disabled; the two plugins talk over one Cordis event
(git-autobackup/before-run), with no import between them.
Quick start
cd path\to\dsh-safe
dsh-safe.cmd status :: where the profile is, what was installed recently
dsh-safe.cmd plugins :: numbered bundle list + installed-but-not-selected
dsh-safe.cmd try :: test-boot a copy of the profile in a sandbox (no changes)
dsh-safe-ui.cmd :: recovery page in the browser (also works when the app is down)
Before you change anything, or right before you try a risky plugin:
dsh-safe.cmd snapshot -l before-thing-x
The commands
| Command | What it does |
|---|---|
status | Shows profile path, whether the app is running, bundle/local counts, recent plugin operations, crash evidence |
snapshot [-l label] | Copies package.json + cordis.yml + cordis.patch.yml into a timestamped snapshot |
snapshots / list | Lists snapshots (--json for scripting) |
restore [id|latest] | Puts a snapshot back; the current state is saved as a new snapshot first |
plugins / bundles | Numbered list of selected bundles with a [core/local/official/third-party/installed] tag |
disable [name...] | Removes bundles from dsh.profile.bundles; no names = interactive picker |
enable [name...] | Adds installed bundles back; no names = interactive picker |
try | Clone this profile under a temporary name, boot it in an isolated DSH_HOME, wait for the web server, probe it, then delete the clone |
ui | Start a local recovery page: crash evidence, plugin checkboxes, snapshots, "test boot", "launch" |
watch | Serves the recovery page and opens it when Harness writes a new crash or startup-failure log |
watch --install | Starts that watcher hidden at every Windows sign-in (--uninstall removes it) |
recover | Reads the newest startup log and explains which plugin failed and why |
recover --pick --apply | Shows suspect plugins, lets you pick which ones to disable, applies it |
recover --apply | Disables the newest safe suspect |
recover --safe-mode --apply | Keeps only dsh-base + dsh-web-app (profile still intact) |
open | Runs try; if the boot fails it auto-disables the mapped culprit, retries, and only then launches the app |
disable/enable accept names or entry ids (include:workflow-kit), and
--dry-run prints the plan. Core bundles (@deepseek-ai/dsh-base,
@deepseek-ai/dsh-web-app) are refused unless you pass --force — disabling them
takes the whole app down.
Recovery recipes
1. The app crashes on launch (dialog says "Host process exited"). Do not click the built-in "Disable third-party plugins" button unless you want to lose every third-party bundle. Instead:
dsh-safe.cmd recover --pick --apply
dsh-safe.cmd open
2. The app opens and hangs on "loading" forever.
The built-in dialog never appears for a hang. Run dsh-safe.cmd recover first: the
startup log usually names the plugin that threw during apply(). Then
dsh-safe.cmd recover --pick --apply.
3. You installed a plugin and now nothing works.
status shows the newest install/* operation, plugins shows the bundle list.
Disable the last thing you installed:
dsh-safe.cmd disable @scope/package-name
4. You want to test a profile change before letting it near the real app.
dsh-safe.cmd try — same bundles, same node_modules (via a junction), but a
throwaway profile name and DSH_HOME. The real profile is only read.
5. You disabled the wrong thing.
dsh-safe.cmd snapshots, then dsh-safe.cmd restore <id>. restore with no id
restores the newest snapshot; either way the current state is backed up first, so
restore is itself undoable.
The recovery UI
dsh-safe-ui.cmd (or dsh-safe.cmd ui) starts a tiny web server on 127.0.0.1
and opens it in your browser. It runs as a normal Node process, so it works while
the Harness is crashed, hung, or closed.
The page shows:
- Last startup evidence — the error line, the failed plugins with their first exception, which plugins waited for services, and the suspect list. Each failed plugin that is a selected bundle gets a one-click Disable this button.
- Plugins — every selected bundle with a checkbox, a filter box and a
kindbadge (core/local/official/third-party). Core bundles are locked. There is also an Enable button for installed-but-not-selected bundles. The list is sorted newest install or update first, using the app's plugin-operation log plus thenode_modulestimestamps, so the plugin you just added sits near the top. - Snapshots — the newest 25 snapshots, each with Restore.
- Recent plugin operations — what was installed/enabled and when, so you can spot the change that broke the boot.
- Test boot — runs the same isolated preflight as
try; the result and the failed-plugin list appear in the output panel. - Launch Harness — starts the app (only when it is not already running).
Mutating buttons are blocked while DeepSeek Harness is running, because the app
owns package.json at boot. The page says so in a banner.
The server binds to 127.0.0.1 only and every URL carries a random token, so
another local page cannot drive it. Stop this UI (or Ctrl+C in the terminal)
ends the server.
One-click safe reopen: double-click the DeepSeek Harness (Safe) desktop
shortcut (or run dsh-safe-open.cmd). It opens the recovery page immediately,
even while Harness is running, so you can inspect the profile and choose plugins
before touching anything. Use the page's Test boot button for a sandbox
preflight, fix what is broken, then click Launch Harness; the page shuts
itself down. dsh-safe.cmd open (without --ui) keeps the older
preflight-then-launch behaviour.
Automatically on crash
dsh-safe watch is a small background guard. It serves the recovery page and
watches two places for new failure evidence:
%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*.log— written by the desktop app when a boot or renderer fails;%USERPROFILE%\.dsh\logs\startup-*.log— written when the host startup fails.
When a new failure appears it appends the error line to
~/.dsh/safety/watch.log and opens the recovery page in your browser. If the
app is still showing its own crash dialog, the page reports "Harness is
running"; it unlocks by itself once you quit the app, because it keeps polling
while the app is up.
Install it once so it starts hidden at every sign-in (Windows):
dsh-safe watch --install
That writes dsh-safe-watch.vbs into your Startup folder:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\. Remove it with
dsh-safe watch --uninstall, or delete that file. The watcher remembers the
full path of dsh-safe.mjs, so re-run --install if you move this folder.
How try decides the boot is "healthy"
It starts the clone with dsh --profile <trial> --no-open --port 0, waits for the
dsh web: http://127.0.0.1:... line, then probes the URL until it answers. Only
then does it declare success after a short grace period (--grace 3). A crash
during boot shows up as a lower exit code or a Failed plugins block in the trial
startup log. Successful trials delete themselves; failed ones are kept under
%USERPROFILE%\.dsh\safety\trials\ for inspection (add --keep to keep a
successful one).
Safety model
- Close the Harness before
disable,enable,restore, orrecover --apply. The app reads these files at boot and the plugin manager can also write them; editing while it runs invites a last-writer-wins surprise on the next launch. (status,plugins,snapshots,recoverwithout--apply, andtryare read-only and safe while the app runs.) - The real profile is never modified by
try/open's preflight.disable,enable,restore, andrecover --applyall take a snapshot first. - Edits are atomic (write to a temp file, then rename).
openrefuses to launch a second instance while the app is running.- The desktop profile is owned by the Electron app:
dsh --profile desktop --dump-configfails with "managed exclusively by the Electron application". That is whytryclones it under a different profile name instead of booting it directly. - A plugin that throws synchronously in
apply()is contained by the loader (the app still starts with a warning). What actually kills the process is an unhandled rejection/uncaught exception — that is the crash this tool is designed to recover from, because an in-process plugin cannot catch it.
Files it writes
| Path | Contents |
|---|---|
%USERPROFILE%\.dsh\safety\snapshots\<id>\ | package.json, cordis.yml, cordis.patch.yml, meta.json |
%USERPROFILE%\.dsh\safety\trials\<id>\ | disposable profile clone + isolated home |
%USERPROFILE%\.dsh\safety\incidents\ | one JSON per failed preflight (try/open) |
%USERPROFILE%\.dsh\safety\state.json | snapshot counter, last trial |
Environment overrides: DSH_HOME, DSH_PROFILE (default desktop),
DSH_SAFE_DSH (path to a different dsh launcher), DSH_DESKTOP_EXE (path to
DeepSeek Harness.exe), DSH_SAFE_FORCE_TTY=1 (force the picker when input is
piped, useful for tests), DSH_SAFE_APP_RUNNING=0|1 (override the "is the app
running" check the UI uses to block mutations; for tests).
Wrappers
dsh-safe.cmd— the CLI.dsh-safe-ui.cmd—dsh-safe ui, opens the recovery page.dsh-safe-open.cmd—dsh-safe open --ui: opens the recovery page directly (the desktop shortcut points here).dsh-safe.cmd openstill test-boots the profile and launches Harness when it is healthy.
All work from any directory; they resolve dsh-safe.mjs relative to themselves.
If you want to type dsh-safe from anywhere, add the folder that contains
dsh-safe.cmd to your PATH.