dsh-plugin-wsl-env

agent
Guvenlik Denetimi
Basarisiz
Health Uyari
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 7 GitHub stars
Code Basarisiz
  • fs module — File system access in .github/workflows/release.yml
  • exec() — Shell command execution in agent/native/spike/test.mjs
  • spawnSync — Synchronous process spawning in agent/native/spike/test.mjs
  • process.env — Environment variable access in agent/native/spike/test.mjs
  • rm -rf — Recursive force deletion command in agent/wsl-agent.sh
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

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

README.md

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.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. The sidebar's WSL environment panel runs a quick doctor check per distro — host-side, no model turn — and the session-start preflight logs the environment verdict on every session, adding a blocked verdict to the session's context.

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 to auto (the scope when available, otherwise per-command confinement) or explicit 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.

The shortest path, either distribution:

  1. Install bubblewrap once:
    wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap
  2. Install the plugin by name and enable it — the Plugins page
    (Add plugin → dsh-plugin-wsl-env), or
    dsh plugin --profile wsl add dsh-plugin-wsl-env.
  3. Restart the app, open a folder inside the distro; type /wsl-doctor in the
    composer for a ready/blocked verdict before the first command.

The details follow.

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>).

No host patch. The plugin never edits the installed
resources/app.asar. The 在 VS Code 中打开(WSL) bridge is a plugin row, so
an app upgrade cannot drop it and a plugin update only needs reinstall plus
restart — there is no archive re-patch step. The shipped "Open in app" item is a
separate upstream defect; see docs/UPSTREAM-OPEN-IN-APP.md.

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: the Windows/WSL platform, the per-distro WSL mode and identity, .wslconfig and /etc/wsl.conf cross-checks, the distro and host volumes, clock and DNS, bubblewrap usability, systemd / the user manager / Linger, the optional tools, peer versions, the native agent and its cache, a fresh-agent handshake, the shell smoke legs that prove the chain (frame I/O, the confined fence, the batched substrate) and a native HELLO leg, stale session scopes, and the known profile-patch and deprecated-key traps. 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 (every check that run covered passed — not a promise about a later command) 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 <list> (comma-separated) runs the named sections, 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 — the plugin-owned stale-scope sweep and agent-cache purge are safe and automatic, packages need --fix --install and an explicit confirmation, and --fix-owned narrows a run to the plugin-owned two — --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 (typed in the composer — host-side, no model turn), and the wsl_doctor model tool (in-process, merging the live resident's state). DSH_WSL_DOCTOR additionally carries a ready-to-run command (sh -c "$DSH_WSL_DOCTOR --json") that replays the doctor inside the distro. 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 2–10 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. 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.
  • The sidebar's WSL environment panel is the diagnostic page: the installed distros, an on-demand quick doctor check per distro (bwrap / systemd / Linger and the other prerequisites), and a full-run action (--fix-owned, labeled 修复 when a finding is one the plugin can repair and 完整检查 otherwise) that sweeps stale session scopes and purges a broken agent cache, then re-verifies both — without touching packages or Windows-owned configuration; every other warning keeps its manual remedy. Each checked row also states when the check ran, where it came from (手动 or the page-load 后台 refresh), and how its results landed — the per-status totals and the per-section tallies. Each checked row also expands to the full list of checks that run executed, grouped by section with each status and its evidence (the catalog rows a quick run never touched are counted, not listed), so a green or blocked row can be audited instead of trusted. The next action is visible without opening the doctor. The last result per distro is stored under $DSH_HOME/cache/dsh-plugin-wsl-env/status.json (falling back to ~/.dsh), so a page reload or an app restart still shows it; on load, stored results for distros that are currently running are re-checked in the background (never a stopped one), and a failed background run replaces the stored result exactly as observed. No result is labeled expired: the timestamp is the timestamp. Add workspace on a distro folder asks once between Remote WSL (the default, everything runs in the distro) and Windows access (the folder as a 9p share, without the Linux toolchain), recorded in the auto-preset row's windowsWorkspaces. Those recorded directories are also called out where the consequence is felt: a distro workspace running a non-WSL preset keeps a warning above the composer (a one-click switch while the session is still blank, and the durable access-record fix once a turn has started), the session header keeps a small WSL工作区正以Windows模式打开 marker after the warning is collapsed, and the panel lists the recorded directories for audit.

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 false; 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
writableRoots [] extra distro directories to grant (toolchain caches; put the same list on wsl-fs)
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 false; shipped true run confined writes in the session runtime
substrate agent in-distro agent; share is rejected at startup
writableRoots [] the same extra-writable list as wsl-shell
directory-picker-wsl includeHostHome true also offer the Windows path branch (starting at the drives)
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 and any configured writableRoots; /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 to auto or 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
npm install / pnpm add fail with EROFS the sandbox grants only the workspace by default; configure writableRoots on wsl-shell and wsl-fs (e.g. ~/.npm, ~/.cache, ~/.local/share/pnpm); see CONFIGURATION
dsh plugin add prints nothing, or the Plugins page finishes without feedback trust the profile's package.json and its dsh.profile.bundles entry: the plugin listed there is installed
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, links, requirement citations, doc consistency, 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/.

Community-facing documents are available in English and Chinese. The Chinese
README is docs/README.zh.md.

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.md value and trade-offs
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.

Yorumlar (0)

Sonuc bulunamadi