dsh-plugin-wsl-env 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -14
- package/CONTRIBUTING.md +25 -33
- package/README.md +10 -10
- package/SECURITY.md +6 -4
- package/SUPPORT.md +8 -8
- package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
- package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
- package/agent/wsl-agent.sh +4 -5
- package/cordis.patch.yml +8 -1
- package/docs/ARCHITECTURE.md +42 -35
- package/docs/COMPATIBILITY.md +121 -0
- package/docs/CONFIGURATION.md +254 -273
- package/docs/LIMITATIONS.md +61 -26
- package/docs/MIGRATION.md +16 -13
- package/docs/PARITY.md +4 -3
- package/docs/PERFORMANCE.md +11 -9
- package/docs/QUICKSTART.md +46 -21
- package/docs/README.zh.md +7 -7
- package/docs/RELEASING.md +257 -0
- package/docs/REQUIREMENTS.zh.md +77 -0
- package/docs/ROADMAP-1.0.md +105 -61
- package/docs/ROADMAP.md +15 -9
- package/docs/UPSTREAM-FILE-REFERENCE-SEAM.md +3 -3
- package/docs/UPSTREAM-FSIO-EXPORT.md +7 -7
- package/docs/UPSTREAM-OPEN-IN-APP.md +57 -0
- package/docs/UPSTREAM-SESSION-NOTICE.md +4 -4
- package/docs/VALUE.zh.md +144 -0
- package/docs/adr/0002-native-agent.md +348 -0
- package/docs/root-fs-routing.md +186 -0
- package/docs/screenshots/picker.png +0 -0
- package/docs/screenshots/preview.png +0 -0
- package/docs/screenshots/sandbox.png +0 -0
- package/docs/screenshots/terminal.png +0 -0
- package/lib/agent-exec.d.ts +2 -3
- package/lib/agent-shared.js +1 -0
- package/lib/agent.d.ts +19 -8
- package/lib/agent.js +73 -13
- package/lib/fs-routing.d.ts +2 -1
- package/lib/fs-routing.js +13 -1
- package/lib/fs-substrate.d.ts +11 -9
- package/lib/fs-substrate.js +6 -9
- package/lib/fsio-agent.d.ts +2 -3
- package/lib/fsio-agent.js +2 -3
- package/lib/index.d.ts +20 -13
- package/lib/index.js +18 -11
- package/lib/session-runtime.d.ts +5 -3
- package/lib/session-runtime.js +5 -4
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +13 -4
- package/scripts/diagnose.sh +1 -1
- package/scripts/doctor.mjs +3 -3
package/docs/LIMITATIONS.md
CHANGED
|
@@ -5,11 +5,12 @@ boundary; one that is not is a bug report waiting to happen.
|
|
|
5
5
|
|
|
6
6
|
## The filesystem substrate (`wsl-fs`)
|
|
7
7
|
|
|
8
|
-
The file tools
|
|
9
|
-
reads, writes and identities happen
|
|
8
|
+
The WSL-routed file tools use one substrate: the resident in-distro agent —
|
|
9
|
+
reads, writes and identities happen on the filesystem backing the target path
|
|
10
|
+
(typically ext4 for a distro's own files, but other mounts are possible). The former
|
|
10
11
|
Windows-side share backend (`substrate: "share"`) is retired: a profile still
|
|
11
|
-
carrying the value is refused at boot with the migration, and
|
|
12
|
-
|
|
12
|
+
carrying the value is refused at boot with the migration, and WSL-routed file
|
|
13
|
+
operations do not use the Windows-side 9p share. What to know about the substrate:
|
|
13
14
|
|
|
14
15
|
- **A new file publishes with mode 0600.** That is the host backend's own POSIX
|
|
15
16
|
publication semantics (a private 0600 staging file, renamed into place), which
|
|
@@ -27,17 +28,22 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
27
28
|
- **The mutation guard survives to the write.** `createIfAbsent` publishes with
|
|
28
29
|
a no-replace link inside the distro, which closes the check-then-write window
|
|
29
30
|
for creates outright. An overwrite or edit carries the version it was based
|
|
30
|
-
on into the write op, and the agent re-verifies it against a fresh stat
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **A write
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
on into the write op, and the agent re-verifies it against a fresh stat before
|
|
32
|
+
the rename. A mismatch returns `FS_STALE_VERSION`, but a change between the
|
|
33
|
+
check and rename can still be overwritten; this is not an atomic
|
|
34
|
+
compare-and-swap.
|
|
35
|
+
- **A cancelled write can have an unknown outcome.** Cancellation is reported
|
|
36
|
+
before the agent necessarily finishes. A write already being published may
|
|
37
|
+
still take effect; inspect the file before retrying. The native agent refuses
|
|
38
|
+
queued writes when it processes their cancellation before they start. The
|
|
39
|
+
serial shell fallback removes requests cancelled while waiting on the host;
|
|
40
|
+
a request already sent to the agent may still take effect even if it has not
|
|
41
|
+
yet acknowledged the request.
|
|
42
|
+
- **A successful write is not guaranteed durable across a crash.** The native
|
|
43
|
+
agent syncs the staged file before publication; the shell agent attempts
|
|
44
|
+
`sync -f` but ignores failure. Neither syncs the containing directory after
|
|
45
|
+
the rename or link. A kernel panic or power loss can therefore lose data or
|
|
46
|
+
the published directory entry; the operation is not a crash-consistent store.
|
|
41
47
|
- **A Linux filename containing a backslash cannot be addressed.** The UNC
|
|
42
48
|
display form (`\\wsl.localhost\<distro>\...`) is the file's identity
|
|
43
49
|
everywhere - the session header, the GUI, the caches - and the backslash is
|
|
@@ -128,7 +134,7 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
128
134
|
slower than a direct agent call, especially on large files; the model's own
|
|
129
135
|
file operations are unaffected — they use the preset filesystem and the
|
|
130
136
|
resident agent directly. Measured 2026-10-19 with
|
|
131
|
-
[`test/probe/gui-preview.sh`](
|
|
137
|
+
[`test/probe/gui-preview.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/gui-preview.sh) (20 iterations on
|
|
132
138
|
this machine): the routed backend's `stat` p95 2.7 ms, `listDir` p95 5.1 ms,
|
|
133
139
|
and a 1 MiB `readByteRange` p95 **63.5 ms/MiB** — the service's own round
|
|
134
140
|
trips sit on top of those, and the 1 s/MiB budget has a wide margin. No
|
|
@@ -143,6 +149,20 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
143
149
|
shipped row's own caps (`maxBytes`, `maxFileBytes`, `maxLines`, `maxEntries`)
|
|
144
150
|
can be overridden on the `workspace-files` row.
|
|
145
151
|
|
|
152
|
+
## `@` completion
|
|
153
|
+
|
|
154
|
+
- **`@` references still walk the 9p share.** The shipped
|
|
155
|
+
`file-reference-local` service uses `node:fs/promises` from the Windows
|
|
156
|
+
harness process, so a WSL workspace pays a 9p `readdir`/`lstat` per
|
|
157
|
+
directory. On a 20-directory / 520-file tree the walk was **84.7 ms**
|
|
158
|
+
median over 9p versus **1.5 ms** on ext4 (~56×;
|
|
159
|
+
[`probe:file-reference`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/file-reference-9p.sh), 2026-10-19).
|
|
160
|
+
[`src/file-reference-wsl.ts`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/src/file-reference-wsl.ts) is the distro-side
|
|
161
|
+
traversal (built and unit-tested) but is **not** in `cordis.patch.yml`: the
|
|
162
|
+
funnels it would replace are module-internal. The fallback is the 9p walk
|
|
163
|
+
until upstream exposes `FileReferenceTraversal`. Cost, seam and wire
|
|
164
|
+
trigger: [UPSTREAM-FILE-REFERENCE-SEAM.md](UPSTREAM-FILE-REFERENCE-SEAM.md).
|
|
165
|
+
|
|
146
166
|
## The sandbox
|
|
147
167
|
|
|
148
168
|
- **The one-shot fallback has only the host deadline.** With `agent: false`, or
|
|
@@ -158,12 +178,14 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
158
178
|
pays the native preparation/cache cost (~70 ms once); `agentBackend: shell`
|
|
159
179
|
forces the shell reads. v6's FS concurrency would only shave the native
|
|
160
180
|
runtime's 7.7 ms/16 writes and is deferred.
|
|
161
|
-
- **The native backend is the default; aarch64 is
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
181
|
+
- **The native backend is the default; aarch64 is executed under qemu in CI.**
|
|
182
|
+
The `native packages` job builds static-musl x86_64 and aarch64 binaries and
|
|
183
|
+
runs the 18-check suite against both the freshly built aarch64 artifact and
|
|
184
|
+
the committed `agent/native/linux-aarch64/dsh-wsl-agent` under qemu-user.
|
|
185
|
+
Physical aarch64 WSL is out of scope (no Windows-on-ARM machine in reach);
|
|
186
|
+
see [COMPATIBILITY.md](COMPATIBILITY.md#scope-decisions).
|
|
187
|
+
`agentBackend: auto` still selects the packaged binary on an aarch64 distro;
|
|
188
|
+
`native` refuses if preparation fails, and `shell` forces the shell agent.
|
|
167
189
|
- **The session runtime needs systemd and a lingering user manager.** The
|
|
168
190
|
shipped preset sets `sessionSandbox: true`; a distro without systemd as PID 1,
|
|
169
191
|
a reachable `systemd --user`, `systemd-run`, or `Linger` is refused on the
|
|
@@ -179,13 +201,15 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
179
201
|
Windows program, which escapes the Linux-side boundary. The plugin reports this as
|
|
180
202
|
`enforcement: partial`. See [ARCHITECTURE.md](ARCHITECTURE.md#reported-enforcement).
|
|
181
203
|
The `maskWindowsDrive` key on `wsl-shell`, `wsl-fs` and `fs-routing` narrows the
|
|
182
|
-
hole: the confined profile shadows `/mnt` with an empty tmpfs, so
|
|
183
|
-
|
|
204
|
+
hole: the confined profile shadows `/mnt` with an empty tmpfs, so files and
|
|
205
|
+
executables at paths under `/mnt` are hidden. Other mounts, alternate access
|
|
206
|
+
paths and unrestricted file reads are not covered by this mask. It is not a
|
|
184
207
|
closure — a command that can write the workspace can write an executable there and
|
|
185
208
|
run it (binfmt interop dispatches on file content, not location) — so enforcement
|
|
186
209
|
stays `partial`, the result's sandbox fact reports `windowsDrive: "masked"`, and
|
|
187
|
-
|
|
188
|
-
`wsl.conf`).
|
|
210
|
+
distro-level disabling of WSL interop (`[interop] enabled=false` in
|
|
211
|
+
`wsl.conf`) must be configured separately. On `wsl-shell`, an explicitly set
|
|
212
|
+
`interopVisibility` takes precedence over `maskWindowsDrive`.
|
|
189
213
|
- **`bubblewrap` must be installed**, or both providers fail immediately. The
|
|
190
214
|
usability probe re-runs after a failure, so installing bubblewrap while the app
|
|
191
215
|
runs is believed on the next command; only a success is cached. The refusal
|
|
@@ -299,3 +323,14 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
299
323
|
`unknown` activity, which never accumulates idle.
|
|
300
324
|
- Windows-folder host terminals (the `powershell.exe` branch) keep the shipped
|
|
301
325
|
behaviour: `unknown` activity, no reclamation.
|
|
326
|
+
|
|
327
|
+
## Host "Open in app"
|
|
328
|
+
|
|
329
|
+
- **Opening VS Code (or another Electron editor) from the host "Open in app"
|
|
330
|
+
menu can fail independently of this plugin.** The DSH Host process runs with
|
|
331
|
+
`ELECTRON_RUN_AS_NODE=1`. The open-in-app launcher inherits that variable, so
|
|
332
|
+
`Code.exe` starts as Node, exits 1, and the UI reports launch-failed. File
|
|
333
|
+
Explorer and other non-Electron launchers are unaffected; the failure is the
|
|
334
|
+
same on a Windows folder and on a WSL UNC workspace. This repository does
|
|
335
|
+
not own that spawn path. The record and the upstream fix are in
|
|
336
|
+
[UPSTREAM-OPEN-IN-APP.md](UPSTREAM-OPEN-IN-APP.md).
|
package/docs/MIGRATION.md
CHANGED
|
@@ -20,10 +20,10 @@ still move in a **minor**. Within that freedom:
|
|
|
20
20
|
- **Changing a key's meaning is a minor with a migration note**, because a profile
|
|
21
21
|
written for the old meaning keeps booting. The note must say what changed, how
|
|
22
22
|
to keep the old posture, and where the behavior differs.
|
|
23
|
-
- **Peer
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
- **Peer compatibility is ranged.** DSH peer dependencies declare a minimum;
|
|
24
|
+
development dependencies are pinned exactly. A new upstream peer line is
|
|
25
|
+
tested by the [peer canary](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/.github/workflows/peer-canary.yml); changes to
|
|
26
|
+
the tested baseline or supported range belong in the release note.
|
|
27
27
|
- **Tool names and error codes are API.** `FS_OUTSIDE_DISTRO`,
|
|
28
28
|
`SANDBOX_UNAVAILABLE`, `FS_STALE_VERSION` and the diagnostic scripts
|
|
29
29
|
(`doctor`, `diagnose`, `preset`, `bootstrap`) keep their names and contracts;
|
|
@@ -62,7 +62,9 @@ the old meaning keeps booting, but now behaves differently. The migration:
|
|
|
62
62
|
distro: default
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
`pnpm run preset --
|
|
65
|
+
`pnpm run preset --distro default` prints that block for manual merging.
|
|
66
|
+
`--out` replaces the whole destination file, including unrelated overrides;
|
|
67
|
+
see [CONFIGURATION.md](CONFIGURATION.md#configure-wsl-shell-and-wsl-fs). The CHANGELOG
|
|
66
68
|
entry for 0.12.0 states the change; this page is the durable copy. `doctor`
|
|
67
69
|
reports the effective distro mode in its host/distro rows, and `--dump-config`
|
|
68
70
|
shows the composed value.
|
|
@@ -76,21 +78,22 @@ shows the composed value.
|
|
|
76
78
|
| `pnpm run test:coverage` | lines 85 / branches 84 / functions 70 | CI (Node 24, Ubuntu) |
|
|
77
79
|
| Peer line tested | `peer-canary` workflow resolves `next` and runs the scratch upgrade | CI schedule/manual |
|
|
78
80
|
| Packed install/upgrade/uninstall | `pnpm run e2e:install` (temp `DSH_HOME`, 0.11.0 → current, marker) | manual, this machine (10/10 on 2026-10-19) |
|
|
79
|
-
| Real-machine probe suite | `test/probe/run-all-when-closed.sh` (needs the app closed) | manual;
|
|
81
|
+
| Real-machine probe suite | `test/probe/run-all-when-closed.sh --include-fs` (needs the app closed) | manual; 15 probes passed on this Windows/WSL machine on 2026-10-31 |
|
|
80
82
|
| Clean Win11 + Ubuntu first run ≤ 5 min | a clean machine, a stopwatch | **not run** (the project's machine is already configured) |
|
|
81
|
-
| aarch64 executed | the native suite
|
|
83
|
+
| aarch64 executed | the native 18-check suite under qemu in CI | **run in CI** (physical aarch64 WSL is out of scope; see COMPATIBILITY.md) |
|
|
82
84
|
| No open P0 bug for 30 days | issue tracker | time-based; cannot be asserted from a checkout |
|
|
83
85
|
|
|
84
|
-
The
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
The clean-machine first-run timing and the 30-day P0 window stay unrun until
|
|
87
|
+
someone measures them; aarch64 qemu is a CI gate. A release note must not
|
|
88
|
+
claim an unrun row, and [ROADMAP-1.0.md](ROADMAP-1.0.md#wsl-304--10-readiness)
|
|
89
|
+
tracks the 1.0 leftovers. A release that skips the remaining manual rows still
|
|
90
|
+
ships through the tag workflow, but its notes say which gates were executed.
|
|
88
91
|
|
|
89
92
|
## Verifying a migration locally
|
|
90
93
|
|
|
91
94
|
```bash
|
|
92
|
-
pnpm run preset --
|
|
93
|
-
pnpm run doctor --
|
|
95
|
+
pnpm run preset --distro default --check <profile>/cordis.patch.yml # no nested-row trap
|
|
96
|
+
pnpm run doctor --json # effective environment facts
|
|
94
97
|
dsh --profile <name> --dump-config # composed rows
|
|
95
98
|
```
|
|
96
99
|
|
package/docs/PARITY.md
CHANGED
|
@@ -5,9 +5,10 @@ against, and the phase checklist with each phase's current status. The plan
|
|
|
5
5
|
has landed; the rows below record where each phase ended up and what
|
|
6
6
|
deviation, if any, still stands.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
protocol v4, 2026-10-06.
|
|
10
|
-
|
|
8
|
+
Historical verification: `dsh-plugin-wsl-env@0.8.0` / peer `0.2.0-rc.2` / agent
|
|
9
|
+
protocol v4, 2026-10-06. These rows are not a fresh verification of the current
|
|
10
|
+
release or protocol; see [PEER-PARITY.md](PEER-PARITY.md) for the peer
|
|
11
|
+
comparison. The peer pin moved to `0.2.1-rc.2` on 2026-10-17 with every
|
|
11
12
|
seam package's shipped artifact byte-identical to the ones these rows were
|
|
12
13
|
measured against; the real-machine probes were not re-run in that move.
|
|
13
14
|
|
package/docs/PERFORMANCE.md
CHANGED
|
@@ -9,8 +9,8 @@ and how to reproduce them; it does not extrapolate beyond them.
|
|
|
9
9
|
|
|
10
10
|
| | 9p share (`\\wsl.localhost\...`) | This plugin |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| File I/O | host stack crosses the VM boundary per operation | resident agent serves reads/writes
|
|
13
|
-
| Change feed | polling the share | inotify
|
|
12
|
+
| File I/O | host stack crosses the VM boundary per operation | resident agent serves reads/writes inside the distro, on the target's backing filesystem |
|
|
13
|
+
| Change feed | polling the share | inotify for unbounded directories when available, polling otherwise |
|
|
14
14
|
| Commands | host process | distro login shell through `wsl.exe` |
|
|
15
15
|
|
|
16
16
|
The plugin's root filesystem routes every WSL path to the distro backend
|
|
@@ -26,7 +26,7 @@ Absolute numbers are machine-dependent; the ratio and the budgets are the point.
|
|
|
26
26
|
|
|
27
27
|
| Scenario | 9p share | Agent (ext4) | Source |
|
|
28
28
|
|---|---|---|---|
|
|
29
|
-
| 16 concurrent shared reads | 342–614 ms (queue up to 583 ms)
|
|
29
|
+
| 16 concurrent shared reads, shell agent vs native agent | — | shell: 342–614 ms (queue up to 583 ms); native: **2.4–4.1 ms** after A7, first start ~70 ms once | [ADR 0002 fs queue](adr/0002-native-agent.md#fs-queue-measurement-a6-2026-10-18) |
|
|
30
30
|
| Single FS op (host round trip) | — | p95 ≈ 22 ms | [ROADMAP-1.0.md](ROADMAP-1.0.md#7-metrics) |
|
|
31
31
|
| GUI-preview backend: `stat` | — | p95 **2.7 ms** | `pnpm run probe:gui-preview` (20 iterations, 2026-10-19) |
|
|
32
32
|
| GUI-preview backend: listing | — | p95 **5.1 ms** | same |
|
|
@@ -47,14 +47,16 @@ pnpm run probe:gui-preview # stat / listDir / 1 MiB page read, p50/p95
|
|
|
47
47
|
node test/probe/fs-queue.mjs # the agent queue, sequential vs concurrent, with per-op latency
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Both need a working distro and Node; neither boots the harness.
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
Both need WSL interop, a working distro and Node; neither boots the harness.
|
|
51
|
+
The queue measurement also needs systemd and a usable native binary; it prints
|
|
52
|
+
wall times and queue metrics but does not enforce latency budgets. The GUI-preview
|
|
53
|
+
probe reports p50/p95/max and fails when its budgets are exceeded.
|
|
53
54
|
|
|
54
55
|
## What is not measured
|
|
55
56
|
|
|
56
57
|
- End-to-end GUI paint time (the browser and the service's own work are out of
|
|
57
58
|
scope for these probes).
|
|
58
|
-
- 9p numbers for large files or many small files
|
|
59
|
-
|
|
60
|
-
- aarch64
|
|
59
|
+
- 9p numbers for large files or many small files; A6 compared shell and native
|
|
60
|
+
agents, not the Windows-side 9p share.
|
|
61
|
+
- Physical aarch64 WSL latency (the native 18-check suite runs under qemu in
|
|
62
|
+
CI; that is functional correctness, not a host-round-trip measurement).
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Quickstart
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Steps from a Windows + WSL 2 machine to a working WSL session.
|
|
4
4
|
This page is the entry point; every claim links to the document that owns it.
|
|
5
5
|
|
|
6
6
|
## 1. Prerequisites
|
|
@@ -11,10 +11,11 @@ This page is the entry point; every claim links to the document that owns it.
|
|
|
11
11
|
| A distro | one line in the list above | `wsl.exe --install -d Ubuntu` |
|
|
12
12
|
| `bubblewrap` | `wsl.exe -d <distro> -- which bwrap` | `wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap` |
|
|
13
13
|
| systemd + Linger (for the session runtime) | `node <plugin>/scripts/doctor.mjs` (installed) or `pnpm run doctor` (checkout) | `[boot] systemd=true` in `/etc/wsl.conf`, `wsl --shutdown`, then `loginctl enable-linger "$USER"`; or set `sessionSandbox: auto`/`false` |
|
|
14
|
-
| Node 22.19
|
|
14
|
+
| Node `^22.19.0` or `>=24.0.0` | only needed for source/development work | not needed inside the distro to use the published plugin |
|
|
15
15
|
|
|
16
|
-
The doctor (`scripts/doctor.mjs`, or `pnpm run doctor` from a checkout)
|
|
17
|
-
|
|
16
|
+
The doctor (`scripts/doctor.mjs`, or `pnpm run doctor` from a checkout) checks
|
|
17
|
+
the environment and prints remedies for detected failures. Confirm the distro's
|
|
18
|
+
WSL version separately with `wsl.exe -l -v`. Doctor is read-only unless you pass `--fix` (stale scopes) or
|
|
18
19
|
`--fix --install` (packages, after a confirmation).
|
|
19
20
|
|
|
20
21
|
## 2. Install
|
|
@@ -23,6 +24,8 @@ From a Windows PowerShell where the DSH CLI is available:
|
|
|
23
24
|
|
|
24
25
|
```powershell
|
|
25
26
|
dsh plugin --profile <profile> add dsh-plugin-wsl-env
|
|
27
|
+
# 若解析到旧版本(发布当天的元数据缓存),显式指定:
|
|
28
|
+
# dsh plugin --profile <profile> add dsh-plugin-wsl-env@<latest>
|
|
26
29
|
dsh --profile <profile> --dump-config # the plugin rows must be there
|
|
27
30
|
```
|
|
28
31
|
|
|
@@ -37,6 +40,23 @@ or an UNC path directly. The session's commands, file tools, terminal and previe
|
|
|
37
40
|
all run in that distro; a Windows folder keeps the normal Windows environment.
|
|
38
41
|
Two distros can be open at once, each with its own environment.
|
|
39
42
|
|
|
43
|
+
### Open the workspace in VS Code (Remote-WSL)
|
|
44
|
+
|
|
45
|
+
Opening the WSL folder as a Windows UNC share works but is the degraded mode:
|
|
46
|
+
files cross 9p, the terminal is PowerShell, extensions run on Windows. The full
|
|
47
|
+
integration is VS Code's Remote-WSL — the server runs inside the distro, the
|
|
48
|
+
integrated terminal is the distro shell, and file operations are local ext4:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
# from the plugin checkout:
|
|
52
|
+
scripts/open-wsl-vscode.ps1 -Path "\\wsl.localhost\ubuntu\home\me\proj"
|
|
53
|
+
# or by hand:
|
|
54
|
+
code --folder-uri "vscode-remote://wsl+ubuntu/home/me/proj"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Requires the `code` shim on PATH (VS Code installer adds it) and the
|
|
58
|
+
Remote-WSL extension.
|
|
59
|
+
|
|
40
60
|

|
|
41
61
|
|
|
42
62
|
The terminal panel runs in the same distro — `uname -a` answers with the
|
|
@@ -55,10 +75,13 @@ distro kernel. If the first command fails, the error names the remedy; the
|
|
|
55
75
|
[doctor](CONFIGURATION.md) and the [Troubleshooting table](../README.md#troubleshooting)
|
|
56
76
|
cover the common cases.
|
|
57
77
|
|
|
58
|
-
The
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
([`test/probe/sandbox.sh`](
|
|
78
|
+
The probe below runs the shipped kernel-enforced bwrap profiles live — writes
|
|
79
|
+
inside the workspace land, tested writes outside are refused with `EROFS`, and
|
|
80
|
+
the strict profile hides `/mnt` entirely
|
|
81
|
+
([`test/probe/sandbox.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/sandbox.sh)):
|
|
82
|
+
|
|
83
|
+
This is partial isolation, not a Windows security boundary: Windows interop
|
|
84
|
+
can still launch executables outside `/mnt`. See [LIMITATIONS.md](LIMITATIONS.md).
|
|
62
85
|
|
|
63
86
|

|
|
64
87
|
|
|
@@ -75,8 +98,9 @@ pnpm test
|
|
|
75
98
|
pnpm dev # a dev server survives the command that started it with sessionSandbox: true
|
|
76
99
|
```
|
|
77
100
|
|
|
78
|
-
The server's port
|
|
79
|
-
default); open
|
|
101
|
+
The server's port can appear in `DSH_WSL_PORTS` after a successful snapshot
|
|
102
|
+
(refreshed every 10 s by default); open a forwarded port from Windows at
|
|
103
|
+
`http://localhost:<port>`.
|
|
80
104
|
|
|
81
105
|
### Python
|
|
82
106
|
|
|
@@ -106,14 +130,16 @@ go build ./...
|
|
|
106
130
|
go test ./...
|
|
107
131
|
```
|
|
108
132
|
|
|
109
|
-
The module cache lives
|
|
133
|
+
The module cache normally lives under the distro user's `$GOPATH`; its actual
|
|
134
|
+
location and backing filesystem depend on the Go environment and mounts.
|
|
110
135
|
|
|
111
136
|
### Monorepo
|
|
112
137
|
|
|
113
138
|
Open the repository root as the workspace. Each package's commands run from the
|
|
114
139
|
same distro; `pnpm -r`, `turbo`, `nx` and `make` see one Linux filesystem. The
|
|
115
|
-
file watcher uses inotify
|
|
116
|
-
|
|
140
|
+
file watcher uses inotify for unbounded directories when `inotifywait` is
|
|
141
|
+
available; otherwise it falls back to polling. If a change is missed, see the
|
|
142
|
+
watcher bounds in [CONFIGURATION.md](CONFIGURATION.md).
|
|
117
143
|
|
|
118
144
|
## Compatibility
|
|
119
145
|
|
|
@@ -123,21 +149,20 @@ executed here.
|
|
|
123
149
|
|
|
124
150
|
| Tier | Configuration | Status |
|
|
125
151
|
|---|---|---|
|
|
126
|
-
| 1 | Ubuntu
|
|
127
|
-
| 2 |
|
|
152
|
+
| 1 | Ubuntu / Debian, systemd, x86_64 | Probed. Debian harness-level probes pass against a `wslfs-debian` profile (2026-10-26). |
|
|
153
|
+
| 2 | Fedora, openSUSE, Arch, Alpine; aarch64 native agent | Distro matrix in [COMPATIBILITY.md](COMPATIBILITY.md). aarch64 18-check runs under qemu in CI; physical aarch64 WSL is out of scope. |
|
|
128
154
|
| Unsupported | WSL 1, Linux/macOS hosts, Windows without WSL 2 | Not supported; the plugin attaches to `wsl.exe` |
|
|
129
155
|
|
|
130
156
|
With `sessionSandbox: auto` a distro without systemd/Linger still gets the
|
|
131
157
|
per-command `bubblewrap` sandbox and the result reports the narrower scope;
|
|
132
|
-
`true` refuses. See [CONFIGURATION.md](CONFIGURATION.md#
|
|
158
|
+
`true` refuses. See [CONFIGURATION.md](CONFIGURATION.md#sandbox-and-preflight) for
|
|
133
159
|
the sandbox trade-offs and [PERFORMANCE.md](PERFORMANCE.md) for the measured
|
|
134
160
|
latency.
|
|
135
161
|
|
|
136
162
|
## Demo
|
|
137
163
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
evidence in the meantime; the absence is recorded in
|
|
164
|
+
The screenshots above show the picker, terminal, preview and sandbox probe.
|
|
165
|
+
There is no GIF yet. The [Troubleshooting table](../README.md#troubleshooting)
|
|
166
|
+
and the probe output (`pnpm run probe:sandbox`, `pnpm run probe:gui-preview`)
|
|
167
|
+
provide reproducible evidence; the remaining demo work is tracked in
|
|
143
168
|
[ROADMAP-1.0.md](ROADMAP-1.0.md#wsl-302--discoverability-and-docs-entry).
|
package/docs/README.zh.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/dsh-plugin-wsl-env)
|
|
7
7
|
[](../LICENSE)
|
|
8
8
|
|
|
9
|
-
一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
|
|
9
|
+
一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件,在 Windows 的 WSL 2 发行版中打开项目工作区。
|
|
10
10
|
|
|
11
|
-
Harness 进程仍在 Windows
|
|
11
|
+
Harness 进程仍在 Windows 上。命令执行、shell 脚本和文件访问都在发行版的 Linux 空间内完成,以获得全套 Linux shell 工具链。发行版内不安装辅助服务或守护进程,也不开放端口,受限命令在 `bubblewrap` 中运行。
|
|
12
12
|
|
|
13
13
|
仅支持 Windows + WSL 2。
|
|
14
14
|
|
|
@@ -16,7 +16,7 @@ Harness 进程仍在 Windows 上。命令、文件操作、文件夹选择器和
|
|
|
16
16
|
|
|
17
17
|
Windows 上的编程工具要在 WSL 项目中工作,有三条路:把整个工具装进发行版、连接发行版内的辅助服务,或由插件直接提供发行版环境。本插件是第三条。两类会话共用一套流程,同时保留发行版的真实工具和命令沙箱。
|
|
18
18
|
|
|
19
|
-
- **真实的 Linux 环境。** 命令在发行版自己的登录 shell 中运行,`PATH`、`nvm`、`cargo`、`pyenv` 和 rc
|
|
19
|
+
- **真实的 Linux 环境。** 命令在发行版自己的登录 shell 中运行,`PATH`、`nvm`、`cargo`、`pyenv` 和 rc 文件都生效。文件工具通过发行版内的 agent 访问发行版路径,底层文件系统取决于路径。
|
|
20
20
|
- **发行版内没有服务。** 没有辅助服务、没有守护进程、没有监听端口。唯一持久化的内容是 `~/.cache/dsh-wsl/agent/` 下按内容寻址的原生 agent 副本。
|
|
21
21
|
- **默认有沙箱。** 受限命令在发行版内的 `bubblewrap` 中运行。缺少 `bubblewrap` 时命令直接失败,不会脱箱运行。
|
|
22
22
|
- **按会话生效。** 打开发行版文件夹的会话获得发行版环境;打开 Windows 文件夹的会话保留 Windows 工具。两者可同时运行在一个 harness 进程中。
|
|
@@ -72,7 +72,7 @@ dsh --profile wsl # 启动
|
|
|
72
72
|
wsl.exe -d <发行版> -u root -- apt-get install -y bubblewrap
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
在插件检出目录中,`pnpm run bootstrap
|
|
75
|
+
在插件检出目录中,`pnpm run bootstrap <发行版>` 检查四个发行版工具,并按包管理器打印安装命令;加 `--install` 仅在缺少 bubblewrap 时执行该命令。若只缺少其他工具,请自行执行打印的命令。
|
|
76
76
|
|
|
77
77
|
升级:再次执行 `add` 命令。卸载:`dsh plugin --profile wsl remove dsh-plugin-wsl-env`;之后可删除 `~/.cache/dsh-wsl`。如果你为了恢复预览在 profile 里加过 `workspace-files-wsl` 的临时覆盖,升级后请删掉:插件不再挂载该行。
|
|
78
78
|
|
|
@@ -135,11 +135,11 @@ wsl preset(每个会话)
|
|
|
135
135
|
| `workspace-write` | 上面的权限,加上会话工作区;会话 runtime 下 `/tmp` 是发行版真实 `/tmp` |
|
|
136
136
|
| `danger-full-access` | 不经过沙箱 |
|
|
137
137
|
|
|
138
|
-
设置 `sessionSandbox: false`
|
|
138
|
+
设置 `sessionSandbox: false` 时,`workspace-write` 命令使用私有的 `/tmp`,而不是发行版的真实 `/tmp`;`read-only` 不挂载可写的 `/tmp`。
|
|
139
139
|
|
|
140
140
|
`bubblewrap` 是必需项。缺失或损坏时,受限命令报 `SANDBOX_UNAVAILABLE`,不会运行。在对应的行设置 `sandbox: false` 可关闭沙箱;模型会收到"该操作无沙箱"的说明。
|
|
141
141
|
|
|
142
|
-
隔离上报为 `partial`。发行版内的命令仍可通过 `/mnt/c` 启动 Windows 程序,`bubblewrap` 无法控制 Windows 进程。`maskWindowsDrive: true` 让受限视图看不到 `/mnt
|
|
142
|
+
隔离上报为 `partial`。发行版内的命令仍可通过 `/mnt/c` 启动 Windows 程序,`bubblewrap` 无法控制 Windows 进程。`maskWindowsDrive: true` 让受限视图看不到 `/mnt`,但会被 `interopVisibility: visible` 覆盖;遮罩只能收窄这个洞,不能关死。关闭 WSL interop 需要另行在发行版的 `/etc/wsl.conf` 中设置 `[interop] enabled=false`。见 [docs/ARCHITECTURE.md](ARCHITECTURE.md#sandbox) 和 [SECURITY.md](../SECURITY.md)。
|
|
143
143
|
|
|
144
144
|
## 常见问题
|
|
145
145
|
|
|
@@ -147,7 +147,7 @@ wsl preset(每个会话)
|
|
|
147
147
|
|---|---|
|
|
148
148
|
| 每条命令都报 `SANDBOX_UNAVAILABLE` | 在发行版内安装 `bubblewrap`,或设置 `sandbox: false` |
|
|
149
149
|
| 会话在第一条命令前就被拒绝 | 启用 systemd 和 linger,或在 `wsl-shell`、`wsl-fs` 上设置 `sessionSandbox: false` |
|
|
150
|
-
| 发行版内 `glob`、`grep` 很慢 | 运行 `pnpm run bootstrap
|
|
150
|
+
| 发行版内 `glob`、`grep` 很慢 | 运行 `pnpm run bootstrap <发行版>`,使用打印的安装命令安装 `ripgrep` |
|
|
151
151
|
| `dsh plugin add` 没有输出 | 插件已安装;这是成功 |
|
|
152
152
|
| 改了 `lib/` 但不生效 | 运行 `pnpm run sync:windows`,然后重启应用 |
|
|
153
153
|
|