DobyChao/dsh-workspace-enhancement ↗★ 0
dsh-workspace-enhancement
Unified SSH remote-execution plugin for DeepSeek Harness — ProxyJump chain, SFTP filesystem, subprocess and PTY over ssh2
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:DobyChao/dsh-workspace-enhancement说明文档
阅读完整 README ↗dsh-workspace-enhancement
English | 中文
A DeepSeek Harness workspace-enhancement plugin — local and remote (SSH) workspaces managed in one place. A session can hold multiple workspaces (a main cwd plus a thin declaration list of side directories); machines, TOFU host keys and keychain passwords all live in your local ~/.dsh. Built on ssh2.
Features
| Feature | Description |
|---|---|
| Remote workspaces | ctx.subprocess + ctx.fs transparent remote providers: one SSH chain (multi-hop) runs bash / files / PTY / directory browsing with no code changes on the tools |
| Multi-workspace sessions | The「⊕ 工作区」button in the session header: attach one or more side workspaces (local dirs or remote machine dirs) as a thin declaration list (mount/unmount + label; the permission tiers were retired with ADR-0019); the model is told about them and can operate them directly |
| Add-workspace flow | Connection sidebar (saved machines, ~/.ssh/config aliases, local) + directory browser (breadcrumbs, native chooser, new folder); remote "Connect & open" creates the session straight on the server |
| Machine settings page | Machine CRUD / test / set-current / forget host key; OS-keychain passwords; TOFU host keys (accept-new default) |
| Session awareness | Remote marker + online tri-state + reconnect in the sidebar; per-session prompt injection states the remote / side-workspace context |
| Cross-server execution | sw_exec(server, command) runs a command on a named server (a registry id like c1, or the temporary id of sw_connect save:false; defaults to the session's machine) — the target OS is probed once per connection and reported (bash -c on POSIX, pwsh -Command on win32); on Windows hosts a bash tool is registered for remote-Linux workspaces |
| Remote approval gate | Optional per-machine gate (AUDIT-6 / ADR-0020, default off): every shell-shaped remote command and every remote terminal asks the platform approval service before it runs — human asks you each time, ai auto-grants a short read-only whitelist (pwd, ls, git status, …) and asks for the rest; every ask/reject pair lands in the session's audit log |
| Model tools | sw_status, sw_connect (save:false = temporary), sw_pick_workspace, sw_exec (cross-server execution) |
| Runtime localization | UI copy, the per-session remote-context prompt, sw_* tool descriptions/errors AND protocol-data validation/routing errors follow the settings-page Language option (zh/en); the UI defaults to the browser language, so a Chinese browser stays Chinese. Only bad-request: protocol-layer diagnostics (machine-readable contract) stay English |
How it works
flowchart LR
subgraph local["Your machine"]
agent["agent loop
orchestration · memory · LLM calls"] --> seam["this plugin
ctx.subprocess · ctx.fs"]
end
subgraph remote["Remote host"]
run["bash · files · PTY (terminal)"]
end
seam -- "one SSH connection (multi-hop jumps)" --> run
No DSH install on the remote: the model orchestrates locally, commands run remotely, results come back into context.
Design notes (implementation facts, not user features):
- Registry & routing:
remote-workspaces/machines.jsonis the single source of truth;ssh:///(and the localdsw-routesplaceholder tree) route every operation to the right machine;~/.ssh/configaliases are recognized. - Security: TOFU host keys (
accept-new/verify/off), per-machine OS keychain (DPAPI / security / secret-tool), credentials redacted in error messages. Remote commands run with the remote OS account's permissions — the local sandbox does not apply to them; the per-machine approval gate can be enabled (default off, ADR-0020). - Side workspaces are a thin declaration list (ADR-0019): a side root only declares "this session can operate on this directory directly" (mount/unmount + label) — there are no fs/exec permission tiers, because any absolute path on the same machine is already reachable from a remote session and per-root tiers never fenced shells. Remote side roots still participate in routing (an absolute path under a remote side root goes to that machine even when the session cwd is local); real isolation lives in the per-session
sandbox/mode(local) and the operator's trust boundary. sw_execsemantics: the command always runs on a named server —servertakes a registry id or the temporarysw_connect save:falseid and defaults to the session's machine (a local session without a server errors). The target OS is probed once per connection (uname -s→cmd /c ver→unknown) and reported in the first output line; POSIX/unknown runsbash -c, win32 runspwsh -Command. The spawn goes through the same mixed provider, so machine routing applies unchanged.run_in_backgroundmirrors the official bash tool (job id returned immediately, no timeout; requiresctx.jobsand errors honestly when absent);sandbox_permissions/escalation is intentionally unsupported (deployment policy only).- win32
bashseam: on a Windows host the plugin registers thebashtool itself (the official bash executor is not composed there, so the name is free) — a remote-Linux session runsbash -con the server, a local Windows session gets a clear error instead of silently degrading; POSIX hosts never register it (the officialbashtool owns the name). - Remote approval gate (ADR-0020, AUDIT-6): one gate on the mixed subprocess seam's remote branch —
bash -c/pwsh -Command-shaped spawns and remote terminals askctx.approvalbefore any SSH activity (machine moderemoteApproval: 'off' | 'human' | 'ai'in machines.json, default'off'so upgrades change nothing). In'ai'mode a prepend waterfall answerer auto-grants only a reviewable read-only whitelist — everything else, and any classifier failure, goes to the human answerer (fail closed:neverpolicy and missing answerers are deterministic denials). Honest boundaries: the SFTP write path is NOT gated (writing a script +bash script.shbypasses the shape gate — the structural answer is the planned remote sandbox runner, REQ-I9); the plugin's own fixed probes andsw_connect save:falsetemporary connections are not gated; local sessions are unaffected. UX-1'sremote-fullpermission preset snippet keeps its canonical home in ADR-0015 (apply it by hand incordis.patch.yml).
Install
# from npm (v0.1.0+)
dsh plugin --profile web add dsh-workspace-enhancement
# from source: npm run build first (host loads lib/)
dsh plugin --profile web add
First install with DSH's supply-chain pnpm: native build scripts are blocked by default — allow them once per profile in
pnpm-workspace.yaml(unstrictallowBuilds):ssh2,cpu-features,koffi,node-pty,dsh-subprocess-local— then rundsh plugin --profile web install. Without it the firstaddexits non-zero (ERR_PNPM_IGNORED_BUILDS) and the bundle is not appended.
Compatibility
Which plugin version to install depends on the DSH host family you run — check it with dsh --version
(and the family under your DSH install, e.g. /@deepseek-ai/):
| Your DSH host | Install | Notes |
|---|---|---|
0.1.5 line (0.1.5-rc.1, 0.1.5-rc.2, …) | 0.1.4 or newer | The only supported family (peers are ^0.1.5-rc.1). The browser channel rides the official shared /api transport (/api/dsw/), so no standalone /dsw route is needed |
0.1.2-rc.1 family (0.1.2, 0.1.3 releases) | 0.1.3 — the last release of that line | No longer supported (retired 2026-09-11). That line moved the Connection seam — connection.rpc.handle can no longer register a channel — so no fix is backported to it |
| any other / older line | — | Never supported |
Host and plugin must move together. The 0.1.4 line speaks /api/dsw/* while 0.1.3 and earlier speak
/dsw/*, and 0.1.3 has no readByteRange. Mixing them leaves the connection / directory UI without a data
channel (the host still boots, the UI silently cannot load machines or browse). Upgrading DSH means upgrading
this plugin in the same step; the full window and the upstream drift log are in
docs/compatibility.md.
Roadmap
Current status and remaining milestones: docs/ROADMAP.md. The single backlog lives in docs/backlog.md; the generated state snapshot is docs/status.md.
Development
Start with AGENTS.md (rules, commands, red lines). Then:
| Doc | What it answers |
|---|---|
| docs/backlog.md | what is planned, in progress, blocked, done |
| docs/status.md | version, HEAD, backlog roll-up (generated by npm run status) |
| docs/architecture.md | how the plugin is built and wired |
| docs/decisions/ | why it is built that way (ADRs) |
| docs/testing.md | test layers, how to run them, sandbox limits |
| docs/compatibility.md | host version support window and upstream drift tracking |
| docs/rounds/ | what each development round delivered and how it was verified |
One gate for everything: npm run check (static constraints + typecheck + unit tests + build + pack smoke) —
the same command CI runs.
References
- dsh-ssh: remote execution engine —
ctx.subprocess/ctx.fsproviders, jump chains, PTY, directory-picker seam,session.routeplaceholder. - dsh-remote: workspace helper — machines registry, TOFU, OS keychain, web UI and settings page.
License
MIT