dsh-plugin-wsl-env
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.
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
dsh-plugin-wsl-env
English · 中文
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,pyenvand 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
bubblewrapinside the distro. Ifbubblewrapis 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
cmddiffer 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.
bubblewrapin the distro. It is required.- systemd in the distro for the default session sandbox: set
[boot] systemd=truein/etc/wsl.conf, restart WSL, then runloginctl enable-linger "$USER"once. Without systemd, setsessionSandboxtoauto(the scope when available, otherwise per-command confinement) or explicitfalseon bothwsl-shellandwsl-fs. - Optional:
ripgrepfor search,gitfor change snapshots,inotify-toolsfor file watching.
No Node.js is required inside the distro.
Install
The Windows desktop app ships no dsh executable: the installer writesDeepSeek 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:
- Install
bubblewraponce:wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap - Install the plugin by name and enable it — the Plugins page
(Add plugin →dsh-plugin-wsl-env), ordsh plugin --profile wsl add dsh-plugin-wsl-env. - Restart the app, open a folder inside the distro; type
/wsl-doctorin the
composer for aready/blockedverdict 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 thedsh.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 installedresources/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/xand\\wsl.localhost\ubuntu\home\you\xare 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) anddanger-full-access. Onlydanger-full-accessruns 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 theauto-presetrow'swindowsWorkspaces. 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-shellruns commands through one resident agent inside a systemd user scope. The agent is the packaged static-musldsh-wsl-agentfor the distro's architecture (x86_64oraarch64), with the shell agent as fallback. Each command runs in the login shell under abubblewrapmount profile. The scope outlives the command, so background servers survive andpssees the session. WithsessionSandbox: false, the agent stays per distro and each command gets its own profile.wsl-fsserves 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-filesrow, not a plugin replacement: its client half is the browserfileprovider. 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)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi