dsh-plugin-wsl-env 0.13.0 → 0.13.2
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 +108 -0
- package/CONTRIBUTING.md +18 -7
- package/SUPPORT.md +1 -1
- package/cordis.patch.yml +2 -1
- package/docs/PEER-PARITY.md +24 -19
- package/docs/ROADMAP-1.0.md +3 -3
- package/docs/UPSTREAM-FSIO-EXPORT.md +3 -2
- package/lib/agent-exec.d.ts +9 -2
- package/lib/agent-exec.js +2 -2
- package/lib/index.js +8 -0
- package/lib/preflight.js +7 -1
- package/lib/shell-env.js +8 -1
- package/package.json +24 -24
- package/scripts/doctor.mjs +39 -6
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,114 @@ were part of the record.
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [0.13.2] - 2026-10-26
|
|
20
|
+
|
|
21
|
+
This release tracks DeepSeek Harness `0.2.1-rc.3`: the peer declarations become
|
|
22
|
+
floor ranges so a newer harness installs the plugin unchanged, and the
|
|
23
|
+
lockfile's transitive `undici` moves past a security advisory that Dependabot
|
|
24
|
+
could not clear on its own.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`undici` in the lockfile moves to the security-fixed `8.12.3` via a pnpm
|
|
29
|
+
override.** `undici` rides in only as an exact transitive pin of
|
|
30
|
+
`@deepseek-ai/dsh-http-proxy` — a package upstream controls — so Dependabot's
|
|
31
|
+
security job could resolve no patched version (it reports "the latest
|
|
32
|
+
possible version … is 8.11.2" and fails). `pnpm-workspace.yaml` now carries
|
|
33
|
+
`overrides: { undici: '>=8.12.3' }` (the pnpm 11 settings home; the
|
|
34
|
+
`package.json` `pnpm` field is no longer read), the lockfile swaps the one
|
|
35
|
+
package, and the override comment records that it is retired when upstream
|
|
36
|
+
moves the pin. Nothing under `src/` reads `undici`; the check chain is
|
|
37
|
+
unaffected.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- **The peer declarations become floor ranges, so a newer harness installs
|
|
42
|
+
the plugin unchanged.** The `@deepseek-ai/dsh-*` entries in
|
|
43
|
+
`peerDependencies` move from the exact pin to `>=0.2.1-rc.3`, and
|
|
44
|
+
`@deepseek-ai/cordis` / `@deepseek-ai/schemastery` widen from `~` to `^`
|
|
45
|
+
(`^4.0.5`, `^3.18.5`). An app that ships a newer harness release now
|
|
46
|
+
satisfies the peers without waiting for a plugin release — same-line rc
|
|
47
|
+
bumps and every stable release install cleanly; only a brand-new
|
|
48
|
+
prerelease line (`0.2.2-rc.1` against `>=0.2.1-rc.3`) still warns, because
|
|
49
|
+
semver admits a prerelease only against a comparator on its own tuple and
|
|
50
|
+
no range can promise an unpublished line. That residue is what the weekly
|
|
51
|
+
peer-canary is for: it still resolves the `next` dist-tag into a scratch
|
|
52
|
+
tree and flags when the line moves past a floor — the signal to re-run the
|
|
53
|
+
`docs/PEER-PARITY.md` comparison and raise the floor in the commit that
|
|
54
|
+
matches the harness release. The devDependencies keep their exact pins, so
|
|
55
|
+
the gates stay reproducible and the parity audit keeps naming one line.
|
|
56
|
+
`scripts/doctor.mjs` learned the new shapes (`peerSatisfied` now reads `>=`
|
|
57
|
+
and `^`, and its `compareVersions` orders prerelease identifiers by semver
|
|
58
|
+
§11 — `rc.10` above `rc.9`), `scripts/peer-canary.mjs` parses and preserves
|
|
59
|
+
the shapes, and `AGENTS.md`, `CONTRIBUTING.md`, `SUPPORT.md` and
|
|
60
|
+
`docs/PEER-PARITY.md` state the new policy.
|
|
61
|
+
|
|
62
|
+
- **The dev pin line moves to DeepSeek Harness `0.2.1-rc.3`.** Every
|
|
63
|
+
`@deepseek-ai/dsh-*` pin in `devDependencies` goes from `0.2.1-rc.2` to
|
|
64
|
+
`0.2.1-rc.3` — the release the `next` dist-tag resolves to — and
|
|
65
|
+
`pnpm-lock.yaml` refreshes with it. `@deepseek-ai/cordis` and
|
|
66
|
+
`@deepseek-ai/schemastery` stay at `4.0.5` / `3.18.5`: upstream's `vendor/`
|
|
67
|
+
did not change on this line.
|
|
68
|
+
|
|
69
|
+
No module under `src/` changed, and that is the finding. The three peers
|
|
70
|
+
this plugin subclasses or replicates ship byte-identical `lib/index.js`
|
|
71
|
+
files on rc.3 — `dsh-fs-local` (the fsio dialect, still 909 lines), `dsh-fs`
|
|
72
|
+
(the `FsVersion` brand) and `dsh-bash-local` (the spill machinery) — so
|
|
73
|
+
every line reference in `docs/PEER-PARITY.md` still names the same code;
|
|
74
|
+
only the tarballs' digests changed, and the parity doc now carries the
|
|
75
|
+
`0.2.1-rc.3` ones with the re-check dated 2026-10-26. That byte-identity is
|
|
76
|
+
also the evidence behind the new peer floor: the same code answers above
|
|
77
|
+
rc.3. Upstream's release notes for the line are UI- and LLM-side
|
|
78
|
+
(navigation entry customization, analytics, the official `/models`
|
|
79
|
+
listing): the one composition-visible change is a new `entry-customization`
|
|
80
|
+
UI row in the app's own `cordis.patch.yml`, which rides with the app's
|
|
81
|
+
bundle rather than this plugin's layer. `preset-wsl` needed no
|
|
82
|
+
regeneration — `packages/bundle/web-app/presets/standard.patch.yml` is
|
|
83
|
+
byte-identical on rc.3 — and the provenance comment in `cordis.patch.yml`
|
|
84
|
+
now names the rc.3 tree.
|
|
85
|
+
|
|
86
|
+
## [0.13.1] - 2026-10-19
|
|
87
|
+
|
|
88
|
+
A patch release restoring `wsl-shell-env`: the component mounts again, and the
|
|
89
|
+
managed `DSH_*` facts it contributes now reach commands on the default
|
|
90
|
+
resident-agent path.
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- **Upstream proposals filed.** The session-notice seam
|
|
95
|
+
([#10263](https://github.com/deepseek-ai/deepseek-harness/discussions/10263)),
|
|
96
|
+
the `spawnSpec` seam (#10261), the fsio export (#10262), the UNC-workspace bug
|
|
97
|
+
(#10260) and the WSL-workspaces RFC (#10264) now point at their discussions;
|
|
98
|
+
the local drafts carry the links.
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
- **`wsl-shell-env` no longer fails to activate.** The row sweeps the
|
|
103
|
+
live-agent list at mount to warm facts for sessions that opened before it,
|
|
104
|
+
but declared only `shellEnv` in `inject` and read `ctx.agents` as a property.
|
|
105
|
+
Cordis 4 refuses that read — `cannot get property "agents" without inject` —
|
|
106
|
+
so `apply()` rejected and the plugin manager listed the component as 异常
|
|
107
|
+
(`包含的组件 → 异常`), with the whole contribution missing from shell calls.
|
|
108
|
+
The sweep now reads the service through `ctx.get("agents")`, the sanctioned
|
|
109
|
+
access for a service this row does not inject: activation no longer depends
|
|
110
|
+
on an agent loop, and the sweep still runs when one is composed.
|
|
111
|
+
`wsl-preflight` carried the same property read inside a `try`/`catch`, where
|
|
112
|
+
the throw silently disabled its mount sweep; it gets the same fix.
|
|
113
|
+
`test/shell-env.test.mjs` and the real-cordis check in
|
|
114
|
+
`test/preflight.test.mjs` mount both rows against the guard.
|
|
115
|
+
|
|
116
|
+
- **The managed `DSH_*` namespace now reaches commands on the default
|
|
117
|
+
resident-agent path.** `ShellExecSpec.dshEnv` was forwarded only by the
|
|
118
|
+
one-shot `wsl.exe` fallback; `executeViaAgent` dropped it before
|
|
119
|
+
`agent.exec` — after the mount fix above, the row registered its facts but
|
|
120
|
+
no model shell call could see `DSH_HOME`, `DSH_SESSION_ID` or any
|
|
121
|
+
`DSH_WSL_*` value (`env | grep '^DSH_'` was empty). The handle now carries
|
|
122
|
+
the merged map (`env` first, `dshEnv` last — the same order the one-shot
|
|
123
|
+
path uses) into the agent's `SETENV` frames. `test/agent-exec.test.mjs` and
|
|
124
|
+
`test/provider.test.mjs` pin the frame, and both checks fail against the
|
|
125
|
+
previous build.
|
|
126
|
+
|
|
19
127
|
## [0.13.0] - 2026-10-19
|
|
20
128
|
|
|
21
129
|
This release ships the P0/P1 hardening batch: session-scoped `auto`
|
package/CONTRIBUTING.md
CHANGED
|
@@ -23,13 +23,24 @@ Developing and running the gates do have dependencies. The tests import pinned
|
|
|
23
23
|
algorithm, and the boot test starts a row through the real Loader — so a fresh
|
|
24
24
|
checkout needs one `pnpm install` before the gates pass.
|
|
25
25
|
|
|
26
|
-
Those pins are deliberate, and they
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
Those pins are deliberate, and they split by role. The `devDependencies` stay
|
|
27
|
+
exact so the gates and the `docs/PEER-PARITY.md` audit name one reproducible
|
|
28
|
+
line. The `peerDependencies` are floors — `>=` for the `@deepseek-ai/dsh-*`
|
|
29
|
+
packages, caret for `@deepseek-ai/cordis` and `@deepseek-ai/schemastery` — so
|
|
30
|
+
an app that ships a newer harness keeps installing the plugin without waiting
|
|
31
|
+
for a plugin release. A floor is still raised on purpose, not by drift: it
|
|
32
|
+
moves in the commit that matches a harness release, with the parity comparison
|
|
33
|
+
and the gate chain re-run, and the weekly peer-canary
|
|
34
|
+
(`.github/workflows/peer-canary.yml`) resolves the `next` dist-tag into a
|
|
35
|
+
scratch tree and flags when the line has moved past the floor. One semver
|
|
36
|
+
limit is worth knowing: a floor range cannot name future prerelease lines —
|
|
37
|
+
`0.2.2-rc.1` does not satisfy `>=0.2.1-rc.3`, because the spec admits a
|
|
38
|
+
prerelease only against a comparator on its own `[major, minor, patch]` tuple
|
|
39
|
+
— so a brand-new rc line warns until the floor rises; same-line rcs and every
|
|
40
|
+
stable release never do.
|
|
41
|
+
|
|
42
|
+
The plugin subclasses peer classes and mirrors peer protocol, so the range
|
|
43
|
+
says nothing about the parity claims: those are re-verified per floor raise.
|
|
33
44
|
|
|
34
45
|
pnpm runs the scripts in this repository, pinned through the `packageManager`
|
|
35
46
|
field (corepack resolves it). Treat `pnpm run <script>` as the reference
|
package/SUPPORT.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|---|---|
|
|
7
7
|
| Operating system | Windows 10 and Windows 11 |
|
|
8
8
|
| Linux side | WSL2 with at least one distro, and systemd as PID 1 with a lingering user manager (`loginctl enable-linger`) for the shipped session runtime. Set `sessionSandbox: false` on the preset's `wsl-shell` to keep the per-command sandbox instead |
|
|
9
|
-
| DSH packages | the `0.2.1-rc.
|
|
9
|
+
| DSH packages | the `0.2.1-rc.3`-or-newer packages the `peerDependencies` floors declare; an app on a newer harness installs unchanged |
|
|
10
10
|
|
|
11
11
|
The DSH row is checked weekly: `.github/workflows/peer-canary.yml` resolves the
|
|
12
12
|
`next` dist-tag of every pinned peer, installs that line into a scratch copy and
|
package/cordis.patch.yml
CHANGED
|
@@ -185,7 +185,8 @@
|
|
|
185
185
|
# Generated from the shipped standard preset so the embedded plan-mode
|
|
186
186
|
# section and every nested group survive byte-for-byte: this copy is
|
|
187
187
|
# `packages/bundle/web-app/presets/standard.patch.yml` of the
|
|
188
|
-
# `dsh-v0.2.1-rc.
|
|
188
|
+
# `dsh-v0.2.1-rc.3` source tree (unchanged since `0.2.1-rc.2`), changed
|
|
189
|
+
# in exactly three ways —
|
|
189
190
|
# 1. every shipped row mounts inside `wsl-env`, so the preset's tools see
|
|
190
191
|
# the distro's shell and fs (the realm is declared on the group);
|
|
191
192
|
# 2. `tool-bash` is enabled and `tool-pwsh` disabled outright, where the
|
package/docs/PEER-PARITY.md
CHANGED
|
@@ -7,18 +7,20 @@ same refusal dialect on both substrates. The **read path** holds end to end;
|
|
|
7
7
|
the **edit path** carries two declared divergences, listed at the end. This
|
|
8
8
|
file records the verification against the real peers.
|
|
9
9
|
|
|
10
|
-
- Peers: `@deepseek-ai/dsh-fs-local@0.2.1-rc.
|
|
11
|
-
|
|
10
|
+
- Peers: `@deepseek-ai/dsh-fs-local@0.2.1-rc.3` (the exact version the
|
|
11
|
+
devDependencies pin, and the floor the peer range `>=0.2.1-rc.3` was
|
|
12
|
+
verified against), plus `@deepseek-ai/dsh-fs@0.2.1-rc.3` for the
|
|
12
13
|
`FsVersion` brand the local package imports. Divergence 2 is measured
|
|
13
|
-
against a third peer, `@deepseek-ai/dsh-bash-local@0.2.1-rc.
|
|
14
|
+
against a third peer, `@deepseek-ai/dsh-bash-local@0.2.1-rc.3` — the shell
|
|
14
15
|
executor carries the collect-budget/spill machinery; `spill` and
|
|
15
16
|
`maxSpillBytes` do not appear in `dsh-fs-local` at all.
|
|
16
|
-
- The `0.2.1-rc.
|
|
17
|
-
peers' shipped `lib/index.js` are **byte-identical** to the `0.2.
|
|
17
|
+
- The `0.2.1-rc.3` move leaves every line reference below intact: all three
|
|
18
|
+
peers' shipped `lib/index.js` are **byte-identical** to the `0.2.1-rc.2`
|
|
18
19
|
ones (`cmp` clean; `dsh-fs-local`'s is still 909 lines), so only the
|
|
19
|
-
tarballs' digests changed
|
|
20
|
-
|
|
21
|
-
`
|
|
20
|
+
tarballs' digests changed — upstream shipped the manifests' version fields
|
|
21
|
+
and nothing else in these packages. `dsh-fs@0.2.1-rc.3` still has no
|
|
22
|
+
`./invariant` export subpath (dropped back at `0.2.1-rc.2`); this plugin
|
|
23
|
+
imports neither it nor `dsh-invariants`.
|
|
22
24
|
- What "not imported" means: the peer's **fsio module** is not reachable from
|
|
23
25
|
the package's exports, so `lib/fsio-text.js` reimplements it and imports
|
|
24
26
|
nothing from it (the export is proposed in `docs/UPSTREAM-FSIO-EXPORT.md`).
|
|
@@ -33,24 +35,24 @@ file records the verification against the real peers.
|
|
|
33
35
|
|
|
34
36
|
```sh
|
|
35
37
|
mkdir -p /tmp/peer-audit && cd /tmp/peer-audit
|
|
36
|
-
npm pack @deepseek-ai/dsh-fs-local@0.2.1-rc.
|
|
37
|
-
openssl dgst -sha512 -binary deepseek-ai-dsh-fs-local-0.2.1-rc.
|
|
38
|
-
tar xzf deepseek-ai-dsh-fs-local-0.2.1-rc.
|
|
38
|
+
npm pack @deepseek-ai/dsh-fs-local@0.2.1-rc.3 @deepseek-ai/dsh-fs@0.2.1-rc.3 --pack-destination .
|
|
39
|
+
openssl dgst -sha512 -binary deepseek-ai-dsh-fs-local-0.2.1-rc.3.tgz | base64 -w0
|
|
40
|
+
tar xzf deepseek-ai-dsh-fs-local-0.2.1-rc.3.tgz # -> package/lib/index.js
|
|
39
41
|
wc -l package/lib/index.js # expect 909
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
The tarballs this audit compared, by content — the same values
|
|
43
|
-
`npm view <pkg>@0.2.1-rc.
|
|
45
|
+
`npm view <pkg>@0.2.1-rc.3 dist.integrity` reports:
|
|
44
46
|
|
|
45
47
|
```
|
|
46
|
-
@deepseek-ai/dsh-fs-local@0.2.1-rc.
|
|
47
|
-
@deepseek-ai/dsh-fs@0.2.1-rc.
|
|
48
|
+
@deepseek-ai/dsh-fs-local@0.2.1-rc.3 sha512-bB2CwCqvYAdAKl28+5XXMRRq1NBtRmYX3wNOpJ2JlGBm7uj2Aix9Vl9rFig4AQcXXd5Bk738b5KNsOIkbXy+XQ==
|
|
49
|
+
@deepseek-ai/dsh-fs@0.2.1-rc.3 sha512-tENLWe471WlAXMc13J4cns8xU6lFyu1smwpkSKR85vhGr6FyWZhTlpVohZYm66LbwKTmSWiDhd9Urk7VxN9B9w==
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
Each row of the table below names the peer's `lib/index.js` line and the
|
|
51
53
|
literal both sides must produce. Verified 2026-10-03; the read/edit split
|
|
52
54
|
re-verified 2026-10-06; the whole table re-checked against `0.2.1-rc.2`,
|
|
53
|
-
2026-10-17.
|
|
55
|
+
2026-10-17 and against `0.2.1-rc.3`, 2026-10-26.
|
|
54
56
|
|
|
55
57
|
## Verdict
|
|
56
58
|
|
|
@@ -93,7 +95,7 @@ declared extensions, listed at the end.
|
|
|
93
95
|
2. **Per-stream truncation flags.** The EXEC frame carries an output budget;
|
|
94
96
|
the RES line reports which streams the agent cut, surfaced as honest
|
|
95
97
|
`truncated` flags. The collect-budget/spill machinery of the shell peer,
|
|
96
|
-
`@deepseek-ai/dsh-bash-local@0.2.1-rc.
|
|
98
|
+
`@deepseek-ai/dsh-bash-local@0.2.1-rc.3` (`lib/index.js:30`, `:110`), is the
|
|
97
99
|
spill-file path we deliberately do not run.
|
|
98
100
|
3. **The transport around the ops** (ACK, KILL, the confined resident) is
|
|
99
101
|
protocol this package owns; the peer has no agent.
|
|
@@ -120,9 +122,12 @@ declared extensions, listed at the end.
|
|
|
120
122
|
|
|
121
123
|
## Maintenance
|
|
122
124
|
|
|
123
|
-
When `package.json
|
|
124
|
-
new tarballs, check the sha512, and walk the table's peer line
|
|
125
|
-
against the replicas. The
|
|
125
|
+
When a peer floor or dev pin moves in `package.json`, re-run the comparison
|
|
126
|
+
above — pack the new tarballs, check the sha512, and walk the table's peer line
|
|
127
|
+
references against the replicas. The weekly peer-canary resolves the `next`
|
|
128
|
+
dist-tag into a scratch tree and runs the gate chain against it, so the signal
|
|
129
|
+
to raise the floor arrives before a user reports a drift. The fastest canary:
|
|
130
|
+
stat a file, list its directory, and
|
|
126
131
|
compare the versions (`test/fsio-agent.test.mjs` asserts the intra-distro half
|
|
127
132
|
of this); the strings above are the other half. The split dialect has its own
|
|
128
133
|
regression: `test/fs-substrate.test.mjs` asserts the missing-file refusal, and
|
package/docs/ROADMAP-1.0.md
CHANGED
|
@@ -339,9 +339,9 @@ command.
|
|
|
339
339
|
- [x] The preflight checks bwrap, systemd PID 1, `systemd-run` and Linger.
|
|
340
340
|
- [x] The warning is visibly attached to the session through the
|
|
341
341
|
runtime-context entry (not only logs).
|
|
342
|
-
- [ ] A GUI notice/banner renders it: upstream
|
|
343
|
-
|
|
344
|
-
|
|
342
|
+
- [ ] A GUI notice/banner renders it: filed upstream as
|
|
343
|
+
[#10263](https://github.com/deepseek-ai/deepseek-harness/discussions/10263);
|
|
344
|
+
until a seam lands, the advisory stays in the log and runtime context.
|
|
345
345
|
- [x] The warning never installs anything and never changes fail-closed
|
|
346
346
|
behavior.
|
|
347
347
|
- [x] A session with a usable environment produces no warning.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Upstream proposal: export the fsio text helpers from `dsh-fs-local`
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
|
|
3
|
+
Status: filed upstream as
|
|
4
|
+
[deepseek-ai/deepseek-harness#10262](https://github.com/deepseek-ai/deepseek-harness/discussions/10262)
|
|
5
|
+
(Ideas); the filing convention is [UPSTREAM-TERMINAL-TITLE.md](UPSTREAM-TERMINAL-TITLE.md).
|
|
5
6
|
Targeting `deepseek-harness` @ `packages/fs/fs-local`, the `0.2.1-rc.x` line
|
|
6
7
|
this plugin pins exactly (`@deepseek-ai/dsh-fs-local@0.2.1-rc.2`, in both the
|
|
7
8
|
dev and peer dependency blocks of `package.json`). This is the Shape-A
|
package/lib/agent-exec.d.ts
CHANGED
|
@@ -129,10 +129,10 @@ export interface AgentExecutionHandle {
|
|
|
129
129
|
* @returns the execution handle (`status`, `exitCode`, `signal`, `observed`,
|
|
130
130
|
* `done`, `readOutput`, `kill`, `result`).
|
|
131
131
|
*/
|
|
132
|
-
export declare function agentExecutionHandle({ agent, cwd, argv, timeoutMs, maxOutputBytes, signal, onStarted }: {
|
|
132
|
+
export declare function agentExecutionHandle({ agent, cwd, argv, timeoutMs, maxOutputBytes, env, signal, onStarted }: {
|
|
133
133
|
/**
|
|
134
134
|
* The {@link WslAgent}-like runner: `exec` accepting
|
|
135
|
-
* `{cwd, argv, timeoutMs, signal}` and resolving `{exitCode, stdout, stderr}`;
|
|
135
|
+
* `{cwd, argv, timeoutMs, env, signal}` and resolving `{exitCode, stdout, stderr}`;
|
|
136
136
|
* throws `AgentUnavailableError` when out.
|
|
137
137
|
*/
|
|
138
138
|
agent: AgentExecRunner;
|
|
@@ -150,6 +150,13 @@ export declare function agentExecutionHandle({ agent, cwd, argv, timeoutMs, maxO
|
|
|
150
150
|
* stream as `truncated: true`.
|
|
151
151
|
*/
|
|
152
152
|
maxOutputBytes?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Environment for this command: the caller's entries with the managed
|
|
155
|
+
* `DSH_*` snapshot merged last. The agent applies it as `SETENV` frames
|
|
156
|
+
* immediately before the command's `EXEC` (see `WslAgent.exec`); dropping it
|
|
157
|
+
* here is what kept every managed fact out of the resident-agent path.
|
|
158
|
+
*/
|
|
159
|
+
env?: Record<string, string>;
|
|
153
160
|
/** The caller's cancellation. */
|
|
154
161
|
signal?: AbortSignal;
|
|
155
162
|
/**
|
package/lib/agent-exec.js
CHANGED
|
@@ -54,7 +54,7 @@ function memoryReader(buffer) {
|
|
|
54
54
|
* @returns the execution handle (`status`, `exitCode`, `signal`, `observed`,
|
|
55
55
|
* `done`, `readOutput`, `kill`, `result`).
|
|
56
56
|
*/
|
|
57
|
-
export function agentExecutionHandle({ agent, cwd, argv, timeoutMs = 0, maxOutputBytes = 0, signal, onStarted }) {
|
|
57
|
+
export function agentExecutionHandle({ agent, cwd, argv, timeoutMs = 0, maxOutputBytes = 0, env, signal, onStarted }) {
|
|
58
58
|
// Our own controller is the kill channel; the caller's signal races it.
|
|
59
59
|
const controller = new AbortController();
|
|
60
60
|
let timedOut = false;
|
|
@@ -72,7 +72,7 @@ export function agentExecutionHandle({ agent, cwd, argv, timeoutMs = 0, maxOutpu
|
|
|
72
72
|
controller.abort(new Error(BASH_TIMEOUT_REASON));
|
|
73
73
|
}, timeoutMs).unref?.();
|
|
74
74
|
}
|
|
75
|
-
const settle = agent.exec({ cwd, argv, timeoutMs, maxOutputBytes, signal: controller.signal }).then((result) => ({ ok: true, result }), (error) => {
|
|
75
|
+
const settle = agent.exec({ cwd, argv, timeoutMs, maxOutputBytes, env, signal: controller.signal }).then((result) => ({ ok: true, result }), (error) => {
|
|
76
76
|
// A workdir the distro cannot enter is the command's own failure, not
|
|
77
77
|
// the agent's: synthesize the relay-shaped stderr the one-shot path
|
|
78
78
|
// produces, so the executor's `workdirFailure` check reports it the
|
package/lib/index.js
CHANGED
|
@@ -831,6 +831,13 @@ export class WslShellExecutor extends LocalBashExecutor {
|
|
|
831
831
|
const argv = runtime !== undefined
|
|
832
832
|
? this.distroArgv(resolved, shell)
|
|
833
833
|
: confined !== undefined ? confined.argv : this.distroArgv(resolved, shell);
|
|
834
|
+
// The managed `DSH_*` snapshot merges last, exactly as the one-shot path's
|
|
835
|
+
// spawn env does (`ShellExecSpec`: `dshEnv` wins over `env`). The agent
|
|
836
|
+
// applies these as SETENV frames immediately before the command's EXEC, so
|
|
837
|
+
// the resident path now carries `DSH_HOME`, `DSH_SESSION_ID` and the
|
|
838
|
+
// `DSH_WSL_*` facts too — without this the whole registry was invisible on
|
|
839
|
+
// the default path while the one-shot fallback forwarded it.
|
|
840
|
+
const env = { ...resolved.env, ...resolved.dshEnv };
|
|
834
841
|
const execution = agentExecutionHandle({
|
|
835
842
|
agent,
|
|
836
843
|
// The one-shot path converts the spec's workdir into the distro's
|
|
@@ -839,6 +846,7 @@ export class WslShellExecutor extends LocalBashExecutor {
|
|
|
839
846
|
// reach `chdir` (that is the bug this line fixes).
|
|
840
847
|
cwd: toLinuxPath(resolved.workdir, { distro }),
|
|
841
848
|
argv,
|
|
849
|
+
env: Object.keys(env).length > 0 ? env : undefined,
|
|
842
850
|
timeoutMs: resolved.timeoutMs,
|
|
843
851
|
// The agent enforces the collect budget distro-side (the EXEC frame
|
|
844
852
|
// carries it); `maxSpillBytes` stays one-shot-only — the agent path has
|
package/lib/preflight.js
CHANGED
|
@@ -176,8 +176,14 @@ export async function apply(ctx, config = {}) {
|
|
|
176
176
|
// Mount order is not a guarantee: a session created during startup can finish
|
|
177
177
|
// before this service mounts and miss the event entirely (dsh-headless does
|
|
178
178
|
// exactly that, and auto-preset sweeps for the same reason).
|
|
179
|
+
//
|
|
180
|
+
// `agents` is optional (this row injects nothing), so read it with `ctx.get`:
|
|
181
|
+
// a property read of an undeclared service throws in cordis 4, and the catch
|
|
182
|
+
// below would turn that into a silent no-op — exactly the sweep this block
|
|
183
|
+
// exists to perform.
|
|
179
184
|
try {
|
|
180
|
-
|
|
185
|
+
const agents = ctx.get("agents");
|
|
186
|
+
for (const agent of agents?.list() ?? [])
|
|
181
187
|
void check(agent);
|
|
182
188
|
}
|
|
183
189
|
catch (error) {
|
package/lib/shell-env.js
CHANGED
|
@@ -157,7 +157,14 @@ export async function apply(ctx, config = {}) {
|
|
|
157
157
|
ensurePorts(parsed.distro);
|
|
158
158
|
};
|
|
159
159
|
const off = lifecycle.on?.("agent/created", (payload) => consider(payload?.agent));
|
|
160
|
-
|
|
160
|
+
// The live-agent sweep is a convenience, not a dependency: `inject` names
|
|
161
|
+
// only `shellEnv`, so reading `ctx.agents` as a property makes cordis 4
|
|
162
|
+
// throw "cannot get property \"agents\" without inject" and marks the whole
|
|
163
|
+
// component failed. `ctx.get` is the sanctioned read for a service this
|
|
164
|
+
// plugin did not inject, and it answers undefined when no agent loop is
|
|
165
|
+
// composed — the row still activates and resolves facts per shell call.
|
|
166
|
+
const live = ctx.get("agents");
|
|
167
|
+
for (const agent of live?.list?.() ?? [])
|
|
161
168
|
consider(agent);
|
|
162
169
|
const dispose = ctx.shellEnv.register({
|
|
163
170
|
name: "dsh-plugin-wsl-env",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-wsl-env",
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.13.2",
|
|
4
|
+
"description": "让 DeepSeek Harness 以 WSL 子系统作为会话的执行环境:shell、文件读写与命令执行均在子系统内完成,目录选择器与 GUI 终端同样定位到子系统路径,并通过子系统内的 bubblewrap 对命令进行沙箱隔离",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"dsh",
|
|
@@ -125,9 +125,9 @@
|
|
|
125
125
|
"lint:types": "tsc --noEmit",
|
|
126
126
|
"build": "node scripts/build.mjs",
|
|
127
127
|
"lint:build": "node test/build-freshness.mjs",
|
|
128
|
-
"test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=85 --test-coverage-branches=84 --test-coverage-functions=70 --test-coverage-exclude='scripts/**' test/agent-exec.test.mjs test/agent-fs-script.test.mjs test/agent-protocol.test.mjs test/agent.test.mjs test/fs-substrate.test.mjs test/fs.boot.test.mjs test/composition.test.mjs test/provider.test.mjs test/file-reference-wsl.test.mjs test/fsio-agent.test.mjs test/fsio-text.test.mjs test/git-route.test.mjs test/listing.test.mjs test/paths.test.mjs test/ports.test.mjs test/preset-choice.test.mjs test/probe-cache.test.mjs test/preflight.test.mjs test/sandbox.test.mjs test/search-exec.test.mjs test/search-route.test.mjs test/shell.test.mjs test/terminal-activity.test.mjs test/terminal.test.mjs test/watcher.test.mjs test/wsl.test.mjs test/fs-decisions.test.mjs test/session-scope.test.mjs test/session-runtime.test.mjs test/native-agent.test.mjs test/peers.test.mjs test/agent-shared.test.mjs test/distro-route.test.mjs test/fs-routing.test.mjs test/doctor.test.mjs test/profile-preset.test.mjs test/packaging.test.mjs test/peer-canary.test.mjs",
|
|
128
|
+
"test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=85 --test-coverage-branches=84 --test-coverage-functions=70 --test-coverage-exclude='scripts/**' test/agent-exec.test.mjs test/agent-fs-script.test.mjs test/agent-protocol.test.mjs test/agent.test.mjs test/fs-substrate.test.mjs test/fs.boot.test.mjs test/composition.test.mjs test/provider.test.mjs test/file-reference-wsl.test.mjs test/fsio-agent.test.mjs test/fsio-text.test.mjs test/git-route.test.mjs test/listing.test.mjs test/paths.test.mjs test/ports.test.mjs test/preset-choice.test.mjs test/probe-cache.test.mjs test/preflight.test.mjs test/sandbox.test.mjs test/search-exec.test.mjs test/search-route.test.mjs test/shell.test.mjs test/shell-env.test.mjs test/terminal-activity.test.mjs test/terminal.test.mjs test/watcher.test.mjs test/wsl.test.mjs test/fs-decisions.test.mjs test/session-scope.test.mjs test/session-runtime.test.mjs test/native-agent.test.mjs test/peers.test.mjs test/agent-shared.test.mjs test/distro-route.test.mjs test/fs-routing.test.mjs test/doctor.test.mjs test/profile-preset.test.mjs test/packaging.test.mjs test/peer-canary.test.mjs",
|
|
129
129
|
"test:syntax": "node test/syntax.mjs",
|
|
130
|
-
"test:unit": "node --test test/fs.boot.test.mjs test/fsio-text.test.mjs test/composition.test.mjs test/provider.test.mjs test/file-reference-wsl.test.mjs test/paths.test.mjs test/listing.test.mjs test/shell.test.mjs test/terminal.test.mjs test/probe-cache.test.mjs test/preflight.test.mjs test/preset-choice.test.mjs test/sandbox.test.mjs test/agent-protocol.test.mjs test/agent.test.mjs test/agent-fs-script.test.mjs test/fsio-agent.test.mjs test/fs-substrate.test.mjs test/agent-exec.test.mjs test/ports.test.mjs test/watcher.test.mjs test/wsl.test.mjs test/fs-decisions.test.mjs test/git-route.test.mjs test/search-route.test.mjs test/search-exec.test.mjs test/terminal-activity.test.mjs test/session-scope.test.mjs test/session-runtime.test.mjs test/native-agent.test.mjs test/peers.test.mjs test/agent-shared.test.mjs test/distro-route.test.mjs test/fs-routing.test.mjs test/doctor.test.mjs test/profile-preset.test.mjs test/packaging.test.mjs test/peer-canary.test.mjs",
|
|
130
|
+
"test:unit": "node --test test/fs.boot.test.mjs test/fsio-text.test.mjs test/composition.test.mjs test/provider.test.mjs test/file-reference-wsl.test.mjs test/paths.test.mjs test/listing.test.mjs test/shell.test.mjs test/shell-env.test.mjs test/terminal.test.mjs test/probe-cache.test.mjs test/preflight.test.mjs test/preset-choice.test.mjs test/sandbox.test.mjs test/agent-protocol.test.mjs test/agent.test.mjs test/agent-fs-script.test.mjs test/fsio-agent.test.mjs test/fs-substrate.test.mjs test/agent-exec.test.mjs test/ports.test.mjs test/watcher.test.mjs test/wsl.test.mjs test/fs-decisions.test.mjs test/git-route.test.mjs test/search-route.test.mjs test/search-exec.test.mjs test/terminal-activity.test.mjs test/session-scope.test.mjs test/session-runtime.test.mjs test/native-agent.test.mjs test/peers.test.mjs test/agent-shared.test.mjs test/distro-route.test.mjs test/fs-routing.test.mjs test/doctor.test.mjs test/profile-preset.test.mjs test/packaging.test.mjs test/peer-canary.test.mjs",
|
|
131
131
|
"lint:style": "node test/style.mjs",
|
|
132
132
|
"lint:links": "node scripts/check-links.mjs",
|
|
133
133
|
"prepublishOnly": "pnpm test",
|
|
@@ -179,32 +179,32 @@
|
|
|
179
179
|
"@deepseek-ai/cordis": "4.0.5",
|
|
180
180
|
"@deepseek-ai/cordis-plugin-include": "*",
|
|
181
181
|
"@deepseek-ai/cordis-plugin-loader": "*",
|
|
182
|
-
"@deepseek-ai/dsh-bash-local": "0.2.1-rc.
|
|
183
|
-
"@deepseek-ai/dsh-fs": "0.2.1-rc.
|
|
184
|
-
"@deepseek-ai/dsh-fs-local": "0.2.1-rc.
|
|
185
|
-
"@deepseek-ai/dsh-fs-sandbox": "0.2.1-rc.
|
|
186
|
-
"@deepseek-ai/dsh-host-directory-picker": "0.2.1-rc.
|
|
187
|
-
"@deepseek-ai/dsh-sandbox": "0.2.1-rc.
|
|
188
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.
|
|
189
|
-
"@deepseek-ai/dsh-subprocess": "0.2.1-rc.
|
|
190
|
-
"@deepseek-ai/dsh-subprocess-local": "0.2.1-rc.
|
|
191
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.
|
|
182
|
+
"@deepseek-ai/dsh-bash-local": "0.2.1-rc.3",
|
|
183
|
+
"@deepseek-ai/dsh-fs": "0.2.1-rc.3",
|
|
184
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-rc.3",
|
|
185
|
+
"@deepseek-ai/dsh-fs-sandbox": "0.2.1-rc.3",
|
|
186
|
+
"@deepseek-ai/dsh-host-directory-picker": "0.2.1-rc.3",
|
|
187
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-rc.3",
|
|
188
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.3",
|
|
189
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-rc.3",
|
|
190
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.1-rc.3",
|
|
191
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.3",
|
|
192
192
|
"@types/js-yaml": "^4.0.9",
|
|
193
193
|
"@types/node": "~22.20.5",
|
|
194
194
|
"js-yaml": "4.3.2",
|
|
195
195
|
"typescript": "~6.0.3"
|
|
196
196
|
},
|
|
197
197
|
"peerDependencies": {
|
|
198
|
-
"@deepseek-ai/cordis": "
|
|
199
|
-
"@deepseek-ai/dsh-bash-local": "0.2.1-rc.
|
|
200
|
-
"@deepseek-ai/dsh-fs": "0.2.1-rc.
|
|
201
|
-
"@deepseek-ai/dsh-fs-local": "0.2.1-rc.
|
|
202
|
-
"@deepseek-ai/dsh-host-directory-picker": "0.2.1-rc.
|
|
203
|
-
"@deepseek-ai/dsh-sandbox": "0.2.1-rc.
|
|
204
|
-
"@deepseek-ai/dsh-subprocess": "0.2.1-rc.
|
|
205
|
-
"@deepseek-ai/dsh-subprocess-local": "0.2.1-rc.
|
|
206
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.
|
|
207
|
-
"@deepseek-ai/schemastery": "
|
|
198
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
199
|
+
"@deepseek-ai/dsh-bash-local": ">=0.2.1-rc.3",
|
|
200
|
+
"@deepseek-ai/dsh-fs": ">=0.2.1-rc.3",
|
|
201
|
+
"@deepseek-ai/dsh-fs-local": ">=0.2.1-rc.3",
|
|
202
|
+
"@deepseek-ai/dsh-host-directory-picker": ">=0.2.1-rc.3",
|
|
203
|
+
"@deepseek-ai/dsh-sandbox": ">=0.2.1-rc.3",
|
|
204
|
+
"@deepseek-ai/dsh-subprocess": ">=0.2.1-rc.3",
|
|
205
|
+
"@deepseek-ai/dsh-subprocess-local": ">=0.2.1-rc.3",
|
|
206
|
+
"@deepseek-ai/dsh-typert-protocol": ">=0.2.1-rc.3",
|
|
207
|
+
"@deepseek-ai/schemastery": "^3.18.5"
|
|
208
208
|
},
|
|
209
209
|
"peerDependenciesMeta": {
|
|
210
210
|
"@deepseek-ai/cordis": {
|
package/scripts/doctor.mjs
CHANGED
|
@@ -145,19 +145,24 @@ function parseArgs(argv) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
|
-
* Whether an installed version satisfies one peer range. The
|
|
149
|
-
*
|
|
150
|
-
*
|
|
148
|
+
* Whether an installed version satisfies one peer range. The manifest's peer
|
|
149
|
+
* ranges are floor ranges (`>=`) and caret ranges (`^`), and the
|
|
150
|
+
* devDependencies carry exact pins — those three shapes are all this
|
|
151
|
+
* understands; anything else is reported as unchecked rather than guessed at.
|
|
152
|
+
* The floor is the plugin's own compatibility claim, so it compares plainly
|
|
153
|
+
* against the floor version; the semver gate that excludes other lines'
|
|
154
|
+
* prereleases is an installer concern, not this check's.
|
|
151
155
|
* @param {string} installed - the installed version.
|
|
152
156
|
* @param {string} range - the declared peer range.
|
|
153
157
|
* @returns {boolean | undefined} true/false, or undefined for an unsupported shape.
|
|
154
158
|
*/
|
|
155
159
|
export function peerSatisfied(installed, range) {
|
|
156
|
-
if (range.startsWith("
|
|
160
|
+
if (range.startsWith(">=")) return compareVersions(installed, range.slice(2)) >= 0;
|
|
161
|
+
if (range.startsWith("^")) {
|
|
157
162
|
const [major, minor, patch] = range.slice(1).split(".");
|
|
158
163
|
return compareVersions(installed, `${major}.${minor}.${patch ?? "0"}`) >= 0
|
|
159
164
|
&& installed.split(".")[0] === major
|
|
160
|
-
&& installed.split(".")[1] === minor;
|
|
165
|
+
&& (major !== "0" || installed.split(".")[1] === minor);
|
|
161
166
|
}
|
|
162
167
|
if (/^[0-9]/.test(range)) return installed === range;
|
|
163
168
|
return undefined;
|
|
@@ -188,7 +193,35 @@ export function compareVersions(left, right) {
|
|
|
188
193
|
if (a.pre === b.pre) return 0;
|
|
189
194
|
if (a.pre === "") return 1;
|
|
190
195
|
if (b.pre === "") return -1;
|
|
191
|
-
return a.pre
|
|
196
|
+
return comparePrerelease(a.pre, b.pre);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Compare two prerelease strings by semver §11: dot-separated identifiers,
|
|
201
|
+
* numeric ones numerically and below alphanumeric ones, the rest lexically,
|
|
202
|
+
* a longer list above its own prefix. Plain `localeCompare` would order
|
|
203
|
+
* `rc.10` below `rc.9`.
|
|
204
|
+
* @param {string} left - the first prerelease.
|
|
205
|
+
* @param {string} right - the second prerelease.
|
|
206
|
+
* @returns {number} negative, zero or positive like a comparator.
|
|
207
|
+
*/
|
|
208
|
+
function comparePrerelease(left, right) {
|
|
209
|
+
const a = left.split(".");
|
|
210
|
+
const b = right.split(".");
|
|
211
|
+
for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
|
|
212
|
+
const x = a[i];
|
|
213
|
+
const y = b[i];
|
|
214
|
+
if (x === undefined) return -1;
|
|
215
|
+
if (y === undefined) return 1;
|
|
216
|
+
if (x === y) continue;
|
|
217
|
+
const numericX = /^\d+$/.test(x);
|
|
218
|
+
const numericY = /^\d+$/.test(y);
|
|
219
|
+
if (numericX && numericY) return Number(x) - Number(y);
|
|
220
|
+
if (numericX) return -1;
|
|
221
|
+
if (numericY) return 1;
|
|
222
|
+
return x < y ? -1 : 1;
|
|
223
|
+
}
|
|
224
|
+
return 0;
|
|
192
225
|
}
|
|
193
226
|
|
|
194
227
|
/**
|