DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Big-Dao /

Big-Dao/dsh-plugin-wsl-env

Verified

Run a DeepSeek Harness session against a WSL distro: ctx.shell, ctx.fs and the GUI terminal served from inside the distro, sandboxed with bubblewrap

★ 8 Stars0 Forks3 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@da7b5a31

dsh-plugin-wsl-env

English · 中文

CI npm license

A DeepSeek Harness plugin that opens the project workspace inside a WSL 2 distribution on Windows.

The harness process stays on Windows. Command execution, shell scripts and file access all run in the distro's Linux environment for the full Linux shell toolchain. No helper service or daemon is installed, no port is opened, and confined commands run under bubblewrap.

Windows + WSL 2 only.

New here? Start with docs/QUICKSTART.md — prerequisites, recipes and the compatibility table.

Do I need this plugin?

If the harness itself can run in the distro, it is simpler not to use this plugin: install the CLI there and run it from the distro's shell. No UNC paths, no 9p round trips, no Windows-to-distro agent.

# inside the distro; the CLI needs Node >= 24.9
npm install -g @deepseek-ai/dsh
dsh web                       # browser UI, reached through WSL's localhost forwarding
dsh headless "run the tests"  # one task, one answer, exit

Use this plugin when the harness has to stay on Windows:

Your setup What to use
Work happens inside WSL; a terminal or browser UI is enough the CLI installed in the distro
You want the Windows desktop app, with commands and files in the distro this plugin
One session must reach Windows and WSL folders at once this plugin — per-path routing is the point

Mixing the two has one trap: the desktop profile name is owned by the Electron app, and the npm CLI refuses to manage that profile's plugins — use the app's Plugins page for it. docs/VALUE.zh.md has the full comparison and the cases this plugin is not for.

Why this plugin

A Windows coding tool that works on WSL projects has three options: run the whole tool inside the distro, connect to a helper server inside the distro, or let the plugin provide the distro environment. This plugin is the third option. One workflow covers both kinds of session, with the distro's real tools and the command sandbox intact.

  • Real Linux environment. Commands run in the distro's own login shell. Its PATH, nvm, cargo, pyenv and rc files apply. File tools access distro paths through an in-distro agent; the backing filesystem depends on the path.
  • No service in the distro. There is no helper server, no daemon and no listening port. The only persisted artifact is a content-addressed copy of the native agent in ~/.cache/dsh-wsl/agent/.
  • Sandboxed by default. Confined commands run under bubblewrap inside the distro. If bubblewrap is missing, the command fails; it does not run unconfined.
  • Per-session. A session opened on a distro folder gets the distro environment. A session opened on a Windows folder keeps the normal Windows tools. Both can run in one harness process.
  • Integrated with the GUI. The folder picker lists installed distros, the terminal opens a shell in the distro, and the model can see listening ports. A doctor button in the composer runs the environment diagnosis as a /wsl-doctor command — host-side, no model turn — and the session-start preflight announces the environment verdict on every session.

Why Linux, not PowerShell

Most coding agents learn from Linux-first material, and most open-source toolchains are built and tested on Linux first. Asking an agent to work through a Windows shell adds a translation layer on every call:

  • Training prior. Examples, CI scripts, Dockerfiles and most public answers use POSIX shells and GNU tools. A model's first instinct is bash, not PowerShell.
  • Toolchain support. Node, Python, Rust, Go, C/C++ and most build systems are validated on Linux first. Windows support is often a port with different quoting, paths, line endings and file locking.
  • Shell semantics. PowerShell and cmd differ from POSIX in quoting, escaping, pipelines, environment variables, exit codes and path separators. Mixing the two produces errors that look like model mistakes.
  • Environment drift. Windows setups vary by PowerShell version, execution policy, PATH and installed tools. Linux environments are easier to reproduce and containerize.

The result is more failed commands, more retries and more time spent on syntax instead of the task. Generated code is affected too: shell snippets, build commands and CI-style one-liners are usually written for POSIX, and the distro is where they run unchanged. A real Linux login shell removes that translation layer. Modern PowerShell and Windows-native toolchains narrow the gap, but the default prior and most project scripts still assume POSIX.

This plugin keeps the GUI on Windows and moves only the execution environment into the distro, so the agent gets the shell that its training and the toolchain both assume.

How it compares

Desktop remote (VS Code, ZCode) This plugin
Inside the distro a helper server one cached agent binary
Network a listening port none
Command sandbox none bubblewrap
Removal delete the server delete the cache

A desktop remote reconnects faster and is more mature. A CLI agent installed inside WSL is simpler if it covers your work. This plugin is for the case where the harness stays on Windows and commands must be sandboxed.

Requirements

  • Windows 10 or 11 with WSL 2 and at least one distro.
  • bubblewrap in the distro. It is required.
  • systemd in the distro for the default session sandbox: set [boot] systemd=true in /etc/wsl.conf, restart WSL, then run loginctl enable-linger "$USER" once. Without systemd, set sessionSandbox: false on both wsl-shell and wsl-fs.
  • Optional: ripgrep for search, git for change snapshots, inotify-tools for file watching.

No Node.js is required inside the distro.

Install

The Windows desktop app ships no dsh executable: the installer writes DeepSeek Harness.exe, and the CLI entry lives inside resources/app.asar (the probe scripts launch it through ELECTRON_RUN_AS_NODE). Use the distribution you have.

Desktop app. Open Plugins in the sidebar, choose Add plugin, enter the exact package name dsh-plugin-wsl-env, then enable it. The plugin manager writes the active profile's package.json — the dependency and the dsh.profile.bundles entry — which is the same state the CLI command below produces. To run from a checkout, add it as a linked package instead of a name. Start it the way the installer's shortcut does: DeepSeek Harness.exe, no arguments. The app opens the profile it is configured with — the one the Plugins page writes into — so a different profile is a CLI-distribution thing (dsh --profile <name>).

CLI distribution (dsh on PATH), in a Windows terminal:

dsh wsl --from-default-profile web --dump-config   # create a profile named wsl
dsh plugin --profile wsl add dsh-plugin-wsl-env    # install the plugin and its settings
dsh --profile wsl --dump-config                    # verify: prints "# == dsh-plugin-wsl-env"
dsh --profile wsl                                  # start

Install bubblewrap before the first command:

wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap

From a checkout, pnpm run doctor is the one-command environment check: Windows/WSL versions, the per-distro WSL mode, .wslconfig and /etc/wsl.conf cross-checks, the disk, clock and DNS, bubblewrap usability, systemd / the user manager / Linger, the optional tools, peer versions, the agent cache, a fresh-agent handshake, three smoke legs that prove the chain (frame I/O, the confined fence, the batched substrate), stale session scopes, the known profile-patch and deprecated-key traps, and whether a newer stable release exists on the registry. The report itemizes every check — grouped by distro, each with an explicit status marker and a copy-paste remedy under every failure — and ends in a verdict, ready (the first command should succeed) or blocked (the named checks to fix), computed under the effective configuration. A run can be scoped: --quick probes the first-command prerequisites only (~10 s), --sections <name> runs one area, and a scoped report lists the checks it did not run as explicit skips. --json is the machine contract, --fix re-verifies what it fixed (packages need --fix --install and an explicit confirmation), --all checks every installed distro, and --bundle <path> writes the diagnostic file a setup report attaches (--redact strips the paths). The fixed skeleton renders in the host's language — detected from the Windows locale, overridable with --locale <tag> or DSH_WSL_DOCTOR_LOCALE; --json stays language-independent. The same report answers through three surfaces: this CLI, the /wsl-doctor command (the composer button — host-side, no model turn), and the wsl_doctor model tool (in-process, merging the live resident's state). pnpm run diagnose is the CLI's read-only alias. pnpm run bootstrap <distro> remains the narrower tool-install helper.

The plugin notices its own updates about once a day (first check a few minutes after startup) by asking the app's plugin manager — it never fetches on its own. When a newer stable release exists, the managed DSH_WSL_PLUGIN_UPDATE variable in every distro session carries the fact; wsl-update-check.enabled: false in the patch config turns the check off, and DSH_UPDATE_REGISTRIES overrides which registries the doctor's on-demand check asks (comma-separated, or empty to skip it on an air-gapped machine). Upgrading stays manual, because the harness has no hot reload: install again (Plugins page, or the add command), then restart. Uninstall on the Plugins page, or with dsh plugin --profile wsl remove dsh-plugin-wsl-env where the CLI is installed; the agent cache under ~/.cache/dsh-wsl can then be deleted. If you added a local workspace-files-wsl workaround to your profile to restore previews, remove it after upgrading: the plugin no longer mounts that row.

Using it

Open a distro folder as the workspace, for example \\wsl.localhost\ubuntu\home\you\project.

  • Opening a folder in any distro runs there, even when it is not WSL's default.
  • Commands run in the distro's login shell. /home/you/x and \\wsl.localhost\ubuntu\home\you\x are the same file.
  • New terminal opens a shell in the same distro and directory.
  • The model sees listening ports and can return the URL of a dev server. WSL 2 forwards localhost to Windows.
  • The Permissions selector offers read-only, workspace-write (default) and danger-full-access. Only danger-full-access runs without the sandbox.

Configure

Settings live on named rows in $DSH_HOME/profiles/<name>/cordis.patch.yml. The main values:

Row Key Default Purpose
wsl-shell distro '' distro mode; empty/auto follows the workspace, default forces WSL's default
sandbox true confine commands with bubblewrap
sessionSandbox shipped true run the session's commands in one systemd user scope
agentBackend auto auto, native or shell for the in-distro agent
maskWindowsDrive false hide /mnt from confined commands
wsl-fs distro '' distro mode; empty/auto follows each target path
restrictToDistro true under a fixed distro, refuse paths that belong to another distro
sandbox true check confined writes against the policy
sessionSandbox shipped true run confined writes in the session runtime
substrate agent in-distro agent; share is rejected at startup
directory-picker-wsl includeHostHome true also list the Windows home directory
subprocess-wsl distro '' distro mode; empty/auto follows the launch directory
wsl-preflight distro '' distro the environment checks probe; empty/auto follows the session workspace
startup true run the same checks once at plugin load, so the verdict is in the log before any session opens

fs-routing is the top-level router for root-level ctx.fs calls: its distro settings are independent of wsl-fs, and it is a top-level row patched by id like the others. wsl-shell and wsl-fs are inside the shipped wsl preset, so overriding them by id does not work. Override the whole preset-wsl row instead; docs/CONFIGURATION.md has the recipe and the remaining keys. cordis.patch.yml is the commented reference for every shipped value.

Architecture

One harness process can serve Windows-folder and distro-folder sessions at the same time. Services attach at two levels:

app level (one per process)
├─ directory-picker-wsl, workspace-files, subprocess-wsl
├─ fs-routing: root filesystem routed by path
└─ auto-preset: binds the wsl preset when a session opens a distro folder

wsl preset (per session)
├─ wsl-shell: commands
└─ wsl-fs: file operations
  • wsl-shell runs commands through one resident agent inside a systemd user scope. The agent is the packaged static-musl dsh-wsl-agent for the distro's architecture (x86_64 or aarch64), with the shell agent as fallback. Each command runs in the login shell under a bubblewrap mount profile. The scope outlives the command, so background servers survive and ps sees the session. With sessionSandbox: false, the agent stays per distro and each command gets its own profile.
  • wsl-fs serves reads and writes through the same in-distro agent. Confined writes run in a long-lived agent whose mount profile matches the policy, so the kernel enforces the write boundary. There is no host-side file fallback: if the agent is unavailable, file operations fail.
  • The GUI file tree and previews are the shipped workspace-files row, not a plugin replacement: its client half is the browser file provider. It reads WSL paths through the routed root filesystem, so it stays distro-side, at the cost of a few extra agent round trips.
  • App-level services route by path. A distro UNC path goes to the distro; a Windows drive path stays on the host. See docs/root-fs-routing.md.

Sandbox

Mode What a command can do
read-only read the whole distro; a fresh /dev is mounted, nothing else is writable
workspace-write the above, plus the session workspace; /tmp is the distro's real /tmp with the session runtime
danger-full-access no sandbox

With sessionSandbox: false, a workspace-write command gets a private /tmp instead of the distro's real one. read-only does not mount a writable /tmp.

bubblewrap is required. If it is missing or broken, confined commands report SANDBOX_UNAVAILABLE and do not run. Setting sandbox: false on the owning row disables confinement; the model is told that the operation is unsandboxed.

Enforcement is reported as partial. A command inside the distro can still start a Windows executable through /mnt/c, and bubblewrap cannot control Windows processes. maskWindowsDrive: true hides /mnt from the confined view unless overridden by interopVisibility: visible; this narrows the hole but does not close it. Disabling WSL interop requires a separate distro-level change: set [interop] enabled=false in /etc/wsl.conf. See docs/ARCHITECTURE.md and SECURITY.md.

Troubleshooting

Symptom Fix
every command reports SANDBOX_UNAVAILABLE install bubblewrap in the distro, or set sandbox: false
the session refuses before the first command run pnpm run doctor; enable systemd and linger, or set sessionSandbox: false on wsl-shell and wsl-fs
glob and grep are slow in the distro run pnpm run bootstrap <distro> and use its printed install command to install ripgrep
dsh plugin add prints nothing the plugin is already installed; this is success
a profile patch for wsl-shell/wsl-fs changes nothing those rows are nested inside preset-wsl. Run pnpm run preset --check <patch>; it fails with the complete block to paste
changes under lib/ have no effect run pnpm run sync:windows, then restart the app

More error codes and refusals are in docs/ARCHITECTURE.md. Everything the plugin does not do is in docs/LIMITATIONS.md.

Development

pnpm install           # development dependencies
pnpm run build         # compile src/ into lib/ (lib/ is committed)
pnpm test              # style, syntax, build consistency, types, unit tests
pnpm run test:coverage # unit tests with coverage thresholds
pnpm run sync:windows  # update the Windows-side copy the app loads

The toolchain follows the harness host: Node ^22.19.0 || >=24.0.0. src/ is TypeScript; lib/ is generated and must stay in sync. The app loads a Windows-side copy of lib/, so run sync:windows and restart the app after a build.

Real-machine probes run on Windows + WSL only. See CONTRIBUTING.md and test/probe/.

Documentation

Document Content
docs/QUICKSTART.md first run, recipes, compatibility
docs/PERFORMANCE.md ext4 agent vs 9p, measured latency, how to reproduce
docs/MIGRATION.md upgrading from earlier versions, release gates
docs/COMPATIBILITY.md the tested Windows/WSL/distro matrix and scope decisions
docs/API.md the package's subpath exports at a glance
docs/CONFIGURATION.md every configuration key and how to override it
docs/ARCHITECTURE.md services, path routing, sandbox, error codes
docs/LIMITATIONS.md what the plugin does not do
docs/ROADMAP.md current plan and deferred work
docs/ROADMAP-1.0.md the 1.0 and ecosystem plan
docs/VALUE.zh.md value and trade-offs in Chinese
CONTRIBUTING.md development loop, gates, probes
SECURITY.md private vulnerability reporting
SUPPORT.md supported versions and support channels
docs/RELEASING.md release checklist

License

MIT. See LICENSE.

—/ 5

No ratings yet

Verified DSH bundle

Commit da7b5a31aac7

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout