dsh-plugin-wsl-env 0.7.4 → 0.8.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 +298 -0
- package/CONTRIBUTING.md +129 -39
- package/README.md +20 -20
- package/docs/ARCHITECTURE.md +6 -3
- package/docs/LIMITATIONS.md +13 -0
- package/docs/README.zh.md +20 -20
- package/lib/agent-confined.d.ts +42 -0
- package/lib/agent-confined.js +25 -41
- package/lib/agent-errors.d.ts +31 -0
- package/lib/agent-errors.js +19 -16
- package/lib/agent-exec.d.ts +159 -0
- package/lib/agent-exec.js +151 -165
- package/lib/agent-protocol.d.ts +192 -0
- package/lib/agent-protocol.js +76 -100
- package/lib/agent-shared.d.ts +27 -0
- package/lib/agent-shared.js +12 -12
- package/lib/agent.d.ts +353 -0
- package/lib/agent.js +536 -552
- package/lib/auto-preset.d.ts +110 -0
- package/lib/auto-preset.js +157 -141
- package/lib/bwrap.d.ts +90 -0
- package/lib/bwrap.js +18 -18
- package/lib/file-reference-wsl.d.ts +160 -0
- package/lib/file-reference-wsl.js +192 -173
- package/lib/fs-decisions.d.ts +107 -0
- package/lib/fs-decisions.js +64 -60
- package/lib/fs-routing.d.ts +182 -0
- package/lib/fs-routing.js +173 -116
- package/lib/fs-substrate.d.ts +258 -0
- package/lib/fs-substrate.js +248 -248
- package/lib/fs.d.ts +10 -0
- package/lib/fs.js +5 -0
- package/lib/fsio-agent.d.ts +307 -0
- package/lib/fsio-agent.js +414 -410
- package/lib/fsio-text.d.ts +115 -0
- package/lib/fsio-text.js +104 -84
- package/lib/git-route.d.ts +100 -0
- package/lib/git-route.js +46 -47
- package/lib/index.d.ts +1003 -0
- package/lib/index.js +1101 -997
- package/lib/listing.d.ts +114 -0
- package/lib/listing.js +68 -55
- package/lib/paths.d.ts +182 -0
- package/lib/paths.js +136 -105
- package/lib/picker.d.ts +266 -0
- package/lib/picker.js +371 -351
- package/lib/ports.d.ts +44 -0
- package/lib/ports.js +39 -29
- package/lib/preset-choice.d.ts +33 -0
- package/lib/preset-choice.js +15 -7
- package/lib/probe-cache.d.ts +40 -0
- package/lib/probe-cache.js +35 -29
- package/lib/sandbox-core.d.ts +79 -0
- package/lib/sandbox-core.js +71 -88
- package/lib/sandbox.d.ts +48 -0
- package/lib/sandbox.js +4 -4
- package/lib/search-exec.d.ts +96 -0
- package/lib/search-exec.js +52 -79
- package/lib/search-route.d.ts +75 -0
- package/lib/search-route.js +30 -31
- package/lib/shell-env.d.ts +88 -0
- package/lib/shell-env.js +78 -70
- package/lib/shell.d.ts +10 -0
- package/lib/shell.js +5 -0
- package/lib/subprocess.d.ts +313 -0
- package/lib/subprocess.js +267 -262
- package/lib/terminal-activity.d.ts +98 -0
- package/lib/terminal-activity.js +29 -39
- package/lib/terminal-route.d.ts +27 -0
- package/lib/terminal-route.js +16 -8
- package/lib/watcher.d.ts +77 -0
- package/lib/watcher.js +120 -126
- package/lib/workspace-files-route.d.ts +132 -0
- package/lib/workspace-files-route.js +73 -81
- package/lib/workspace-files-wsl.d.ts +257 -0
- package/lib/workspace-files-wsl.js +311 -251
- package/lib/wsl.d.ts +266 -0
- package/lib/wsl.js +144 -151
- package/package.json +68 -20
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,304 @@ reference below points at that record's numbering.
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.8.0] - 2026-10-06
|
|
16
|
+
|
|
17
|
+
The toolchain's TypeScript story completes (pnpm, a checkJs gate, and 36 of 36
|
|
18
|
+
modules with their declarations shipped), and with it three behaviour fixes
|
|
19
|
+
land: the byte-window cap is enforced, the agent path's EXEC capture budget is
|
|
20
|
+
honoured, and the filesystem's host-path mapping speaks its synchronous
|
|
21
|
+
contract.
|
|
22
|
+
|
|
23
|
+
One theme: the toolchain moves from npm to pnpm. `pnpm-lock.yaml` replaces
|
|
24
|
+
`package-lock.json`, CI installs through corepack with
|
|
25
|
+
`pnpm install --frozen-lockfile` (the `packageManager` field pins pnpm 11.7.0,
|
|
26
|
+
matching the harness repository), and the three dependencies with install
|
|
27
|
+
scripts (`@deepseek-ai/dsh-subprocess-local`, `koffi`, `node-pty`) are
|
|
28
|
+
allow-listed in `pnpm-workspace.yaml` — pnpm 11 blocks dependency build
|
|
29
|
+
scripts by default. With it: `engines` and the CI matrix align with the harness
|
|
30
|
+
host's own floor (`^22.19.0 || >=24.0.0`), so the Node 20 legs are gone, and
|
|
31
|
+
the per-target lock test drops its unused `withResolvers` gate, a Node 22+ API
|
|
32
|
+
that hung those legs on every push.
|
|
33
|
+
|
|
34
|
+
One more theme: the whole repository comes under a TypeScript check gate.
|
|
35
|
+
`tsc --noEmit` under `strict`/`checkJs` now runs as part of `pnpm test` over
|
|
36
|
+
`lib/**`, `test/**` and the probe scripts, with the types taken from the peer
|
|
37
|
+
packages' own declarations and the service augmentations loaded through
|
|
38
|
+
`types/dsh-services.d.ts`. The gate's first pass surfaced the seam work this
|
|
39
|
+
package already carries: the private `spawnSpec` the WSL spawn directory needs
|
|
40
|
+
(`docs/UPSTREAM-SPAWN-SEAM.md` bridges it) and a synchronous-contract violation
|
|
41
|
+
in `processPathFromHostPath`, pending a design decision.
|
|
42
|
+
|
|
43
|
+
Extending the gate to the tests found real defects the untyped corners had
|
|
44
|
+
been hiding: `distroReadBytes` accepted the byte-window cap (both call sites
|
|
45
|
+
pass it) but neither documented nor enforced it, so a ranged `readBytes`
|
|
46
|
+
asking past the page cap was served instead of refused — upstream refuses a
|
|
47
|
+
window the same way it refuses a page. Fixed against the upstream wording and
|
|
48
|
+
arithmetic (`lib/workspace-files-wsl.js`), with an absent length now
|
|
49
|
+
defaulting to the window cap rather than the whole-file cap, as upstream's
|
|
50
|
+
`resolveWindow` defaults it; the refusal is pinned in
|
|
51
|
+
`test/workspace-files-wsl.test.mjs`. Two hand-written JSDoc contracts wider
|
|
52
|
+
than their code (`gitSpawnRewrite`'s `argv`/`cwd`, `wslErrorCode`'s
|
|
53
|
+
`stdout`/`stderr` — both tolerate absence at runtime) and one narrower than
|
|
54
|
+
its caller (`uncToPosix`'s path) were corrected to what the functions actually
|
|
55
|
+
take. Test doubles are typed as the slice of the seam they script, reaching
|
|
56
|
+
the seam's declared type through one documented cast at the injection point —
|
|
57
|
+
no `any` and no new `@ts-expect-error` anywhere in the suite.
|
|
58
|
+
|
|
59
|
+
And the migration's third step opens: `src/agent-protocol.ts` is the first
|
|
60
|
+
module whose source is TypeScript. `pnpm run build` (`tsconfig.build.json`)
|
|
61
|
+
emits `lib/agent-protocol.js` plus `lib/agent-protocol.d.ts` under the same
|
|
62
|
+
export paths, and the artifacts stay committed so a bare checkout remains
|
|
63
|
+
loadable and the tarball needs no build step; the new `lint:build` gate
|
|
64
|
+
(`test/build-freshness.mjs`) makes `pnpm test` fail the moment a committed
|
|
65
|
+
artifact drifts from its source. Generated files are excluded from the
|
|
66
|
+
`checkJs` gate — their TypeScript sources are type-checked with
|
|
67
|
+
`noEmitOnError` instead, which is strictly stronger. The protocol message
|
|
68
|
+
union now has one home: `lib/agent.js` and the script-driven tests import
|
|
69
|
+
`AgentMessage` and friends from the protocol module's declarations instead of
|
|
70
|
+
restating them.
|
|
71
|
+
|
|
72
|
+
And the declarations land for every module. A second, declaration-only pass
|
|
73
|
+
(`tsconfig.dts.json`, driven by `scripts/build.mjs`) emits `.d.ts` for the
|
|
74
|
+
hand-written, JSDoc-typed JavaScript, and every export subpath now carries its
|
|
75
|
+
`types` condition — downstream finally sees this plugin's types, verified end
|
|
76
|
+
to end (a consumer's wrong call is rejected with the precise signature). The
|
|
77
|
+
declaration pass must run against a clean tree: a stale `.d.ts` is resolved as
|
|
78
|
+
an input exactly where `./x.js` would be, and re-emitting through it silently
|
|
79
|
+
degrades the result — a precise type in `index.d.ts` collapsed to `any` that
|
|
80
|
+
way during the migration — so the build deletes the declarations its first
|
|
81
|
+
pass does not own before re-emitting, and `lint:build` snapshots the
|
|
82
|
+
artifacts, runs the real build, byte-compares and restores the tree.
|
|
83
|
+
`lint:style` gained the invariant that keeps the surface from rotting: a
|
|
84
|
+
JavaScript export without its `types` sibling fails the gate.
|
|
85
|
+
|
|
86
|
+
`lib/paths.js` follows as the second migrated module — the path algebra every
|
|
87
|
+
other module imports, plus `DistroPath`/`WorldPathOptions`, which consumers
|
|
88
|
+
reference through `import("./paths.js").DistroPath`; the TypeScript source
|
|
89
|
+
keeps those names, so nothing downstream changes except that they now come
|
|
90
|
+
from a checked source. CONTRIBUTING records the per-module checklist this
|
|
91
|
+
settled into: write `src/<name>.ts`, add the generated JavaScript to the two
|
|
92
|
+
`exclude` lists, build, and let the suite verify the artifact is
|
|
93
|
+
behaviourally identical.
|
|
94
|
+
|
|
95
|
+
Three leaf modules then follow in one pass — `agent-errors` (the two error
|
|
96
|
+
classes the executor switches on), `probe-cache` (the verdict policy that
|
|
97
|
+
remembers successes and re-probes failures) and `listing` (the picker's pure
|
|
98
|
+
helpers, `boundedInsert`'s generic included). Each was verified export-for-
|
|
99
|
+
export against its predecessor, and the whitespace-normalized code diff is
|
|
100
|
+
tsc's statement reflow and nothing else; every source keeps its module doc
|
|
101
|
+
plus a note naming the generated artifact.
|
|
102
|
+
|
|
103
|
+
The next four carry the decision layer: `fsio-text` (the byte-for-byte
|
|
104
|
+
replication of the peer's text mechanics — `FsCodedError` gained a declared
|
|
105
|
+
`code` field, and the stream decoder is typed through `node:util`, since
|
|
106
|
+
Node's global `TextDecoder` is a value, not a type), `fs-decisions` (the
|
|
107
|
+
refusal descriptors, with `Refusal` as an exported interface and `isCoded` as
|
|
108
|
+
a real type predicate), and `git-route`/`search-route` (the two spawn
|
|
109
|
+
rewrites; their generated artifacts are byte-identical modulo whitespace).
|
|
110
|
+
Nine of the thirty-six modules now have TypeScript sources.
|
|
111
|
+
|
|
112
|
+
Six more follow — `terminal-route`, `preset-choice`, `ports`, `bwrap`,
|
|
113
|
+
`agent-confined` and `agent-shared` — and they cross a structural line: a
|
|
114
|
+
source may now import a module that is not migrated yet. `rootDirs` merges
|
|
115
|
+
`src/` and `lib/` for resolution, so `./agent.js` resolves through the
|
|
116
|
+
generated `lib/agent.d.ts` while the emitted specifier stays `./agent.js`;
|
|
117
|
+
because that merge also pulls declarations in as program inputs, pass 1 now
|
|
118
|
+
emits into a scratch directory the build script copies from, keeping inputs
|
|
119
|
+
and outputs disjoint (TS5055 otherwise). `agent-shared`'s generated artifact
|
|
120
|
+
is byte-identical even after whitespace normalization; the rest differ only
|
|
121
|
+
by tsc's statement reflow. Fifteen of the thirty-six modules are TypeScript.
|
|
122
|
+
|
|
123
|
+
The mid-size tier follows: `wsl` (the `wsl.exe` interop primitives — its four
|
|
124
|
+
option shapes are exported interfaces now), `agent-exec` (the agent-backed
|
|
125
|
+
execution handle, whose request/result/runner/handle typedefs the tests and
|
|
126
|
+
`search-exec` already imported by name) and `shell-env` (the `DSH_*` fact
|
|
127
|
+
contributor, typing the harness registry it contributes through structurally,
|
|
128
|
+
as its JSDoc always did). `agent-exec` surfaced one Node typing fact worth
|
|
129
|
+
knowing: `Buffer.alloc(0)` infers `Buffer<ArrayBuffer>` while an incoming
|
|
130
|
+
`Buffer` is `Buffer<ArrayBufferLike>`, so the accumulators are annotated
|
|
131
|
+
`Buffer`. Eighteen of the thirty-six modules are TypeScript now.
|
|
132
|
+
|
|
133
|
+
Seven small modules follow — `watcher` (the in-distro mtime watch loop and its
|
|
134
|
+
protocol, `ChildProcess` and options typed), `sandbox-core`/`sandbox` (the
|
|
135
|
+
bwrap probe-and-confine pair; `WslConfined`/`WslSandboxCoreInstance` are
|
|
136
|
+
exported interfaces and the two class fields are `declare` so the emitted
|
|
137
|
+
JavaScript stays byte-identical), `terminal-activity`, `search-exec`, and the
|
|
138
|
+
two entry re-exports `shell`/`fs` (whose artifacts are byte-identical). At
|
|
139
|
+
twenty-five of thirty-six, the remaining modules are the large ones: `agent`,
|
|
140
|
+
`fs-substrate`, `fsio-agent`, `picker`, `subprocess`, `auto-preset`,
|
|
141
|
+
`workspace-files-*`, `file-reference-wsl`, `fs-routing`, and finally `index`.
|
|
142
|
+
|
|
143
|
+
The route layer follows — `workspace-files-route` (the pure build/parse pairs
|
|
144
|
+
for the distro-routed file service), `file-reference-wsl` (the `@`-completion
|
|
145
|
+
traversal strategy, with `TraversalDirent`/`FileReferenceTraversal` as
|
|
146
|
+
exported interfaces) and `fs-routing` (the root-plane coordinate router).
|
|
147
|
+
`fs-routing` is where the compiler re-met the documented upstream seam:
|
|
148
|
+
`WslFileSystem.processPathFromHostPath` is async against the `FileSystem`
|
|
149
|
+
interface's synchronous contract, so the router's lazy backend is typed as the
|
|
150
|
+
concrete class rather than casting the mismatch away — the seam keeps its one
|
|
151
|
+
visible `@ts-expect-error` in `index.js`. The port also dropped two dead
|
|
152
|
+
imports `fs-routing` had carried since a refactor (`windowsToLinuxMount`,
|
|
153
|
+
`linuxJoin`), which the emitted artifact no longer contains. The orphaned
|
|
154
|
+
`linuxJoin` doc block above the class — a leftover of that same refactor — is
|
|
155
|
+
gone too. Twenty-eight of the thirty-six modules are TypeScript.
|
|
156
|
+
|
|
157
|
+
`fs-substrate` and `auto-preset` follow — the agent-backed filesystem face
|
|
158
|
+
(`ResolvedTarget` exported as an interface, its six instance fields declared
|
|
159
|
+
with `declare` so the emitted JavaScript stays byte-identical) and the
|
|
160
|
+
preset-adoption plugin (whose harness service slices stay spelled
|
|
161
|
+
structurally, and whose `ensureSession` patch carries the `this: unknown`
|
|
162
|
+
parameter the JSDoc already documented). Thirty of the thirty-six modules
|
|
163
|
+
are TypeScript; the remaining six are `agent`, `fsio-agent`, `picker`,
|
|
164
|
+
`subprocess`, `workspace-files-wsl` and `index`.
|
|
165
|
+
|
|
166
|
+
`picker` follows — the biggest single module migrated so far after the
|
|
167
|
+
protocol file itself (494 lines). Its config, the cached distro listing and
|
|
168
|
+
the per-distro home map are `declare`d so the emitted JavaScript stays
|
|
169
|
+
byte-identical, and the browse capability object stays an inline class field,
|
|
170
|
+
which is how the original script wrote it. Thirty-one of the thirty-six
|
|
171
|
+
modules are TypeScript.
|
|
172
|
+
|
|
173
|
+
`subprocess` follows — the composition-level provider behind the GUI's
|
|
174
|
+
terminal window, and the last module that overrides a shipped peer class for
|
|
175
|
+
the terminal/search routing. Its `spawnTerminal`/`spawn` overrides are checked
|
|
176
|
+
against the peer's declared signatures (they pass without a new assertion),
|
|
177
|
+
and the two JSDoc casts the script carried (`env`, the stdout collect budget)
|
|
178
|
+
are absorbed into the types. Thirty-two of the thirty-six modules are
|
|
179
|
+
TypeScript; the last four are `agent`, `fsio-agent`, `workspace-files-wsl`
|
|
180
|
+
and `index`.
|
|
181
|
+
|
|
182
|
+
`agent` follows — the resident in-distro agent's whole lifecycle (756 lines).
|
|
183
|
+
Its seven internal methods stay `private`, which surfaced the migration's one
|
|
184
|
+
structural seam: a class with private members is nominal, so the declaration
|
|
185
|
+
generated for the still-un-migrated `fsio-agent` and the one this source emits
|
|
186
|
+
are not interchangeable even though the runtime object is the same class. The
|
|
187
|
+
one boundary (`fs-substrate` handing agents to `DistroFs`) crosses it through a
|
|
188
|
+
single documented assertion naming the lib-side type; it disappears when
|
|
189
|
+
`fsio-agent` migrates. Thirty-three of the thirty-six modules are TypeScript.
|
|
190
|
+
|
|
191
|
+
`fsio-agent` follows, and with it that seam closes: the lib-side declaration
|
|
192
|
+
it used to drag in is gone, so the boundary assertion in `fs-substrate` is
|
|
193
|
+
deleted in the same commit — the class is declared once now, and the private
|
|
194
|
+
members it keeps are nominal against nothing else. Thirty-four of the
|
|
195
|
+
thirty-six modules are TypeScript; `workspace-files-wsl` and `index` remain.
|
|
196
|
+
|
|
197
|
+
`workspace-files-wsl` follows — the distro-routed GUI file service, with
|
|
198
|
+
`WorkspaceFilesWslConfig`/`DistroListing` exported, the row-config read kept
|
|
199
|
+
as its one documented double cast (the base class keeps `config` private), and
|
|
200
|
+
the `readByteRange` expect-error left exactly where it was: that one is an
|
|
201
|
+
open design decision, not a migration artifact. Thirty-five of the thirty-six
|
|
202
|
+
modules are TypeScript; `index` is the last one.
|
|
203
|
+
|
|
204
|
+
`index` closes the migration — the integration layer's two providers, 1400
|
|
205
|
+
lines, with both documented upstream seams intact: the private `spawnSpec` a
|
|
206
|
+
`wsl.exe` spawn needs, and the `processPathFromHostPath` contract violation,
|
|
207
|
+
keep their `@ts-expect-error` carrying the reason. The generated artifact is
|
|
208
|
+
proven structurally identical to the hand-written module: an AST comparison
|
|
209
|
+
that treats the erased type wrappers (`as`, `@type` casts, parentheses) as
|
|
210
|
+
transparent reports only the imports the source no longer needs — a never-used
|
|
211
|
+
`fileURLToPath`, the dead `isUnderDistro`, and the type-only `WslAgent` (which
|
|
212
|
+
tsc elides; the module still loads through its other edges) — plus tsc's
|
|
213
|
+
reflow and one internal rename in `decorateResult`, whose result row now
|
|
214
|
+
derives from the handle type instead of a second type parameter. `resolve()`
|
|
215
|
+
returns the precise `WslExecSpec` where the JSDoc declared the wider
|
|
216
|
+
`ShellExecSpec`. Thirty-six of thirty-six modules are TypeScript.
|
|
217
|
+
|
|
218
|
+
The transitional scaffolding then retires, nothing left for it to do:
|
|
219
|
+
`rootDirs` and the scratch declaration emission existed for a mixed tree
|
|
220
|
+
(`src/` sources importing un-migrated `lib/*.js` neighbours), and the
|
|
221
|
+
declaration-only second pass (`tsconfig.dts.json`) existed to give the
|
|
222
|
+
hand-written JavaScript its `.d.ts`. `pnpm run build` is one `tsc` pass
|
|
223
|
+
emitting JavaScript and declarations straight into `lib/`, pruning
|
|
224
|
+
declarations no source owns; the `checkJs` gate drops its 36-entry exclude
|
|
225
|
+
list and states what is NOT generated — `test/**` and the ambient service
|
|
226
|
+
types, with `src/` checked by the build program (`noEmitOnError`); and
|
|
227
|
+
`lint:build` enforces the closed corpus (every `lib/*.js` has its
|
|
228
|
+
`src/<name>.ts`, every source has both artifacts), so "`lib/` is entirely
|
|
229
|
+
generated" is a checked property rather than a convention.
|
|
230
|
+
|
|
231
|
+
The real-machine probes then re-ran green on the migrated tree — 12 of 12, the
|
|
232
|
+
one verification face CI cannot cover (its self-hosted leg is skipped):
|
|
233
|
+
filesystem mutation through the throwaway wslfs profile, terminal, mode,
|
|
234
|
+
missing-wsl, bwrap, the shell confinement wiring, sandbox-off, picker, agent
|
|
235
|
+
protocol, watch, substrate and exec. The run surfaced two defects in the probe
|
|
236
|
+
harness itself. `ELECTRON_RUN_AS_NODE` — the flag that boots the harness CLI in
|
|
237
|
+
node mode — never crossed the WSL interop boundary: interop hands a Windows
|
|
238
|
+
process only the variables `WSLENV` lists, so the inline assignment was dropped
|
|
239
|
+
and the app booted as its GUI self, leaving the probe waiting on a process that
|
|
240
|
+
would never run; `test/probe/env.sh` lists the flag now, composed with any
|
|
241
|
+
`WSLENV` the caller had. And the kill-cleanup check counted every `sleep` in
|
|
242
|
+
the distro, which the resident agent's lease watchdog — introduced after the
|
|
243
|
+
probes last ran — makes non-zero by construction, alongside in-flight timeout
|
|
244
|
+
watchers; the check counts its own command line now, so a kill that really
|
|
245
|
+
leaked still fails it. And the suite's live log sits inside the checkout,
|
|
246
|
+
where the mirror sync's `tar` read it while it grew — `file changed as we read
|
|
247
|
+
it`, exit 1, `set -e` failing the probe before it ran; the sync excludes
|
|
248
|
+
`test/probe/.scratch` now, which is run scratch nothing at runtime reads.
|
|
249
|
+
|
|
250
|
+
One real defect fell out of the type gate's migration notes and closes here:
|
|
251
|
+
the agent execution path handed the EXEC frame the volatile wrapper object
|
|
252
|
+
where a byte budget belongs. `encodeExecFrame`'s `> 0` test was false on an
|
|
253
|
+
object, the frame carried 0, and 0 is the agent's "uncapped" — so the cut at
|
|
254
|
+
the caller's per-stream budget that `agent-exec` documents never ran, and a
|
|
255
|
+
chatty command cost unbounded distro memory. `src/index.ts` now reads
|
|
256
|
+
`config.maxOutputBytes.get()` like every other volatile field, and
|
|
257
|
+
`test/provider.test.mjs` pins the resolved number in the frame (the check
|
|
258
|
+
fails against the previous artifact). The one-shot path was always correct —
|
|
259
|
+
it reads the same field through the upstream executor's own `.get()`.
|
|
260
|
+
|
|
261
|
+
The second pending seam closes the same way — with an implementation instead
|
|
262
|
+
of a note. `WslFileSystem.processPathFromHostPath` was `async` against the
|
|
263
|
+
peer interface's synchronous contract, so the consumers that read it without
|
|
264
|
+
awaiting (spill-policy's attachment mapping, the session controller's and the
|
|
265
|
+
UI's path verification) received a Promise — always truthy, never the
|
|
266
|
+
`undefined` they test for — and mapped nothing. It is synchronous now, built
|
|
267
|
+
from what is already known: the distro once `distro()` has resolved it (the
|
|
268
|
+
pinned config, or the default `wsl.exe` answered by an earlier operation) and
|
|
269
|
+
the configured `cwd`, or the resolved home for a relative path. A call that
|
|
270
|
+
would have to ask `wsl.exe` — a cold default distro, a relative path before
|
|
271
|
+
the home is known — answers `undefined`, the same "no mapping" the ssh
|
|
272
|
+
provider gives, so the consumers take their documented refusal path instead of
|
|
273
|
+
receiving a bogus value. The value keeps the world spelling the backend keys
|
|
274
|
+
on, so their map → resolve → processPath round trip closes exactly as it does
|
|
275
|
+
for the host backend. `test/provider.test.mjs` pins the sync value, the round
|
|
276
|
+
trip, and both `undefined` cases (the checks fail against the previous
|
|
277
|
+
artifact). The `@ts-expect-error` count drops to three: the two remaining
|
|
278
|
+
design decisions and the upstream spawn seam.
|
|
279
|
+
|
|
280
|
+
The last in-code seam note closes by deletion rather than repair. The GUI file
|
|
281
|
+
service's `readByteRange` — a method older than the migration, which the type
|
|
282
|
+
gate flagged with an expect-error because its host-workspace branch called a
|
|
283
|
+
base method that does not exist — turned out to be unreachable: the remote
|
|
284
|
+
layer registers exactly the methods a decorated class declares (typert writes
|
|
285
|
+
its markers per instance, from the class that carries the decorators), the
|
|
286
|
+
base service has no such method, and nothing in the plugin or in the harness
|
|
287
|
+
calls it. Ranged reads already go through the wire op they always did —
|
|
288
|
+
`readBytes` with `options.range`, which the distro branch serves and the host
|
|
289
|
+
branch delegates. The method and its one test line are gone (the range case
|
|
290
|
+
stays covered through `readBytes`), and one more expect-error goes with it:
|
|
291
|
+
two remain, the exit-4 kind and the upstream spawn seam.
|
|
292
|
+
|
|
293
|
+
## [0.7.5] - 2026-10-05
|
|
294
|
+
|
|
295
|
+
One theme: a failed environment probe retries instead of being trusted
|
|
296
|
+
forever - the shell placeholder that used to pin bash onto every later
|
|
297
|
+
command, and the hardcoded distro name that minted UNC identities.
|
|
298
|
+
|
|
299
|
+
### Fixed
|
|
300
|
+
|
|
301
|
+
- **A failed shell probe no longer pins `bash` for the executor's
|
|
302
|
+
lifetime.** `defaultShell` reported the literal `"bash"` when both probes
|
|
303
|
+
failed - a cold distro, a flaky first `wsl.exe` call - and the executor
|
|
304
|
+
cached that placeholder permanently: every later command ran bash on a
|
|
305
|
+
zsh distro, and the shell tool introduced itself as bash. The probes now
|
|
306
|
+
answer `undefined`; the executor caches only a real resolution and falls
|
|
307
|
+
back to bash per call, so the next command retries. `shell-env` follows:
|
|
308
|
+
an unresolved shell simply omits the `DSH_WSL_SHELL` fact instead of
|
|
309
|
+
presenting a guess as the distro's login shell. Alongside, `toWorldPath`
|
|
310
|
+
no longer mints UNC identities under the hardcoded `"Ubuntu"` fallback -
|
|
311
|
+
the pinned distro is a required option now, and every caller passes it.
|
|
312
|
+
|
|
15
313
|
## [0.7.4] - 2026-10-05
|
|
16
314
|
|
|
17
315
|
One theme: the 0.2.0 review's engineering debt closes out - the five
|
package/CONTRIBUTING.md
CHANGED
|
@@ -10,26 +10,115 @@ the conventions this repository uses.
|
|
|
10
10
|
| Windows 10 or 11 | the plugin targets Windows only |
|
|
11
11
|
| WSL2 with at least one distro | `wsl.exe -l -v` lists the installed distros |
|
|
12
12
|
| `bubblewrap` in the distro | `sudo apt install bubblewrap`. The sandbox fails closed without it. |
|
|
13
|
-
| Node
|
|
13
|
+
| Node 22.19+, or 24 | the versions CI runs. `engines` requires `^22.19.0 \|\| >=24.0.0`, the harness host's own floor. |
|
|
14
14
|
|
|
15
15
|
## Toolchain
|
|
16
16
|
|
|
17
|
-
The package
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
The runtime package ships an empty `dependencies` object on purpose: it is
|
|
18
|
+
loaded into the DSH process and everything it needs arrives as a peer package.
|
|
19
|
+
Running the plugin from a checkout has no install step.
|
|
20
|
+
|
|
21
|
+
Developing and running the gates do have dependencies. The 0.7.x tests import
|
|
22
|
+
pinned `@deepseek-ai/*` packages — the composition and boot tests run the
|
|
23
|
+
loader's real patch algorithm — so a fresh checkout needs one `pnpm install`
|
|
24
|
+
before the gates pass.
|
|
25
|
+
|
|
26
|
+
pnpm runs the scripts in this repository, pinned through the `packageManager`
|
|
27
|
+
field (corepack resolves it). Treat `pnpm run <script>` as the reference
|
|
28
|
+
toolchain.
|
|
29
|
+
|
|
30
|
+
Do not add a runtime dependency — see above. Style tooling is kept out for the
|
|
31
|
+
same reason: there is no ESLint and no Prettier. The checks that would normally
|
|
32
|
+
need them are built from Node builtins instead. `test/style.mjs` verifies the
|
|
33
|
+
rules `.editorconfig` states, and it checks that every `files` entry still
|
|
34
|
+
matches tracked content and every `exports` target still exists. The syntax pass
|
|
35
|
+
inside `pnpm test` uses Node's own parser.
|
|
36
|
+
|
|
37
|
+
Exactly three dependencies run install scripts (`@deepseek-ai/dsh-subprocess-local`,
|
|
38
|
+
`koffi`, `node-pty` — the native pieces); they are allow-listed in
|
|
39
|
+
`pnpm-workspace.yaml` and everything else builds nothing.
|
|
40
|
+
|
|
41
|
+
## The type gate
|
|
42
|
+
|
|
43
|
+
`lint:types` runs `tsc --noEmit` under `strict` with `checkJs`: the JavaScript
|
|
44
|
+
in `lib/` is fully type-checked, and the types come from the peer packages'
|
|
45
|
+
own `.d.ts` — nothing is invented twice. `types/dsh-services.d.ts` pulls in the
|
|
46
|
+
`declare module '@deepseek-ai/cordis'` augmentations the service packages ship,
|
|
47
|
+
so `ctx.fs`, `ctx.shell`, `ctx.sandboxPolicy` and friends are typed; it declares
|
|
48
|
+
nothing itself and ships nowhere.
|
|
49
|
+
|
|
50
|
+
The gate's scope is what is NOT generated: `test/**` (the probe scripts
|
|
51
|
+
included) and the ambient service types the build shares. The sources under
|
|
52
|
+
`src/` are checked by the build program itself (`strict`, `noEmitOnError`),
|
|
53
|
+
which is strictly stronger than checking their emitted JavaScript. The shipped
|
|
54
|
+
artifact was checked first because it is where protocol drift with the pinned
|
|
55
|
+
peers hurts; the tests and probes came next, and the errors they surfaced were
|
|
56
|
+
real — a test that pinned a raw number into an argv, a couple of hand-written
|
|
57
|
+
JSDoc contracts wider than the code (`wslErrorCode`, `gitSpawnRewrite`) and
|
|
58
|
+
narrower than their seams, and one accepted-but-never-enforced byte-window cap
|
|
59
|
+
in the distro-routed file service, found because a fake hands the confinement
|
|
60
|
+
seam an `"full"` enforcement the core itself never reports.
|
|
61
|
+
|
|
62
|
+
Three patterns keep the check honest:
|
|
63
|
+
|
|
64
|
+
- A subclass's own config keys are declared as a type mirroring its
|
|
65
|
+
`static Config` schema and read through one explicit cast — the schema and
|
|
66
|
+
the type can then only drift apart visibly.
|
|
67
|
+
- A test double is typed as the slice of the seam it scripts (a duck
|
|
68
|
+
`@typedef`) and reaches the seam's declared type through one documented cast
|
|
69
|
+
at the injection point — the cast states which contract the double stands in
|
|
70
|
+
for, instead of silencing the mismatch with `any`.
|
|
71
|
+
- `@ts-expect-error` is allowed exactly when the suppressed error names a real
|
|
72
|
+
upstream seam gap, carries the reason inline, and the gap is written up in
|
|
73
|
+
`docs/UPSTREAM-*.md`. It is a published finding with a tracking doc, not a
|
|
74
|
+
silencer.
|
|
75
|
+
|
|
76
|
+
## The build
|
|
77
|
+
|
|
78
|
+
The plugin must be loadable from a bare checkout: the harness has no
|
|
79
|
+
transpilation layer, the Windows runtime mirror is a copy of the tree, and the
|
|
80
|
+
tests exercise `lib/*.js` — the published artifact — rather than the sources.
|
|
81
|
+
Sources live in `src/`, built JavaScript and declarations in `lib/`, and the
|
|
82
|
+
artifacts stay COMMITTED. `pnpm run build` (`scripts/build.mjs`) runs one pass —
|
|
83
|
+
`tsc -p tsconfig.build.json` — emitting every `src/*.ts` as JavaScript plus its
|
|
84
|
+
declaration under the same export paths as the hand-written modules it
|
|
85
|
+
replaced. Imports name the artifact (`./x.js`); `nodenext` resolves that
|
|
86
|
+
through the TypeScript source. The build then prunes declarations no source
|
|
87
|
+
owns (a rename leftover), so `lib/` is exactly the artifacts of `src/`.
|
|
88
|
+
|
|
89
|
+
Rules that keep this honest:
|
|
90
|
+
|
|
91
|
+
- Never edit a generated file under `lib/` — its module doc names the source
|
|
92
|
+
to edit and `lint:build` fails the moment the two drift. Generated artifacts
|
|
93
|
+
keep tsc's canonical formatting (four-space, compacted); review `src/` for
|
|
94
|
+
every module.
|
|
95
|
+
- `lint:build` (`test/build-freshness.mjs`) enforces that the corpus is closed
|
|
96
|
+
— every `lib/*.js` has its `src/<name>.ts`, every source has both artifacts —
|
|
97
|
+
then snapshots every generated file, runs the real build, byte-compares and
|
|
98
|
+
RESTORES the snapshot: a check, not a rebuild; the remedy for drift is
|
|
99
|
+
`pnpm run build` plus a commit. It is part of the gate chain after
|
|
100
|
+
`test:syntax`.
|
|
101
|
+
- Generated files are not in the `checkJs` gate at all (`tsconfig.json` covers
|
|
102
|
+
`test/**` and the ambient types); their sources are type-checked by the build
|
|
103
|
+
program with `noEmitOnError`, so a broken build never overwrites `lib/`.
|
|
104
|
+
- Every JavaScript export carries its `types` condition (enforced by
|
|
105
|
+
`lint:style`), so downstream resolves these declarations; there is no
|
|
106
|
+
second, hand-written type surface.
|
|
107
|
+
- The protocol message union and friends are the single home now: consumers
|
|
108
|
+
import them (`@typedef {import("./agent-protocol.js").AgentMessage}
|
|
109
|
+
AgentMessage`) instead of restating them, which is how `lib/agent.js` and
|
|
110
|
+
the script-driven tests read them.
|
|
111
|
+
|
|
112
|
+
### Adding a module
|
|
113
|
+
|
|
114
|
+
1. Write `src/<name>.ts`; imports name the artifact (`./x.js`), which
|
|
115
|
+
`nodenext` resolves through the TypeScript source. Move every type into the
|
|
116
|
+
signature, keep the prose, and keep every exported NAME — types included,
|
|
117
|
+
since consumers import them (`import("./paths.js").DistroPath`). Close the
|
|
118
|
+
module doc with the note that names the generated artifact.
|
|
119
|
+
2. `pnpm run build`, then `pnpm test`. There is no exclude list to maintain and
|
|
120
|
+
no registration step: the build and `lint:build` derive everything from
|
|
121
|
+
`src/`, and `lib/` is generated output — never edit it by hand.
|
|
33
122
|
|
|
34
123
|
## The development loop
|
|
35
124
|
|
|
@@ -50,7 +139,7 @@ The Windows copy under `default-workspace/dsh-plugin-wsl` is a runtime mirror. S
|
|
|
50
139
|
it before launching the app:
|
|
51
140
|
|
|
52
141
|
```bash
|
|
53
|
-
|
|
142
|
+
pnpm run sync:windows
|
|
54
143
|
```
|
|
55
144
|
|
|
56
145
|
The mirror destination sits outside a session workspace. An agent that runs the
|
|
@@ -62,28 +151,29 @@ terminal if you would rather not see the prompt.
|
|
|
62
151
|
|
|
63
152
|
| Command | Covers | Needs a harness |
|
|
64
153
|
|---|---|---|
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
154
|
+
| `pnpm test` | style checks, syntax pass, type check, and unit tests | no |
|
|
155
|
+
| `pnpm run test:coverage` | unit tests with coverage | no |
|
|
156
|
+
| `pnpm run lint:style` | style and lint rules | no |
|
|
157
|
+
| `pnpm run lint:types` | the TypeScript check over `lib/` (`tsc --noEmit`) | no |
|
|
158
|
+
| `pnpm run probe` | filesystem behaviour against a real distro | yes |
|
|
159
|
+
| `pnpm run probe:sandbox` | what bubblewrap confines and what it does not | no |
|
|
160
|
+
| `pnpm run probe:sandbox-shell` | the confined executor through a real boot | yes |
|
|
161
|
+
| `pnpm run probe:terminal` | the terminal provider | yes |
|
|
162
|
+
| `pnpm run probe:substrate` | the agent filesystem substrate over a real `wsl.exe` transport | yes; inside the distro |
|
|
163
|
+
| `pnpm run probe:watch` | the in-distro watcher over a real directory | yes; inside the distro |
|
|
164
|
+
| `pnpm run probe:agent` | the resident-vs-one-shot fallback parity legs | yes; inside the distro |
|
|
165
|
+
| `pnpm run probe:exec` | the agent-backed execution handle: timeout, kill, cwd failure | yes; inside the distro |
|
|
166
|
+
| `pnpm run probe:missing-wsl` | how a `wslPath` that cannot start is reported | yes |
|
|
167
|
+
| `pnpm run probe:picker` | the picker's root level, its refusals and its cap | yes |
|
|
168
|
+
| `pnpm run probe:mode` | which POSIX-mode facts survive the share | no; Windows Node |
|
|
169
|
+
| `pnpm run probe:sandbox-off` | the documented opt-out on both providers | yes |
|
|
170
|
+
|
|
171
|
+
Every script above is wired in `package.json`. `pnpm test` runs `lint:style`,
|
|
172
|
+
`test:syntax`, `lint:types` and `test:unit` in that order. `prepublishOnly` runs `pnpm test` again
|
|
83
173
|
at publish time, so a broken gate stops a release before the upload.
|
|
84
174
|
|
|
85
|
-
CI runs `
|
|
86
|
-
`windows-latest`, and `
|
|
175
|
+
CI runs `pnpm test` on Node 22 and 24, on both `ubuntu-latest` and
|
|
176
|
+
`windows-latest`, and `pnpm run test:coverage` on Node 24. The Windows runners are
|
|
87
177
|
there because the plugin targets Windows; the coverage thresholds need Node 22.8 or
|
|
88
178
|
newer, which is why that job runs on one version.
|
|
89
179
|
|
|
@@ -117,7 +207,7 @@ start, because a leftover file from an earlier run changes the result.
|
|
|
117
207
|
|
|
118
208
|
## What has been verified
|
|
119
209
|
|
|
120
|
-
`
|
|
210
|
+
`pnpm test` covers the pure modules and runs a `--check` pass over every shipped
|
|
121
211
|
module. It cannot import the service modules, because those need DSH peer packages
|
|
122
212
|
that a bare checkout does not have. Only booting the harness closes that gap, which
|
|
123
213
|
is why the probes exist; the archived record's §15.4 documents the five rounds of
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Step 3 should print a layer named `# == dsh-plugin-wsl-env`, and `- id: terminal
|
|
|
28
28
|
|
|
29
29
|
Then open a folder under `\\wsl.localhost\<distro>\...` in the GUI. The picker lists every installed distro at its root level, and **New terminal** opens a shell in the distro.
|
|
30
30
|
|
|
31
|
-
You also need **bubblewrap inside the distro**: run `
|
|
31
|
+
You also need **bubblewrap inside the distro**: run `pnpm run bootstrap -- <distro> --install` (drop `--install` for a read-only check), or paste `wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap`. Without it every command fails closed, see [Sandbox](#sandbox).
|
|
32
32
|
|
|
33
33
|
Uninstall: `dsh plugin --profile wsl remove dsh-plugin-wsl-env`. Upgrade: run the same `add` command again.
|
|
34
34
|
|
|
@@ -73,7 +73,7 @@ Override a row by id in `$DSH_HOME/profiles/<name>/cordis.patch.yml`. The keys w
|
|
|
73
73
|
- **Paths and performance**: the model sees and operates on Linux paths
|
|
74
74
|
(`/home/...`) on the distro's own ext4. `/mnt/c` reaches the Windows disk over
|
|
75
75
|
9p — noticeably slow for many small files; keep heavy-IO projects on the
|
|
76
|
-
distro filesystem. `
|
|
76
|
+
distro filesystem. `pnpm run bootstrap -- <distro>` also reports whether
|
|
77
77
|
ripgrep, git and inotifywait (the search, snapshot and watch backends) are in place.
|
|
78
78
|
- **WSLENV passthrough**: WSL imports only the variables listed in `WSLENV`.
|
|
79
79
|
This plugin admits the managed `DSH_*` namespace by prefix, translating the
|
|
@@ -114,7 +114,7 @@ Commands are confined by `bubblewrap` inside the distro, and file writes are che
|
|
|
114
114
|
|
|
115
115
|
**`bubblewrap` is required, and it fails closed.** Without it every confined command reports `SANDBOX_UNAVAILABLE` instead of running unconfined. Set `sandbox: false` on either provider to opt out; the tool layer then tells the model these operations have no sandbox.
|
|
116
116
|
|
|
117
|
-
**The reported enforcement is `partial`, not `full`.** A process inside the distro can still run a Windows program through WSL interop, for example `/mnt/c/.../*.exe`, and bubblewrap does not govern it. `
|
|
117
|
+
**The reported enforcement is `partial`, not `full`.** A process inside the distro can still run a Windows program through WSL interop, for example `/mnt/c/.../*.exe`, and bubblewrap does not govern it. `pnpm run probe:sandbox` demonstrates the boundary on your machine.
|
|
118
118
|
|
|
119
119
|
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#sandbox) for the design, and [docs/LIMITATIONS.md](docs/LIMITATIONS.md) for everything the plugin does not do.
|
|
120
120
|
|
|
@@ -122,37 +122,37 @@ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#sandbox) for the design, and [do
|
|
|
122
122
|
|
|
123
123
|
| Symptom | Cause | Fix |
|
|
124
124
|
|---|---|---|
|
|
125
|
-
| every command reports `SANDBOX_UNAVAILABLE` | `bubblewrap` is not installed in the distro | run `
|
|
125
|
+
| every command reports `SANDBOX_UNAVAILABLE` | `bubblewrap` is not installed in the distro | run `pnpm run bootstrap -- <distro> --install`, or set `sandbox: false` on both providers |
|
|
126
126
|
| a command or write is refused outside the session folder | expected behaviour of `workspace-write` | accept the wider-permission offer, or open a session on the folder you need |
|
|
127
127
|
| writes are refused even inside the workspace | the session is in `read-only` mode | switch the Permissions selector |
|
|
128
128
|
| `dsh plugin add` warns that no layer was activated | the dependency was already installed, so `add` had nothing to record | run `dsh plugin --profile wsl remove dsh-plugin-wsl-env`, then add it again |
|
|
129
129
|
| the terminal still opens `cmd.exe` | the `terminal-controller` row from the layer did not apply | check that `dsh --profile wsl --dump-config` shows `shell: { path: wsl.exe, name: WSL }` |
|
|
130
130
|
| changes to `lib/` have no effect | ES module cache | restart the app |
|
|
131
131
|
| `link:\\wsl.localhost\...` leaves a broken symlink | pnpm cannot link a UNC path | link a Windows path instead; developing inside the distro needs the runtime mirror, see [Development](#development) |
|
|
132
|
-
| `glob` and `grep` are slow | a distro search without rg inside the distro falls back to rg's own "command not found"; a Windows-folder search is native and unaffected | run `
|
|
132
|
+
| `glob` and `grep` are slow | a distro search without rg inside the distro falls back to rg's own "command not found"; a Windows-folder search is native and unaffected | run `pnpm run bootstrap -- <distro> --install` (installs ripgrep); distro searches always run the distro's rg — they never walk the 9p share |
|
|
133
133
|
| the terminal reports `unknown` activity | only while the resident agent is out — distro terminals are observed from inside the distro (a `DSH_TERMINAL_ID` marker scanned in `/proc`: a shell alone is `idle`, a shell running anything is `busy`) | check the distro is running; idle terminals are reclaimed automatically after the controller's unattended timeout (2 h by default), and `terminalIdleReclaim: false` restores the close-by-hand posture |
|
|
134
134
|
| a result names an `FS_*` code | the code says what refused it, and what clears it | see the error-code table in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#error-codes) |
|
|
135
135
|
|
|
136
136
|
## Development
|
|
137
137
|
|
|
138
138
|
```bash
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
139
|
+
pnpm test # style and packaging checks, syntax check, unit tests
|
|
140
|
+
pnpm run test:coverage # the unit tests with coverage thresholds (Node 22.8+)
|
|
141
|
+
pnpm run probe:sandbox # measure inside the distro what bubblewrap does and does not confine
|
|
142
|
+
pnpm run probe # filesystem probe against a real distro (Windows + WSL only)
|
|
143
|
+
pnpm run probe:sandbox-shell # boot a real harness and drive the confined executor
|
|
144
|
+
pnpm run probe:terminal # open a PTY through the terminal provider
|
|
145
|
+
pnpm run probe:substrate # drive the agent filesystem substrate over a real wsl.exe transport (from inside the distro)
|
|
146
|
+
pnpm run probe:watch # arm the in-distro watcher over a real directory (from inside the distro)
|
|
147
|
+
pnpm run probe:agent # the resident-vs-one-shot fallback parity legs (from inside the distro)
|
|
148
|
+
pnpm run probe:exec # the agent-backed execution handle: timeout, kill, cwd failure (from inside the distro)
|
|
149
|
+
pnpm run probe:missing-wsl # boot a profile whose wslPath cannot start (Windows + WSL only)
|
|
150
|
+
pnpm run probe:picker # list the picker's root level, refusals and its cap (Windows + WSL only)
|
|
151
|
+
pnpm run probe:mode # which POSIX-mode facts survive the share (Windows node only)
|
|
152
|
+
pnpm run probe:sandbox-off # prove sandbox: false unconfines both providers (Windows + WSL only)
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
One `pnpm install` reproduces the pinned development dependencies before the first run: the 0.7.x tests import pinned `@deepseek-ai/*` packages (the composition and boot tests exercise the loader's real patch algorithm), while the runtime package itself still ships zero dependencies. pnpm is resolved through the `packageManager` field, so any corepack-enabled Node works. CI runs `pnpm test` on Node 22 and 24, on Linux and Windows, and `pnpm run test:coverage` on Node 24; `engines` matches the harness host's own floor (`^22.19.0 || >=24.0.0`).
|
|
156
156
|
|
|
157
157
|
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) has the file layout, the mounting, and the sandbox design. [CONTRIBUTING.md](CONTRIBUTING.md) has the development loop, the full gate list, and what has been verified.
|
|
158
158
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -185,7 +185,7 @@ selector read that value.
|
|
|
185
185
|
distro can still run a Windows program through WSL interop, for example
|
|
186
186
|
`/mnt/c/.../*.exe`. Bubblewrap does not govern that process.
|
|
187
187
|
|
|
188
|
-
`
|
|
188
|
+
`pnpm run probe:sandbox` demonstrates the boundary on your machine and records it as
|
|
189
189
|
`INFO`.
|
|
190
190
|
|
|
191
191
|
### Failure mode
|
|
@@ -250,7 +250,7 @@ distro checkout.
|
|
|
250
250
|
|
|
251
251
|
The Windows copy at `default-workspace/dsh-plugin-wsl` is a runtime mirror.
|
|
252
252
|
[`test/probe/sync-to-windows.sh`](../test/probe/sync-to-windows.sh) keeps it in
|
|
253
|
-
step with the checkout, and `
|
|
253
|
+
step with the checkout, and `pnpm run sync:windows` runs that script.
|
|
254
254
|
|
|
255
255
|
## Test layers
|
|
256
256
|
|
|
@@ -306,12 +306,15 @@ docs/ this file, the configuration and limitation references, the
|
|
|
306
306
|
```
|
|
307
307
|
|
|
308
308
|
`package.json` publishes a subset of this tree: the `files` list is the contract, and
|
|
309
|
-
`
|
|
309
|
+
`pnpm run lint:style` fails if an entry stops matching tracked content.
|
|
310
310
|
|
|
311
311
|
## Where the reasoning lives
|
|
312
312
|
|
|
313
313
|
- [`CHANGELOG.md`](../CHANGELOG.md) records what changed in each release. It also
|
|
314
314
|
names the documentation a reader would have acted on that is no longer true.
|
|
315
|
+
- [`REQUIREMENTS.zh.md`](REQUIREMENTS.zh.md) is the requirements register: every
|
|
316
|
+
documented behaviour commitment numbered, with where it is specified and what
|
|
317
|
+
verifies it, plus the open items (Chinese, maintainer-facing, not packaged).
|
|
315
318
|
- [`archive/engineering-record.zh.md`](archive/engineering-record.zh.md) is the
|
|
316
319
|
full engineering record, in Chinese: measured contracts, debugging rounds, and
|
|
317
320
|
the conclusions as they stood then.
|