dsh-plugin-wsl-env 0.12.0 → 0.13.1
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 +195 -0
- package/CONTRIBUTING.md +28 -6
- package/README.md +13 -6
- package/SECURITY.md +11 -1
- package/SUPPORT.md +29 -2
- package/cordis.patch.yml +9 -2
- package/docs/ARCHITECTURE.md +7 -5
- package/docs/CONFIGURATION.md +75 -16
- package/docs/LIMITATIONS.md +47 -7
- package/docs/MIGRATION.md +98 -0
- package/docs/PARITY.md +1 -1
- package/docs/PERFORMANCE.md +60 -0
- package/docs/QUICKSTART.md +125 -0
- package/docs/README.zh.md +6 -4
- package/docs/ROADMAP-1.0.md +780 -0
- package/docs/ROADMAP.md +4 -0
- package/docs/UPSTREAM-FILE-REFERENCE-SEAM.md +80 -0
- package/docs/UPSTREAM-FSIO-EXPORT.md +3 -2
- package/docs/UPSTREAM-LSP-SEAM.md +90 -0
- package/docs/UPSTREAM-SESSION-NOTICE.md +95 -0
- package/lib/agent-errors.d.ts +19 -0
- package/lib/agent-errors.js +22 -0
- package/lib/agent-exec.d.ts +9 -2
- package/lib/agent-exec.js +4 -4
- package/lib/agent.d.ts +68 -5
- package/lib/agent.js +129 -22
- package/lib/distro-route.d.ts +75 -0
- package/lib/distro-route.js +86 -0
- package/lib/fs-routing.d.ts +4 -1
- package/lib/index.d.ts +166 -37
- package/lib/index.js +358 -74
- package/lib/preflight.d.ts +46 -3
- package/lib/preflight.js +90 -15
- package/lib/shell-env.d.ts +9 -3
- package/lib/shell-env.js +128 -43
- package/lib/subprocess.d.ts +12 -3
- package/lib/subprocess.js +21 -3
- package/package.json +23 -6
- package/scripts/diagnose.sh +9 -88
- package/scripts/doctor.mjs +548 -0
- package/scripts/profile-preset.mjs +254 -0
package/docs/CONFIGURATION.md
CHANGED
|
@@ -22,35 +22,35 @@ has its own table at the end of this page.
|
|
|
22
22
|
|
|
23
23
|
| Row | Key | Default | Meaning |
|
|
24
24
|
|---|---|---|---|
|
|
25
|
-
| `wsl-shell` | `distro` | `''` *(shipped)* | distro
|
|
25
|
+
| `wsl-shell` | `distro` | `''` *(shipped)* | distro mode: empty/`auto` follows the request workdir, `default` forces WSL's default, and a name pins every command |
|
|
26
26
|
| | `shell` | `''` | pin a shell inside the distro; empty means the user's login shell |
|
|
27
27
|
| | `loginShell` | `true` *(shipped)* | use `<shell> -lc`, which sources your profile, instead of a bare `-c`. Applies only to the POSIX family; a non-POSIX shell (fish, csh) still gets a bare `-c` |
|
|
28
28
|
| | `agent` | `true` | run commands over the resident in-distro agent (one long-lived process; in-distro timeout); the one-shot `wsl.exe` path is fallen back to only when the agent is out of service. Anything else — a cwd refusal, a sandbox refusal — is the command's own verdict and is raised, not retried on the other path |
|
|
29
|
-
| | `sessionSandbox` | `true` *(shipped)* | run this session's commands in one persistent systemd user scope (ADR 0001): a background dev server survives the command that started it, `/tmp` is the distro's real `/tmp` under `workspace-write`, and `ps` sees the session. The executor holds the runtime for the session and stops the scope from the preset's disposer, which reaps the cgroup. Requires `agent: true`, systemd as PID 1, a reachable user manager and `Linger` (`loginctl enable-linger <user>`); the preflight refuses with the remedy when systemd, the user manager, `systemd-run` or `Linger` is missing (a success is cached; a failure is re-probed, so following the remedy needs no app restart).
|
|
29
|
+
| | `sessionSandbox` | `true` *(shipped)* | `true`, `false`, or `auto`. With `true`, run this session's commands in one persistent systemd user scope (ADR 0001): a background dev server survives the command that started it, `/tmp` is the distro's real `/tmp` under `workspace-write`, and `ps` sees the session. The executor holds the runtime for the session and stops the scope from the preset's disposer, which reaps the cgroup. Requires `agent: true`, systemd as PID 1, a reachable user manager and `Linger` (`loginctl enable-linger <user>`); the preflight refuses with the remedy when systemd, the user manager, `systemd-run` or `Linger` is missing (a success is cached; a failure is re-probed, so following the remedy needs no app restart). `auto` uses the scope when those prerequisites answer, and otherwise falls back to the per-command `bwrap` profile — still confined, never unconfined — reporting `sessionScope: "per-command"` in the result (`"session"` when the scope ran); a failed `auto` verdict is re-probed at most once a minute. `false` is the per-command shape. On in the shipped preset |
|
|
30
30
|
| | `agentBackend` | `auto` | the session runtime's in-distro agent. `auto` uses the native agent when a verified source is available — the packaged per-arch static-musl binary at `agent/native/linux-<arch>/dsh-wsl-agent` (x86_64 and aarch64; runs on glibc and musl distros), or the `DSH_WSL_AGENT_NATIVE` development override — and otherwise the shipped shell agent; `native` fails the first command when it cannot prepare instead of silently falling back; `shell` forces the shell agent. Confined writes share the runtime, so they follow the same selection. The native backend is the default (static musl x86_64/aarch64); the shell agent is the fallback, and `native` refuses instead of falling back. This key covers the session runtime (commands and confined writes) and, since A7, the shared read agent (unconfined reads), which also follows this selection with the shell fallback |
|
|
31
31
|
| | `sandbox` | `true` | confine commands with `bubblewrap`; `false` disables the sandbox. Needs a usable `bwrap` inside the distro and fails closed without one (`SANDBOX_UNAVAILABLE`, never an unconfined run) — see [Turning the sandbox off](#turning-the-sandbox-off) |
|
|
32
32
|
| | `maskWindowsDrive` | `false` | shadow `/mnt` with an empty tmpfs in every confined profile: the Windows drive's files cannot be read or exfiltrated and its executables cannot be launched through interop — the hole that lets a "sandboxed" command reach your full Windows token. A narrowing, not a closure (a command can still write an executable into the workspace and run it — binfmt dispatches on content, not location), so enforcement stays `partial` and the result's sandbox fact reports `windowsDrive: "masked"`. The complete closure is distro-level: `[interop] enabled=false` in `wsl.conf` |
|
|
33
33
|
| | `cwd` | `''` | default working directory; empty means the distro user's home |
|
|
34
34
|
| | `timeoutMs` / `maxTimeoutMs` | `120000` / `600000` *(shipped)* | per-call time limit, and the ceiling a call may ask for |
|
|
35
|
-
| `wsl-fs` | `distro` | `''` *(shipped)* |
|
|
36
|
-
| | `restrictToDistro` | `true` *(shipped)* | refuse a path in **another distro**'s share.
|
|
35
|
+
| `wsl-fs` | `distro` | `''` *(shipped)* | distro mode: empty/`auto` follows each target path, `default` forces WSL's default, and a name pins the filesystem |
|
|
36
|
+
| | `restrictToDistro` | `true` *(shipped)* | under a fixed `distro` (`default` or a name), refuse a path in **another distro**'s share (`FS_OUTSIDE_DISTRO`). Under `auto`, a UNC target is served by its own distro, so the fence has nothing foreign to refuse. `/mnt/c` is a directory *inside* the selected distro and is not affected |
|
|
37
37
|
| | `sandbox` | `true` | check `writeText` and `editText` against the policy. Needs a usable `bwrap` in the distro too, and fails closed the same way: a distro without one refuses confined mutations (`FS_IO_ERROR`, with the install guidance) rather than downgrading them |
|
|
38
38
|
| | `maskWindowsDrive` | `false` | as on `wsl-shell`: confined fs mutations publish through a resident whose profile shadows `/mnt` (same honest limits) |
|
|
39
|
-
| | `sessionSandbox` | `true` *(shipped)* | run confined mutations in the same session runtime the shell uses for the same `(distro, workspace, policy)` — one agent, one real `/tmp`, one workspace mount view; requires the same systemd/linger setup and the preflight refuses with the remedy. Set `false` for the per-policy confined resident. The app-level `fs-routing` backend is not session-owned and keeps this off |
|
|
39
|
+
| | `sessionSandbox` | `true` *(shipped)* | `true`, `false`, or `auto`. With `true`, run confined mutations in the same session runtime the shell uses for the same `(distro, workspace, policy)` — one agent, one real `/tmp`, one workspace mount view; requires the same systemd/linger setup and the preflight refuses with the remedy. `auto` uses the runtime when the prerequisites answer and otherwise falls back to the per-policy confined resident (still kernel-confined). Set `false` for the per-policy confined resident. The app-level `fs-routing` backend is not session-owned and keeps this off |
|
|
40
40
|
| | `substrate` | `agent` *(shipped)* | which I/O substrate serves the file tools. Only the resident in-distro agent (`lib/fs-substrate.js`) exists: reads, writes and identities run on ext4 — native symlinks, native mode bits, and the mutation guard survives to the write's atomic publication, kernel-enforced by the confined resident when the policy is confined. The former `share` opt-out (the Windows-side host stack over the 9p share) is refused at boot; delete the line. The comment shipped beside this key in `cordis.patch.yml` still calls `share` the opt-out — that comment is out of date, and the boot refusal is what runs |
|
|
41
41
|
| | `cwd` | `''` | base directory for relative paths; empty means the distro user's home |
|
|
42
42
|
| | `watchMaxDepth` | `0` | how deep the in-distro watch scan walks; `0` walks the whole tree and enables the inotify backend. A positive value keeps the polling scan (inotify cannot bound its walk); a change below the bound is seen only when another change above it fires the same callback |
|
|
43
|
-
| `fs-routing` | `distro` | `''` *(shipped)* |
|
|
43
|
+
| `fs-routing` | `distro` | `''` *(shipped)* | distro mode for the root-plane filesystem: empty/`auto` follows each target's UNC distro, `default` forces WSL's default, and a name pins. **A row of its own**, with its own copy of the distro knobs — changing `wsl-fs` does not change this one, nor the other way round |
|
|
44
44
|
| | `wslPath` | `wsl.exe` *(shipped)* | path to `wsl.exe`; used here only to resolve the default distro |
|
|
45
45
|
| | `sandbox` | `true` *(shipped)* | fence routed mutations by `ctx.sandboxPolicy`; same `bwrap` requirement and fail-closed refusal as `wsl-fs`. The routing row reports no `sandboxMode` — `undefined` whether the fence is on or off |
|
|
46
46
|
| | `maskWindowsDrive` | `false` *(shipped)* | as on `wsl-shell`, for routed mutations |
|
|
47
|
-
| | `restrictToDistro` | `true` *(shipped)* | refuse a path in another distro's share (`FS_OUTSIDE_DISTRO`) |
|
|
47
|
+
| | `restrictToDistro` | `true` *(shipped)* | under a fixed `distro` (`default` or a name), refuse a path in another distro's share (`FS_OUTSIDE_DISTRO`); under `auto`, each UNC target is served by its own distro |
|
|
48
48
|
| | `watchMaxDepth` | `0` *(shipped)* | how deep the routed watch scan walks; `0` walks the whole tree and enables the inotify backend; a positive value keeps the polling scan |
|
|
49
49
|
| | `distroCwd` | `''` | the routed backend's default workdir; empty means the distro user's home. `cwd` belongs to the host backend only — the distro side reads `distroCwd` — while `diffBasisMaxBytes` is shared: it bounds the overwrite diff on both sides |
|
|
50
50
|
| `directory-picker-wsl` | `preferredDistro` | `''` *(shipped)* | the distro listed first in the picker |
|
|
51
51
|
| | `includeHostHome` | `true` *(shipped)* | also list the Windows home directory |
|
|
52
52
|
| | `maxEntries` | `1000` *(shipped)* | maximum entries listed per directory |
|
|
53
|
-
| `subprocess-wsl` | `distro` | `''` *(shipped)* |
|
|
53
|
+
| `subprocess-wsl` | `distro` | `''` *(shipped)* | distro mode for the GUI terminal: empty/`auto` follows the launch directory, `default` forces WSL's default, and a name pins |
|
|
54
54
|
| | `shell` | `''` | pin a shell; empty lets `wsl.exe` decide |
|
|
55
55
|
| | `loginShell` | `true` | whether a pinned shell uses login semantics |
|
|
56
56
|
| | `hostSessions` | `true` | give a session opened on a Windows folder a host `powershell.exe` in its own directory instead of the distro shell at `/mnt/<drive>/…`; `false` restores the composition-owned distro terminal for every session |
|
|
@@ -83,6 +83,21 @@ changes nothing and does not fail: the log carries
|
|
|
83
83
|
`patch: entry "wsl-shell" not found` and `patch: entry "wsl-fs" not found`, and the
|
|
84
84
|
composed config keeps the shipped values.
|
|
85
85
|
|
|
86
|
+
The helper does the extraction and the common edits for you:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pnpm run preset -- --distro Ubuntu --session-sandbox false \
|
|
90
|
+
--out "$DSH_HOME/profiles/<name>/cordis.patch.yml"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`--distro`, `--session-sandbox`, `--sandbox`, `--mask-windows-drive` and
|
|
94
|
+
`--agent-backend` are the supported edits; without `--out` the helper prints the
|
|
95
|
+
complete block instead of writing it. `pnpm run preset -- --check <file>` reads an
|
|
96
|
+
existing layer and **fails with the exact block to paste** when it finds a
|
|
97
|
+
top-level `- id: wsl-shell`/`wsl-fs` entry — the silent no-op is caught before
|
|
98
|
+
boot. The generated block is byte-for-byte the shipped row plus your edits, so a
|
|
99
|
+
plugin upgrade only needs a re-run.
|
|
100
|
+
|
|
86
101
|
The route that works is to override the `preset-wsl` row as a whole. A row's
|
|
87
102
|
`config` is replaced whole, never deep-merged, so the replacement has to carry
|
|
88
103
|
`id`, `order` and the entire `plugins` list — every plugin, the `wsl-env` group
|
|
@@ -164,9 +179,43 @@ is out of service and the call rides the one-shot `wsl.exe` path.
|
|
|
164
179
|
|
|
165
180
|
## Which distro is used
|
|
166
181
|
|
|
167
|
-
`distro: ''`
|
|
168
|
-
|
|
169
|
-
default
|
|
182
|
+
`distro: ''` (or `distro: 'auto'`) follows the workspace: opening
|
|
183
|
+
`\\wsl.localhost\Debian\...` runs there even when Ubuntu is WSL's default.
|
|
184
|
+
`distro: 'default'` forces WSL's default instead, and a fixed distro (`default` or
|
|
185
|
+
a name) pins every session to that distro — a foreign UNC then refuses with
|
|
186
|
+
`FS_OUTSIDE_DISTRO` while `restrictToDistro` holds. A single-distro installation
|
|
187
|
+
needs no configuration.
|
|
188
|
+
|
|
189
|
+
## The strict profile
|
|
190
|
+
|
|
191
|
+
`maskWindowsDrive: true` with `sandbox: true` is the strict profile: every
|
|
192
|
+
confined profile shadows `/mnt` with an empty tmpfs, so a confined command can
|
|
193
|
+
neither read the Windows drive nor launch a Windows executable from it. Write it
|
|
194
|
+
into a profile with the preset helper (the rows are nested inside `preset-wsl`):
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
pnpm run preset -- --sandbox true --mask-windows-drive true \
|
|
198
|
+
--out "$DSH_HOME/profiles/<name>/cordis.patch.yml"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The trade-offs are real: confined commands lose `/mnt/c` entirely — no reading
|
|
202
|
+
Windows files, no running Windows tools, no dev server that expects a Windows
|
|
203
|
+
path. It is also a narrowing, not a closure: a command can still write a Windows
|
|
204
|
+
executable into the workspace and run it there, because WSL's `binfmt_misc`
|
|
205
|
+
dispatches on content, not location. Enforcement therefore stays `partial`, and
|
|
206
|
+
the result reports `windowsDrive: "masked"`. The only complete closure is
|
|
207
|
+
distro-level: `[interop] enabled=false` in `/etc/wsl.conf`, then
|
|
208
|
+
`wsl.exe --shutdown`, which removes interop from the distro entirely.
|
|
209
|
+
|
|
210
|
+
| Interop state | How | `/mnt` | Windows exe from `/mnt` | Workspace-written `.exe` | Evidence |
|
|
211
|
+
|---|---|---|---|---|---|
|
|
212
|
+
| visible (default) | — | readable | runs, and escapes the Linux sandbox | runs | `pnpm run probe:sandbox` — INFO "interop escape is open" |
|
|
213
|
+
| masked (strict) | `maskWindowsDrive: true` | empty tmpfs | unreachable | still runs (binfmt) | `pnpm run probe:sandbox` — the strict-profile checks |
|
|
214
|
+
| disabled | `[interop] enabled=false`, `wsl --shutdown` | no Windows mounts | n/a | fails (no binfmt) | documented; not probed automatically (needs a distro-level change) |
|
|
215
|
+
|
|
216
|
+
The masked and visible legs are asserted on the real machine by
|
|
217
|
+
`test/probe/sandbox.sh`; the disabled leg needs a distro configuration change and
|
|
218
|
+
a WSL shutdown, so it stays a manual check recorded here.
|
|
170
219
|
|
|
171
220
|
## Turning the sandbox off
|
|
172
221
|
|
|
@@ -195,8 +244,18 @@ is missing.
|
|
|
195
244
|
|
|
196
245
|
The same probe also runs when a session opens: the `wsl-preflight` row warns
|
|
197
246
|
with the full remedy if `bwrap` is unusable, so the news arrives with the
|
|
198
|
-
session rather than with the first failed command. It
|
|
199
|
-
|
|
247
|
+
session rather than with the first failed command. It also checks the session
|
|
248
|
+
runtime's prerequisites — systemd as PID 1, the user manager, `systemd-run` and
|
|
249
|
+
Linger — when `sessionSandbox` on the preflight row is `true` (the default,
|
|
250
|
+
matching the shipped preset). Set `sessionSandbox: false` on that row when the
|
|
251
|
+
profile opts out of the session runtime, so it does not warn about a
|
|
252
|
+
prerequisite the profile does not use.
|
|
253
|
+
|
|
254
|
+
The advisory is logged and also published as a `wsl-preflight` runtime-context
|
|
255
|
+
entry, so it reaches the session transcript; DSH 0.2.1 has no toast/banner seam
|
|
256
|
+
for a plugin (see [UPSTREAM-SESSION-NOTICE.md](UPSTREAM-SESSION-NOTICE.md) and
|
|
257
|
+
[LIMITATIONS.md](LIMITATIONS.md#session-start-advisories)). It is advisory — it
|
|
258
|
+
never installs anything and never changes the fail-closed semantics; drop the
|
|
200
259
|
`wsl-preflight` row from your layer (or set `sandbox: false` on it) to silence
|
|
201
260
|
the warning.
|
|
202
261
|
|
|
@@ -218,8 +277,8 @@ When something refuses, the message names the row or the code:
|
|
|
218
277
|
| a command never starts | a `WSL_E_*` code naming the distro | start it with `wsl.exe -d <distro>`, or point `distro` at another one — wider permissions do not help |
|
|
219
278
|
| `wsl.exe` itself cannot be started | the error names the executable and says to check `wslPath` on this row | fix `wslPath`, and check WSL is installed |
|
|
220
279
|
| WSL wedges mid-command | no completion within the deadline | retry once, then `wsl.exe --shutdown` and reopen |
|
|
221
|
-
| a watched path never fires | `FS_IO_ERROR` from `watch` | watch a path inside the
|
|
222
|
-
| a path in another distro is refused | `FS_OUTSIDE_DISTRO` |
|
|
280
|
+
| a watched path never fires | `FS_IO_ERROR` from `watch` | watch a path inside the selected distro |
|
|
281
|
+
| a path in another distro is refused | `FS_OUTSIDE_DISTRO` | only when `distro` is pinned; set `distro: ''`/`auto` to follow the workspace, or open a session in that distro |
|
|
223
282
|
|
|
224
283
|
The full code table — what each code means and what clears it — is
|
|
225
284
|
[ARCHITECTURE.md § Error codes](ARCHITECTURE.md#error-codes); the design boundaries
|
|
@@ -229,5 +288,5 @@ behind the fail-closed choices above are in [LIMITATIONS.md](LIMITATIONS.md).
|
|
|
229
288
|
|
|
230
289
|
| Key | Default | Meaning |
|
|
231
290
|
| --- | --- | --- |
|
|
232
|
-
| `distro` | `''` |
|
|
291
|
+
| `distro` | `''` | distro mode for the `DSH_WSL_*` facts: empty/`auto` follows each session's workspace, `default` forces WSL's default, and a name pins |
|
|
233
292
|
| `portsRefreshMs` | `10000` | how often the `DSH_WSL_PORTS` listening-port snapshot refreshes through the resident agent. A failed scan keeps the last successful list (and a debug line), so a transient agent outage does not read as “no ports”; only a successful empty scan means none were listening |
|
package/docs/LIMITATIONS.md
CHANGED
|
@@ -18,8 +18,10 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
18
18
|
Overwrites and edits keep the original bits. Run `chmod` (or ask the model to)
|
|
19
19
|
when a new file must be executable.
|
|
20
20
|
- **When the agent is out, file operations fail closed.** The distro stopped, or
|
|
21
|
-
the resident crashed
|
|
22
|
-
with the recovery path in the message.
|
|
21
|
+
the resident crashed through its revive budget: `FS_IO_ERROR` naming the
|
|
22
|
+
substrate, with the recovery path in the message. A single crash rides the
|
|
23
|
+
one rebuild (read-only ops only); a spent rebuild enters the bounded revival
|
|
24
|
+
(2/4/8 s), and three deaths make the state permanent with the restart remedy. There is no fallback I/O to degrade
|
|
23
25
|
into — deliberately; the old failure message pointed at the share, and the
|
|
24
26
|
share is what this plugin exists to keep the model off.
|
|
25
27
|
- **The mutation guard survives to the write.** `createIfAbsent` publishes with
|
|
@@ -125,7 +127,13 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
125
127
|
through the pipe before cutting. Expect the GUI file tree and previews to be
|
|
126
128
|
slower than a direct agent call, especially on large files; the model's own
|
|
127
129
|
file operations are unaffected — they use the preset filesystem and the
|
|
128
|
-
resident agent directly.
|
|
130
|
+
resident agent directly. Measured 2026-10-19 with
|
|
131
|
+
[`test/probe/gui-preview.sh`](../test/probe/gui-preview.sh) (20 iterations on
|
|
132
|
+
this machine): the routed backend's `stat` p95 2.7 ms, `listDir` p95 5.1 ms,
|
|
133
|
+
and a 1 MiB `readByteRange` p95 **63.5 ms/MiB** — the service's own round
|
|
134
|
+
trips sit on top of those, and the 1 s/MiB budget has a wide margin. No
|
|
135
|
+
optimization is justified by these numbers; revisit only with a measured
|
|
136
|
+
regression.
|
|
129
137
|
|
|
130
138
|
- **`fs-routing` is the seam.** The shipped service depends on the routed root
|
|
131
139
|
filesystem staying enabled and faithful to the `ctx.fs` contract (`resolve`,
|
|
@@ -193,15 +201,47 @@ crosses the 9p share. What to know about the one substrate there is:
|
|
|
193
201
|
a separate case and is now reported as an error naming that directory, because
|
|
194
202
|
`wsl.exe` would otherwise run the command in `/` and exit 0.
|
|
195
203
|
|
|
204
|
+
## Agent diagnostics
|
|
205
|
+
|
|
206
|
+
- **The doctor's `agent.handshake` check probes a fresh resident, not the live
|
|
207
|
+
one.** It starts `agent/wsl-agent.sh` in the distro, reads its HELLO (pid,
|
|
208
|
+
protocol, script digest) and stops it, so it proves that the agent path can
|
|
209
|
+
start and that the file the distro reads is the shipped one. It cannot read
|
|
210
|
+
the running app resident's last error — there is no IPC channel from a plugin
|
|
211
|
+
script to the app process — so that error stays in the app log. In-process,
|
|
212
|
+
`WslAgent.status()` exposes state, pid, lastError, deaths and pending.
|
|
213
|
+
- **`dsh-wsl-*.scope` counts are point-in-time.** The doctor lists and inspects
|
|
214
|
+
the user scopes once; a scope created or stopped a moment later is not
|
|
215
|
+
reflected. `--fix` runs the same sweep the session preflight uses.
|
|
216
|
+
|
|
217
|
+
## Session-start advisories
|
|
218
|
+
|
|
219
|
+
- **There is no session-UI notice seam in DSH 0.2.1.** When a distro session
|
|
220
|
+
opens, `wsl-preflight` checks `bubblewrap`, systemd as PID 1, the user
|
|
221
|
+
manager, `systemd-run` and Linger. A failure is advisory: it never installs
|
|
222
|
+
anything, never cancels the session and never changes the fail-closed
|
|
223
|
+
behavior of the first command. The finding goes to the host log
|
|
224
|
+
(`logger.warn`) and into the session's runtime context as a `wsl-preflight`
|
|
225
|
+
entry — model-visible on the next assembly — but the GUI today has no
|
|
226
|
+
toast/banner service a plugin can publish to. `pnpm run doctor` is the
|
|
227
|
+
deterministic human-facing check; the exact upstream ask is
|
|
228
|
+
[UPSTREAM-SESSION-NOTICE.md](UPSTREAM-SESSION-NOTICE.md).
|
|
229
|
+
- **The runtime-context entry is a repeated snapshot, not a dismissible
|
|
230
|
+
notification.** It appears in every assembly until the environment is fixed;
|
|
231
|
+
it has no severity decoration and no dismiss control. Removing the
|
|
232
|
+
`wsl-preflight` row (or setting `sandbox: false` on it) silences the probe
|
|
233
|
+
entirely.
|
|
234
|
+
|
|
196
235
|
## Ports
|
|
197
236
|
|
|
198
237
|
- **The port poller keeps the resident agent permanently warm.** The
|
|
199
238
|
`DSH_WSL_PORTS` snapshot refreshes through the shared resident agent every
|
|
200
239
|
`portsRefreshMs` (10 s by default, well inside the agent's idle timeout), so
|
|
201
|
-
while
|
|
202
|
-
in-distro agent process stay resident per
|
|
203
|
-
in flight.
|
|
204
|
-
|
|
240
|
+
while a distro's session is active the agent never idles out: one `wsl.exe`
|
|
241
|
+
and one in-distro agent process stay resident per active distro even with no
|
|
242
|
+
command in flight. The poller starts only for a distro a session actually
|
|
243
|
+
uses; raising `portsRefreshMs` past the agent's idle timeout restores idle
|
|
244
|
+
shutdown, at the cost of staler port facts.
|
|
205
245
|
|
|
206
246
|
## The directory picker
|
|
207
247
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Configuration stability and migration
|
|
2
|
+
|
|
3
|
+
What a release may change, how a breaking configuration change is announced and
|
|
4
|
+
carried, and which gates a release must clear. The version table in
|
|
5
|
+
[RELEASING.md](RELEASING.md#1-decide-the-version) is the source for the bump; this
|
|
6
|
+
page is the contract those bumps follow.
|
|
7
|
+
|
|
8
|
+
## The freeze below 1.0
|
|
9
|
+
|
|
10
|
+
The package is below `1.0.0`, so the on-disk profile format and the peer pins may
|
|
11
|
+
still move in a **minor**. Within that freedom:
|
|
12
|
+
|
|
13
|
+
- **Adding a configuration key is a minor.** The key must have a schema default
|
|
14
|
+
that preserves the previous behavior; a key that changes behavior by existing
|
|
15
|
+
is a major-shaped change and waits.
|
|
16
|
+
- **Removing or renaming a key is a major** *or* a minor with a boot-time refusal
|
|
17
|
+
that names the migration (see below). Silent acceptance of a retired key is
|
|
18
|
+
never allowed: it is the failure the `preset-wsl` trap and the `substrate`
|
|
19
|
+
refusal exist to prevent.
|
|
20
|
+
- **Changing a key's meaning is a minor with a migration note**, because a profile
|
|
21
|
+
written for the old meaning keeps booting. The note must say what changed, how
|
|
22
|
+
to keep the old posture, and where the behavior differs.
|
|
23
|
+
- **Peer pins are exact.** A new upstream peer line is tested by the
|
|
24
|
+
[peer canary](../.github/workflows/peer-canary.yml) and lands as a minor with
|
|
25
|
+
the delta in the release note (or as a patch if the plugin code needs no
|
|
26
|
+
change).
|
|
27
|
+
- **Tool names and error codes are API.** `FS_OUTSIDE_DISTRO`,
|
|
28
|
+
`SANDBOX_UNAVAILABLE`, `FS_STALE_VERSION` and the diagnostic scripts
|
|
29
|
+
(`doctor`, `diagnose`, `preset`, `bootstrap`) keep their names and contracts;
|
|
30
|
+
a rename follows the deprecation path below.
|
|
31
|
+
|
|
32
|
+
## Deprecating a configuration value
|
|
33
|
+
|
|
34
|
+
1. **Announce it in the release that introduces the deprecation**: a CHANGELOG
|
|
35
|
+
entry and the row in [CONFIGURATION.md](CONFIGURATION.md). The old value keeps
|
|
36
|
+
working for that release.
|
|
37
|
+
2. **Keep the old spelling for one minor** when it is unambiguous. When keeping
|
|
38
|
+
it would silently change behavior, refuse it at boot instead and print the
|
|
39
|
+
migration — the `substrate: "share"` case: the value is refused with the
|
|
40
|
+
message that says to delete the line, because serving the share is exactly
|
|
41
|
+
what the plugin must not do.
|
|
42
|
+
3. **Remove it in the next minor** with another CHANGELOG entry. The release note
|
|
43
|
+
names the version where the refusal appeared.
|
|
44
|
+
|
|
45
|
+
### Case study: `distro: ''` (0.12.0)
|
|
46
|
+
|
|
47
|
+
`distro: ''` used to mean "WSL's default distro". 0.12.0 made the empty value
|
|
48
|
+
mean **auto**: follow the request workdir's UNC distro. A profile that relied on
|
|
49
|
+
the old meaning keeps booting, but now behaves differently. The migration:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
# keep the old posture: every command in WSL's default distro
|
|
53
|
+
- id: preset-wsl
|
|
54
|
+
config:
|
|
55
|
+
plugins:
|
|
56
|
+
# … full block; set on both rows …
|
|
57
|
+
- id: wsl-shell
|
|
58
|
+
config:
|
|
59
|
+
distro: default
|
|
60
|
+
- id: wsl-fs
|
|
61
|
+
config:
|
|
62
|
+
distro: default
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`pnpm run preset -- --distro default --out …` writes that block. The CHANGELOG
|
|
66
|
+
entry for 0.12.0 states the change; this page is the durable copy. `doctor`
|
|
67
|
+
reports the effective distro mode in its host/distro rows, and `--dump-config`
|
|
68
|
+
shows the composed value.
|
|
69
|
+
|
|
70
|
+
## Gates a release must clear
|
|
71
|
+
|
|
72
|
+
| Gate | How | Runs where |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| Config docs and CHANGELOG current | review the diff; `lint:links` covers cross-references | CI |
|
|
75
|
+
| `pnpm test` | style, links, syntax, build freshness, tsc, 40 unit files | CI (Node 22/24, Ubuntu + Windows) |
|
|
76
|
+
| `pnpm run test:coverage` | lines 85 / branches 84 / functions 70 | CI (Node 24, Ubuntu) |
|
|
77
|
+
| Peer line tested | `peer-canary` workflow resolves `next` and runs the scratch upgrade | CI schedule/manual |
|
|
78
|
+
| 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; **not run in this environment** since the last batch's targeted probes |
|
|
80
|
+
| 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 on aarch64 WSL or qemu | **not run** (CI builds the binary only) |
|
|
82
|
+
| No open P0 bug for 30 days | issue tracker | time-based; cannot be asserted from a checkout |
|
|
83
|
+
|
|
84
|
+
The last four rows are deliberately marked as unrun rather than assumed: a
|
|
85
|
+
release note must not claim them, and [ROADMAP-1.0.md](ROADMAP-1.0.md#wsl-304--10-readiness)
|
|
86
|
+
tracks them. A release that skips the manual rows still ships through the tag
|
|
87
|
+
workflow, but its notes say which gates were executed.
|
|
88
|
+
|
|
89
|
+
## Verifying a migration locally
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pnpm run preset -- --distro default --check <profile>/cordis.patch.yml # no nested-row trap
|
|
93
|
+
pnpm run doctor -- --json # effective environment facts
|
|
94
|
+
dsh --profile <name> --dump-config # composed rows
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
A profile that fails to boot after a migration should name the remedy in the
|
|
98
|
+
error; if it does not, that is a bug — report it with the `doctor --json` output.
|
package/docs/PARITY.md
CHANGED
|
@@ -83,7 +83,7 @@ shaped a phase of the plan.
|
|
|
83
83
|
| --- | --- | --- | --- |
|
|
84
84
|
| 0 | landed | This document; contract audit above | baseline recorded |
|
|
85
85
|
| 1 | landed | `lib/agent.js` (with `lib/agent-protocol.js`, `lib/agent-shared.js`) and `agent/wsl-agent.sh`: resident process speaking a line protocol (pipe-separated fields, single-line base64 payloads), version + script-digest handshake, lazy start, idle shutdown, crash-rebuild-once, one-shot `wsl.exe` fallback for execution | resident server |
|
|
86
|
-
| 2 | landed | real `watch()` shipped (
|
|
86
|
+
| 2 | landed | real `watch()` shipped: inotify for an unbounded directory since 0.10.0 (polling fallback for a file target, a bounded depth or a distro without `inotify-tools`); the native-ext4 migration of the remaining fs operations finished with the share's retirement — every model-facing file operation now runs on the agent, and the share-era workarounds went with it | native file semantics, change invalidation — inotify primary, polling fallback; blind spots in LIMITATIONS and CONFIGURATION |
|
|
87
87
|
| 3 | landed (deviation: no streaming, no spill files) | `WslShellExecutor` exec via agent; in-distro TERM→KILL; handle shape preserved | native exec latency |
|
|
88
88
|
| 4 | landed | `npm run bootstrap`, family-aware install command, `SANDBOX_UNAVAILABLE` names it; the handshake ships the protocol version plus the script digest | open-and-it-works |
|
|
89
89
|
| 5 | landed | per-session routing by workspace coordinates (`terminal-route.js`, `hostSessions`); idle distro terminals are reclaimed by a distro-side scan (`lib/terminal-activity.js`) — the host-side shell-activity integration still does not arm for `wsl.exe`, which is why the observation is a scan | terminal per window/session |
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Performance: ext4 agent vs the 9p share
|
|
2
|
+
|
|
3
|
+
The plugin's reason to exist is not only correctness: a WSL workspace served
|
|
4
|
+
through the Windows 9p share is measurably slower than the same files read on
|
|
5
|
+
the distro's ext4 through the resident agent. This page records the measurements
|
|
6
|
+
and how to reproduce them; it does not extrapolate beyond them.
|
|
7
|
+
|
|
8
|
+
## The two substrates
|
|
9
|
+
|
|
10
|
+
| | 9p share (`\\wsl.localhost\...`) | This plugin |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| File I/O | host stack crosses the VM boundary per operation | resident agent serves reads/writes on ext4 inside the distro |
|
|
13
|
+
| Change feed | polling the share | inotify inside the distro (`watchMaxDepth: 0`) |
|
|
14
|
+
| Commands | host process | distro login shell through `wsl.exe` |
|
|
15
|
+
|
|
16
|
+
The plugin's root filesystem routes every WSL path to the distro backend
|
|
17
|
+
(`fs-routing`), so the GUI file tree, previews and the model's file tools all
|
|
18
|
+
use the agent path. The design and its trade-offs are in
|
|
19
|
+
[root-fs-routing.md](root-fs-routing.md); the known limits are in
|
|
20
|
+
[LIMITATIONS.md](LIMITATIONS.md).
|
|
21
|
+
|
|
22
|
+
## Measured numbers
|
|
23
|
+
|
|
24
|
+
Machine: Windows 11 + WSL 2, Ubuntu, x86_64, the project's development machine.
|
|
25
|
+
Absolute numbers are machine-dependent; the ratio and the budgets are the point.
|
|
26
|
+
|
|
27
|
+
| Scenario | 9p share | Agent (ext4) | Source |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| 16 concurrent shared reads | 342–614 ms (queue up to 583 ms) | **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
|
+
| Single FS op (host round trip) | — | p95 ≈ 22 ms | [ROADMAP-1.0.md](ROADMAP-1.0.md#7-metrics) |
|
|
31
|
+
| GUI-preview backend: `stat` | — | p95 **2.7 ms** | `pnpm run probe:gui-preview` (20 iterations, 2026-10-19) |
|
|
32
|
+
| GUI-preview backend: listing | — | p95 **5.1 ms** | same |
|
|
33
|
+
| GUI-preview backend: 1 MiB page | — | p95 **63.5 ms/MiB** | same |
|
|
34
|
+
|
|
35
|
+
The GUI-preview number covers the routed backend operations a preview pays.
|
|
36
|
+
The shipped `workspace-files` service adds its own calls on top; that service
|
|
37
|
+
shape stays qualitative in [LIMITATIONS.md](LIMITATIONS.md). The 1 s/MiB budget
|
|
38
|
+
has a wide margin, so no further optimization (the deferred v6 FS concurrency)
|
|
39
|
+
is justified by these measurements.
|
|
40
|
+
|
|
41
|
+
## Reproduce
|
|
42
|
+
|
|
43
|
+
Inside the distro, with the checkout:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pnpm run probe:gui-preview # stat / listDir / 1 MiB page read, p50/p95
|
|
47
|
+
node test/probe/fs-queue.mjs # the agent queue, sequential vs concurrent, with per-op latency
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Both need a working distro and Node; neither boots the harness. The probes print
|
|
51
|
+
the raw samples' p50/p95/max and fail when a budget is exceeded, so a regression
|
|
52
|
+
is a red probe rather than a claim in prose.
|
|
53
|
+
|
|
54
|
+
## What is not measured
|
|
55
|
+
|
|
56
|
+
- End-to-end GUI paint time (the browser and the service's own work are out of
|
|
57
|
+
scope for these probes).
|
|
58
|
+
- 9p numbers for large files or many small files beyond the A6 sample above;
|
|
59
|
+
the A6 measurement is the recorded baseline, not a re-runnable benchmark.
|
|
60
|
+
- aarch64; the binary is built but not executed in CI.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Five minutes from a clean Windows + WSL 2 machine to a working WSL session.
|
|
4
|
+
This page is the entry point; every claim links to the document that owns it.
|
|
5
|
+
|
|
6
|
+
## 1. Prerequisites
|
|
7
|
+
|
|
8
|
+
| Need | How to check | If missing |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| Windows 10/11 with WSL 2 | `wsl.exe -l -v` shows `VERSION 2` | `wsl.exe --install` |
|
|
11
|
+
| A distro | one line in the list above | `wsl.exe --install -d Ubuntu` |
|
|
12
|
+
| `bubblewrap` | `wsl.exe -d <distro> -- which bwrap` | `wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap` |
|
|
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+ or 24 | only needed for source/development work | not needed to use the published plugin |
|
|
15
|
+
|
|
16
|
+
The doctor (`scripts/doctor.mjs`, or `pnpm run doctor` from a checkout) covers all
|
|
17
|
+
of the above in one pass and prints a copy-paste remedy per failure. It is read-only unless you pass `--fix` (stale scopes) or
|
|
18
|
+
`--fix --install` (packages, after a confirmation).
|
|
19
|
+
|
|
20
|
+
## 2. Install
|
|
21
|
+
|
|
22
|
+
From a Windows PowerShell where the DSH CLI is available:
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
dsh plugin --profile <profile> add dsh-plugin-wsl-env
|
|
26
|
+
dsh --profile <profile> --dump-config # the plugin rows must be there
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For a profile that already overrides the preset, keep the override as the full
|
|
30
|
+
`preset-wsl` block — `pnpm run preset --check <patch>` fails loudly when a layer
|
|
31
|
+
targets `wsl-shell`/`wsl-fs` by id.
|
|
32
|
+
|
|
33
|
+
## 3. Open a WSL workspace
|
|
34
|
+
|
|
35
|
+
In the GUI, open a folder through the picker (`\\wsl.localhost\<distro>\home\<you>\proj`)
|
|
36
|
+
or an UNC path directly. The session's commands, file tools, terminal and previews
|
|
37
|
+
all run in that distro; a Windows folder keeps the normal Windows environment.
|
|
38
|
+
Two distros can be open at once, each with its own environment.
|
|
39
|
+
|
|
40
|
+
## 4. Verify
|
|
41
|
+
|
|
42
|
+
Ask for a command, e.g. `pwd && uname -a`. Expected: the Linux path and the
|
|
43
|
+
distro kernel. If the first command fails, the error names the remedy; the
|
|
44
|
+
[doctor](CONFIGURATION.md) and the [Troubleshooting table](../README.md#troubleshooting)
|
|
45
|
+
cover the common cases.
|
|
46
|
+
|
|
47
|
+
## Recipes
|
|
48
|
+
|
|
49
|
+
Commands run in the model's session, inside the distro, from the workspace root.
|
|
50
|
+
|
|
51
|
+
### Node / TypeScript
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node -v && pnpm -v
|
|
55
|
+
pnpm install
|
|
56
|
+
pnpm test
|
|
57
|
+
pnpm dev # a dev server survives the command that started it with sessionSandbox: true
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The server's port appears in `DSH_WSL_PORTS` on the next snapshot (10 s by
|
|
61
|
+
default); open it from Windows at `http://localhost:<port>`.
|
|
62
|
+
|
|
63
|
+
### Python
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
67
|
+
pip install -r requirements.txt
|
|
68
|
+
python -m pytest
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Use the distro's interpreter, not a Windows one: the session's `python3` is the
|
|
72
|
+
one on the distro `PATH` (pyenv/conda shims included).
|
|
73
|
+
|
|
74
|
+
### Rust
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cargo build --release
|
|
78
|
+
cargo test
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Targets are compiled natively for the distro's architecture (x86_64 or aarch64);
|
|
82
|
+
do not reuse Windows `target/` directories.
|
|
83
|
+
|
|
84
|
+
### Go
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
go build ./...
|
|
88
|
+
go test ./...
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The module cache lives in the distro's `$GOPATH`, so builds stay on ext4.
|
|
92
|
+
|
|
93
|
+
### Monorepo
|
|
94
|
+
|
|
95
|
+
Open the repository root as the workspace. Each package's commands run from the
|
|
96
|
+
same distro; `pnpm -r`, `turbo`, `nx` and `make` see one Linux filesystem. The
|
|
97
|
+
file watcher uses inotify inside the distro (`watchMaxDepth: 0`). If a change is
|
|
98
|
+
missed, see the watcher bounds in [CONFIGURATION.md](CONFIGURATION.md).
|
|
99
|
+
|
|
100
|
+
## Compatibility
|
|
101
|
+
|
|
102
|
+
Honest status, not a promise. "Probed" means a real-machine run is recorded in
|
|
103
|
+
this repository; "target" means the design supports it but the leg has not been
|
|
104
|
+
executed here.
|
|
105
|
+
|
|
106
|
+
| Tier | Configuration | Status |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| 1 | Ubuntu 24.04 or Debian 12, systemd, x86_64 | Probed on Ubuntu (the project's development machine; the real-machine suite is manual). Debian is a Tier 1 target, probe pending |
|
|
109
|
+
| 2 | Other systemd distros, aarch64 | The aarch64 native binary is built by CI but not executed here; aarch64 WSL or qemu is the pending leg |
|
|
110
|
+
| Unsupported | WSL 1, Linux/macOS hosts, Windows without WSL 2 | Not supported; the plugin attaches to `wsl.exe` |
|
|
111
|
+
|
|
112
|
+
With `sessionSandbox: auto` a distro without systemd/Linger still gets the
|
|
113
|
+
per-command `bubblewrap` sandbox and the result reports the narrower scope;
|
|
114
|
+
`true` refuses. See [CONFIGURATION.md](CONFIGURATION.md#the-strict-profile) for
|
|
115
|
+
the sandbox trade-offs and [PERFORMANCE.md](PERFORMANCE.md) for the measured
|
|
116
|
+
latency.
|
|
117
|
+
|
|
118
|
+
## Demo
|
|
119
|
+
|
|
120
|
+
There is no GIF or screenshot in this repository yet: producing one needs a
|
|
121
|
+
capture of the GUI, which this project has not recorded. The
|
|
122
|
+
[Troubleshooting table](../README.md#troubleshooting) and the probe output
|
|
123
|
+
(`pnpm run probe:sandbox`, `pnpm run probe:gui-preview`) are the reproducible
|
|
124
|
+
evidence in the meantime; the absence is recorded in
|
|
125
|
+
[ROADMAP-1.0.md](ROADMAP-1.0.md#wsl-302--discoverability-and-docs-entry).
|
package/docs/README.zh.md
CHANGED
|
@@ -80,6 +80,7 @@ wsl.exe -d <发行版> -u root -- apt-get install -y bubblewrap
|
|
|
80
80
|
|
|
81
81
|
打开一个发行版文件夹作为工作区,例如 `\\wsl.localhost\ubuntu\home\you\project`。
|
|
82
82
|
|
|
83
|
+
- 打开哪个发行版的文件夹,就在哪个发行版执行——即使它不是 WSL 的默认发行版。
|
|
83
84
|
- 命令在发行版登录 shell 中运行。`/home/you/x` 与 `\\wsl.localhost\ubuntu\home\you\x` 是同一个文件。
|
|
84
85
|
- **New terminal** 在同一发行版、同一目录打开 shell。
|
|
85
86
|
- 模型能看到监听端口,并返回 dev server 的 URL。WSL 2 会把 localhost 转发到 Windows。
|
|
@@ -91,18 +92,18 @@ wsl.exe -d <发行版> -u root -- apt-get install -y bubblewrap
|
|
|
91
92
|
|
|
92
93
|
| 行 | 键 | 默认值 | 作用 |
|
|
93
94
|
|---|---|---|---|
|
|
94
|
-
| `wsl-shell` | `distro` | `''` |
|
|
95
|
+
| `wsl-shell` | `distro` | `''` | 发行版模式;空/`auto` 跟随工作区,`default` 强制 WSL 默认 |
|
|
95
96
|
| | `sandbox` | `true` | 用 `bubblewrap` 约束命令 |
|
|
96
97
|
| | `sessionSandbox` | `true` | 会话命令跑在同一个 systemd user scope 中 |
|
|
97
98
|
| | `agentBackend` | `auto` | 发行版内 agent:`auto`、`native` 或 `shell` |
|
|
98
99
|
| | `maskWindowsDrive` | `false` | 让受限命令看不到 `/mnt` |
|
|
99
|
-
| `wsl-fs` | `distro` | `''` |
|
|
100
|
-
| | `restrictToDistro` | `true` |
|
|
100
|
+
| `wsl-fs` | `distro` | `''` | 发行版模式;空/`auto` 跟随每个目标路径 |
|
|
101
|
+
| | `restrictToDistro` | `true` | 固定发行版(`default` 或名字)时拒绝属于其他发行版的路径 |
|
|
101
102
|
| | `sandbox` | `true` | 按策略检查受限写入 |
|
|
102
103
|
| | `sessionSandbox` | `true` | 受限写入跑在会话 runtime 中 |
|
|
103
104
|
| | `substrate` | `agent` | 发行版内 agent;`share` 在启动时被拒绝 |
|
|
104
105
|
| `directory-picker-wsl` | `includeHostHome` | `true` | 同时列出 Windows 家目录 |
|
|
105
|
-
| `subprocess-wsl` | `distro` | `''` |
|
|
106
|
+
| `subprocess-wsl` | `distro` | `''` | 发行版模式;空/`auto` 跟随启动目录 |
|
|
106
107
|
|
|
107
108
|
`wsl-shell` 和 `wsl-fs` 位于随包的 `wsl` preset 中,按 id 覆盖无效。要改就覆盖整个 `preset-wsl` 行;配方和其余键见 [docs/CONFIGURATION.md](CONFIGURATION.md)。[`cordis.patch.yml`](../cordis.patch.yml) 是随包值的注释参考。
|
|
108
109
|
|
|
@@ -174,6 +175,7 @@ pnpm run sync:windows # 更新应用加载的 Windows 侧副本
|
|
|
174
175
|
| [docs/ARCHITECTURE.md](ARCHITECTURE.md) | 服务、路径路由、沙箱、错误码 |
|
|
175
176
|
| [docs/LIMITATIONS.md](LIMITATIONS.md) | 插件不做的事 |
|
|
176
177
|
| [docs/ROADMAP.md](ROADMAP.md) | 当前计划和推迟项 |
|
|
178
|
+
| [docs/ROADMAP-1.0.md](ROADMAP-1.0.md) | 1.0 与生态优化计划 |
|
|
177
179
|
| [docs/VALUE.zh.md](VALUE.zh.md) | 价值与取舍 |
|
|
178
180
|
| [CONTRIBUTING.md](../CONTRIBUTING.md) | 开发循环、质量门、探针 |
|
|
179
181
|
| [SECURITY.md](../SECURITY.md) | 私下上报漏洞 |
|