dsh-wsl-workspace
Repository-only paths. This package publishes lib/, cordis.patch.yml, both READMEs, TESTING.md, both CHANGELOGs, LICENSE, NOTICE and docs/driving.* (plus docs/README.md, which npm packs because it is named README). Paths under src/, tests/, scripts/, ci/ and the rest of docs/ exist in the repository and are not published — this file names them as coordinates, not as files you can open from the tarball.
English · 中文 · 日本語 · 한국어 · Français · Deutsch · Español · Português · Русский · 日本語 · 한국어 · Français · Deutsch · Español · Português · Русский
alt text
Add a WSL workspace from the DeepSeek Harness web GUI and run the whole agent session — bash commands and file reads/writes — inside a local WSL distribution with Linux paths. Nothing needs to be installed inside WSL. The session can reach both WSL and Windows at the same time: bash commands run inside the WSL distribution, while Windows files stay accessible via /mnt/<drive> (for example /mnt/c/Users/...).
Install
Pick one of the three ways below, then restart dsh web:
# 1) npm package
dsh plugin --profile web add dsh-wsl-workspace
# 2) GitHub repository (ships the prebuilt lib/, no local build required)
dsh plugin --profile web add https://github.com/dsh-wsl-workspace-maintainers/dsh-wsl-workspace
# 3) Local directory (development / self-hosted)
dsh plugin --profile web add D:\path\to\dsh-wsl-workspace
After restarting dsh web, a W button appears in the Workspaces section header, immediately right of the add-workspace control; the collapsed rail keeps it beside Settings at the sidebar foot.
Compatibility
This build declares these DSH releases, each verified on an isolated instance (its own DSH_HOME, dependencies pinned to that release, the full check suite):
0.1.0-rc.7 · 0.1.0-rc.8 · 0.1.1-rc.1 · 0.1.1-rc.2 · 0.1.2-rc.1 · 0.1.3-alpha.2 · 0.1.5-rc.1 · 0.1.5-rc.2 · 0.1.7-rc.1 · 0.1.7-rc.2 · 0.2.0-rc.2 · 0.2.1-rc.2
The list mirrors dsh.compatibility.dshReleases in package.json, and a unit test fails if the two drift apart. 0.2.1-rc.2 is the DSH release DSH Desktop 0.2.1-rc.2 ships — verified against the host inside that application's own bundle, not only against the npm release — so the Desktop is covered by the same declaration; the app's help panel (the dialog's "?" button) shows the same chips next to the plugin version. The plugin detects the DSH generation at runtime and picks the matching API, and a release exposing neither fails loudly instead of leaving an empty workspace. A release outside the list usually still works, but is unverified.
Usage
Click that W button — or pick “Add WSL workspace”, directly under “Add workspace…” in the same control’s menu — to open the "Add WSL workspace" dialog. Pick a distribution from the list, then browse the directory tree or type an absolute Linux path (for example /home/me/proj) — use the Check button to verify the path exists before creating the workspace. The dialog follows the DeepSeek Harness UI language. The username field is optional: leave it empty to run commands as the distribution's default user, or name a Linux user of that distribution to run the session as that user instead (equivalent to wsl.exe -u <username>). The username only changes the bash tool's run identity — the file tools go through the Windows-side WSL share and are unaffected. Each workspace's username is kept in <dshHome>/wsl-workspaces.json; delete the entry (or recreate the workspace from the dialog) to return to the default user.
Click "Create & open" to start a new session in the workspace. In the new session the bash tool executes commands inside the chosen distribution and read/write/edit operate on WSL files, so every path the model sees is a Linux path. The mode picker keeps working as usual: Standard, PTC, Minimal and Creative each land on their WSL variant automatically (the WSL variant entries in the picker are bilingual, e.g. WSL · Standard mode(标准模式)), and Windows files stay reachable from inside the session under /mnt/<drive> (for example /mnt/c/Users/...). The dialog's "?" button opens a panel with the DSH releases this build declares, how the plugin is used, and the limitations it cannot fix.
alt text
Behavior notes
- bash tool: runs inside the WSL distribution as the configured username (empty = the distro default user, often
root), so it can read and write anywhere in the distro. The Windows ACL sandbox cannot wrap wsl.exe — its children run on the Linux kernel side — so WSL itself is the isolation boundary and the DSH file policy does not apply to bash.
- File tools (
read/write/edit): go through the Windows-side WSL 9P share; the username field does not affect them. Two seams the host's own provider would supply are re-established inside the WSL world, because a variant mounts this plugin's fs provider in the preset's isolate realm where the host's fs-sandbox wrapper is not in the call path. Symlinks: the share lists a Linux link but cannot resolve it, so a link path used to look like a missing file; resolve/lstat now ask the distribution (wsl.exe … readlink -f) and continue at the real path, and a link is never replaced by a regular file. The access mode: write/edit are fenced by ctx.sandboxPolicy exactly as the host backend fences them — the same writableRoots allow-list (plus the distribution's /tmp, the temp area of the world the session runs in), the same FS_SANDBOX_DENIED the tool layer renders as a denial, and the same sandboxMode fact it reads for escalation. Because the fence runs after resolution it judges the real path: a link out of the workspace is an outside write and is denied under workspace-write. A deployment that mounts no policy service fences nothing, as on the host. File references (issue #49): a conversation's file links, a tool row's line reference and the turn tail's changed files all open through the right Sidebar, and the address they carry holds the path exactly as the model wrote it — a Linux path. The host resolves such a path with node:path.resolve(cwd, path), where a POSIX absolute path is root-relative, so the pane used to report the file missing: /mnt/d/… landed on the workspace drive (D:\mnt\d\…), an in-distribution path under the cwd share's root, and the drvfs mount 9P cannot serve failed with EPERM. The client half now translates the address before the Sidebar sees it — /mnt/<drive>/… back to X:\…, and every other Linux path through the distribution's UNC share, taken from the session's UNC workspace or from the record stored for a /mnt/<drive> workspace — which repairs the tab's content and the metadata read under the same address at once. A session the plugin does not treat as WSL-bound keeps its addresses untouched, and an address it cannot rebuild byte-for-byte, or whose distribution is unknown, is passed through rather than guessed at: the host plane offered no hook to use instead, because and the endpoint belong to other plugins and cordis refuses a second for a name another fiber owns. Only 0.1.5-rc.1 and later ship a right Sidebar and a document preview at all, so on the six earlier declared releases the hook simply does not install.
Driving it from another agent
docs/driving.md (ships inside the package) answers the five questions a fresh agent cannot invent: how to start a harness headlessly (and the one environment variable that must be unset, or the launch exits 0 silently), where the login URL and its token come from (measured: the token is not consumed while that process lives — restart the instance to invalidate it), this plugin's route plus how to enumerate its methods at runtime (describe), how to inject text into the composer and send it (the composer is Lexical: one beforeinput, never execCommand), and how to move a session off a provider that answers 401. The same facts are returned by the plugin's describe method (route, loopbackOnly, methods, installProof, driving), so they travel with the HTTP surface too.
Changelog
The full release history — every version, newest first — lives in CHANGELOG.md (中文); this README keeps only the current behaviour described above.
Documentation
Most of docs/ is repository-only (it is not in package.json#files; the shipped files are docs/driving.md, docs/driving.zh.md and docs/README.md, which npm packs because it is named README). In the repository, docs/README.md indexes what lives where and in which language: the design record (zh), the per-release compatibility evidence behind the dsh.compatibility.dshReleases declaration, and the archived notes whose banners name what superseded them. Before a change or a release, follow TESTING.md.
License & attribution
MIT — see LICENSE and NOTICE. The NOTICE precisely lists:
- Adapted/inherited source code: DeepSeek Harness (MIT) —
dsh-bash-local (executor mechanics), dsh-fs-local (WslFileSystem subclasses it), and the shipped agent presets (read and transformed by the variant generator);
- Design references (no source copied): dsh-bash-terminal (MIT, wsl argv / WSLENV approach), dsh-side-panel (BSD-3-Clause, host-route pattern), vpshub (MIT, roadmap reference).
Keep LICENSE and NOTICE when redistributing.
Acknowledgments
Special thanks to dsh-deep-whale (DSH Web 鲸鱼娘 skin series · 深海女仆工坊 maid-atelier, CC BY-NC-SA 4.0): the whale girl skin plugin brings a full set of adorable skins to the DeepSeek Harness Web UI and makes daily use of DSH a warmer experience.