dsh-wsl-suite: Connect a WSL workspace for DeepSeek Harness

Preface

Running DeepSeek Harness on Windows with the workspace located in the extension filesystem, when sessions need to run on the Linux side, the conventional approach is to share the directory to WSL via 9P, or to spawn a wsl.exe for every command. The former suffers from polling and visibility issues, while the latter incurs a fixed overhead of a few hundred milliseconds per call. dsh-wsl-suite changes the foundation: it places a resident agent process in the distribution and allows the host to query it on-demand via a JSON-lines pipe.

This plugin is a rewrite of dsh-wsl-workspace, maintained by stallenlu, under the MIT license, with the current version 0.1.4. It validates a specific workflow: adding a WSL workspace from the sidebar, running the entire session inside the distribution, entering the distribution with the terminal, and the model accessing distribution-native tools.

Problems It Solves

First, let’s discuss the differences between the three access methods. Data comes from README testing on real distributions on a local Ubuntu machine:

Operation 9P Shared Per-call wsl.exe spawn Local agent
stat 4.3 ms 212–282 ms 0.20 ms
readdir 2.8 ms 212–282 ms 0.25 ms
Run a command — 212–282 ms 13–20 ms
Glob a 5.2 GB repo (64,625 files) 44 s, missing ~50% of directory tree — 0.59 s

These three consequences determine the design trade-offs of this plugin:

  1. No more polling. The old scheme polled the share every 3 seconds and walked the directory tree every 30 seconds; when WSLg was open, it would pull focus back to the Windows desktop every half minute. The resident agent is queried on-demand; when idle, nothing touches the distribution.
  2. /mnt/<drive> is available. 9P sharing only serves ext4; drvfs mounts are invisible to it. The old scheme required routing changes and probing each drive to synthesize /mnt entries. In the distribution, /mnt/c is just a normal directory, so this logic was removed.
  3. Search runs inside the distribution. The old scheme’s grep/glob relied on the packaged Windows ripgrep running against the share: a full repo walk took 44 s (9P) vs. 0.29 s inside the distribution, and content search took 150 s vs. 0.79 s; furthermore, the 9P run returned 22,436 lines while the native run returned 41,838 lines—silently missing about half the directory tree. The WSL variant switched to running the distribution’s own rg.

These numbers are real-world tests on the README author’s machine. There may be discrepancies with different machines or distributions, but the direction is consistent: metadata operations and searches crossing system boundaries incur overhead one to two orders of magnitude higher than running natively inside the distribution.

Architecture: A Resident Agent Instead of One Process Per Command

Overall data flow:

Browser ──POST /dsh-wsl/api──▶ Host Plugin ──pipe──▶ node agent.mjs (in distro)
                                    │                       │
                                    └── ctx.fs / ctx.shell ◀─┘
  • agent/agent.mjs is a zero-dependency Node script running inside the distribution, providing stat, lstat, readdir, read, readRange, write, mkdir, remove, realpath, exec, kill, ping, communicating via JSON lines. It rebuilds the login environment once at startup, and --exec can see ~/.local/bin.
  • src/channel.ts is the host side of the pipe: it spawns the agent, pairs replies by ID, and closes after 10 minutes of inactivity (inactivity means no pending requests, so a 30-minute test run won’t be mistakenly killed), one channel per distribution, and restarts if the agent dies.
  • src/shell.ts’s WslShellExecutor handles exit codes, stream separation, real-time streams, timeout for killing process groups, and buffer limit for tail output; unobserved rejections from background handles won’t kill the host.
  • src/fs.ts’s WslFileSystem provides 12 primitives, including version-guarded writes, handling FS_STALE_VERSION/FS_AMBIGUOUS_EDIT/FS_NOT_TEXT, sandbox fencing, atomic writes for rename, and byte-accurate CRLF.
  • src/index.ts is the host entry point: distribution discovery, materializing wsl-<mode>, DSH_WSL_DISTRO, and a loopback-only POST route.

Core Features

Adding WSL Workspace from the Sidebar. In the “Workspace” partition title bar, press the W button next to the built-in “Add Workspace” control; if the title bar doesn’t exist, fall back to the bottom list entry. The modal lists installed distributions and allows browsing the Linux file system. Upon confirmation, it registers via the host’s own workspace registry. A WSL workspace is simply a host workspace with a path as a UNC (\\wsl.localhost\Ubuntu\home\you\…), containing only one record and no private storage. The modal follows navigator.language; zh-* displays Simplified Chinese.

Sidebar Terminal Follows the Session into the Distribution. src/terminal.ts wraps the host’s unique subprocess.spawnTerminal: if the cwd in the request is a UNC, the call is rewritten as:

wsl.exe -d <distro> --cd <linux cwd> -- <shell>

Thus, the host’s own ConPTY spawns a real Linux PTY, with input, output, and resize all looped. The Windows cwd is transparently passed through. The POSIX shell name requested is passed into the distribution (/bin/zsh, /bin/fish, etc.; falls back to /bin/bash if not found).

Model Gets WSL Native Tools. Instead of a global mode, a twin (wsl-<mode>) is generated for each healthy preset in the profile (standard → wsl-standard). The shell/fs tools are swapped for the WSL versions, and new grep/glob are added that run the distribution’s built-in ripgrep, covering /mnt/<drive> and being tolerant of unreadable directories (exit 2 preserves partial results rather than failing the entire call). The persona, skills, tasks, and delegation from the preset are passed through unchanged.

Variant Transformation. This happens in src/host/variants.ts by matching lines by ID on the parsed document without touching indentation. This makes it immune to indentation fragility issues seen in the original, such as “invisible subagent lines” or “tool ‘subagent’ is already registered in this scope”. It also clears residual process.platform === 'win32' guards lifted into the world; otherwise, that expression evaluated on the host side would exactly disable the tools this variant is meant to provide.

Installation & Enablement

The package name is dsh-wsl-suite (dsh-wsl could not be registered due to npm anti-piracy rules). Install it under the profile directory:

# In C:\Users\<you>\.dsh\profiles\desktop\
pnpm add dsh-wsl-suite          # or: npm install dsh-wsl-suite

Then add "dsh-wsl-suite" to dsh.profile.bundles in that profile configuration file and restart the harness:

"dsh.profile.bundles": [ ..., "dsh-wsl-suite" ]

If the version hasn’t been released to the registry yet, you can also pull it from GitHub:

pnpm add "dsh-wsl-suite@github:stallenlu/dsh-wsl-suite"

A Naming Rule to Remember: When reading the README, you must remember that the registration ID of the client bundle must equal the package name. The host uses the package name to build the key for the client boot image, and arrive() only accepts bundles where factories have been registered under that ID; otherwise, the entry will never activate. tsdown.config.ts writes this ID into the bundle banner, and tests/client.test.ts asserts that it matches package.json.

Typical Usage

After installation, the typical path is:

  1. Restart the harness; the W button appears in the “Workspace” title bar of the sidebar (if not present, use the bottom list entry).
  2. In the modal, select the distribution, browse to the target directory, and click “Add this folder”.
  3. For sessions containing this workspace, the model’s preset automatically appears with a wsl-<mode> twin, and shell/fs/grep/glob all run inside the distribution.
  4. The sidebar terminal opens inside the distribution, directly as the distribution’s shell.

Self-Check on the Host Side (No UI) and Direct Pipe Verification:

curl -X POST http://127.0.0.1:<gui-port>/dsh-wsl/api \
  -H "content-type: application/json" -d '{"method":"listDistros"}'
# → {"ok":true,"value":{"distros":[{"name":"Ubuntu","default":true}]}}

check and listDir use the resident agent inside the distribution, so a 200 response with real entries is sufficient to prove the entire pipe works.

Development Loop with Local Source Code Modification:

Since the installed copy is copied rather than a symlink, you must reinstall after any change:

npm run verify    # Type check + build + test
plugin_manager remove_bundle target=dsh-wsl-suite
plugin_manager install_bundle target=file:<source-directory>

Then restart DSH. If the entry disappears again, first check if dsh.profile.bundles in the profile still contains dsh-wsl-suite, and then use plugin_manager set_bundle target=dsh-wsl-suite enabled=true to hot-mount it. The README explicitly states that concurrent profile rewriting used to remove this entry.

Applicable Scenarios & Notes

Suitable for: Windows power users, developers working in WSL2 who want Harness sessions and their toolchains to land on the Linux side. If your session is already pinned to Windows, the value of this plugin is limited—it addresses the boundary overhead when toolchains on both sides are inconsistent.

Things to Note:

  • The plugin runs with the permissions of the current dsh process. Installing it into a profile essentially gives Harness the ability to read/write workspaces and execute commands. You should read the source code and the license (MIT) before installing.
  • The client entry’s mounting depends on the host’s private DOM structure (class names of title bars). The README also acknowledges this is a legacy technique inherited from the old plugin: walking up to [class*="_sectionHeader"] and injecting. If the host release changes class names, the sidebar entry will fail, but the bottom list entry will still work as a fallback.
  • When reading materials, be careful to distinguish. dsh-wsl-suite is the rewritten version; the old dsh-wsl-workspace is maintained by a different group of maintainers. The README states that issues with dsh-wsl are submitted to this repository, while issues with the old plugin should go to its own repository.
  • The workload data all comes from real-world tests on the README author’s machine and is only used to illustrate magnitude, not as a baseline that can be reproduced on your machine.

Conclusion

The value of this plugin lies not in new concepts, but in replacing the two slow paths (9P sharing and per-command spawn) with a resident process inside the distribution plus on-demand queries. Polling timers are removed, /mnt/<drive> paths are no longer special, and search returns to the distribution’s own ripgrep. Sidebar entry and terminal integration make WSL workspaces indistinguishable from Windows workspaces within the same UI.

Directory page: https://www.skillhub.cn/plugins/stallenlu/dsh-wsl-suite
GitHub: https://github.com/stallenlu/dsh-wsl-suite

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye