dsh-plugin-wsl-env 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -14
- package/CONTRIBUTING.md +25 -33
- package/README.md +10 -10
- package/SECURITY.md +6 -4
- package/SUPPORT.md +8 -8
- package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
- package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
- package/agent/wsl-agent.sh +4 -5
- package/cordis.patch.yml +8 -1
- package/docs/ARCHITECTURE.md +42 -35
- package/docs/COMPATIBILITY.md +121 -0
- package/docs/CONFIGURATION.md +254 -273
- package/docs/LIMITATIONS.md +61 -26
- package/docs/MIGRATION.md +16 -13
- package/docs/PARITY.md +4 -3
- package/docs/PERFORMANCE.md +11 -9
- package/docs/QUICKSTART.md +46 -21
- package/docs/README.zh.md +7 -7
- package/docs/RELEASING.md +257 -0
- package/docs/REQUIREMENTS.zh.md +77 -0
- package/docs/ROADMAP-1.0.md +105 -61
- package/docs/ROADMAP.md +15 -9
- package/docs/UPSTREAM-FILE-REFERENCE-SEAM.md +3 -3
- package/docs/UPSTREAM-FSIO-EXPORT.md +7 -7
- package/docs/UPSTREAM-OPEN-IN-APP.md +57 -0
- package/docs/UPSTREAM-SESSION-NOTICE.md +4 -4
- package/docs/VALUE.zh.md +144 -0
- package/docs/adr/0002-native-agent.md +348 -0
- package/docs/root-fs-routing.md +186 -0
- package/docs/screenshots/picker.png +0 -0
- package/docs/screenshots/preview.png +0 -0
- package/docs/screenshots/sandbox.png +0 -0
- package/docs/screenshots/terminal.png +0 -0
- package/lib/agent-exec.d.ts +2 -3
- package/lib/agent-shared.js +1 -0
- package/lib/agent.d.ts +19 -8
- package/lib/agent.js +73 -13
- package/lib/fs-routing.d.ts +2 -1
- package/lib/fs-routing.js +13 -1
- package/lib/fs-substrate.d.ts +11 -9
- package/lib/fs-substrate.js +6 -9
- package/lib/fsio-agent.d.ts +2 -3
- package/lib/fsio-agent.js +2 -3
- package/lib/index.d.ts +20 -13
- package/lib/index.js +18 -11
- package/lib/session-runtime.d.ts +5 -3
- package/lib/session-runtime.js +5 -4
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +13 -4
- package/scripts/diagnose.sh +1 -1
- package/scripts/doctor.mjs +3 -3
package/lib/fs-substrate.js
CHANGED
|
@@ -13,12 +13,9 @@
|
|
|
13
13
|
* create publishes with the distro-side no-replace link, a real atomic
|
|
14
14
|
* primitive that refuses to replace. An overwrite or edit carries the
|
|
15
15
|
* version it was based on INTO the write op, and the agent re-verifies it
|
|
16
|
-
* with a fresh stat
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* narrows the share backend's check-then-write window from a host round
|
|
20
|
-
* trip to kernel-adjacent — the honest wording; "closed" was true only
|
|
21
|
-
* for the create case.
|
|
16
|
+
* with a fresh stat before the rename: an earlier conflicting write is
|
|
17
|
+
* refused with `FS_STALE_VERSION`. A writer between that stat and the
|
|
18
|
+
* rename can still be overwritten; only guarded create is atomic.
|
|
22
19
|
* 2. A symlink needs no retry machinery: every identity is resolved where the
|
|
23
20
|
* symlinks live.
|
|
24
21
|
*
|
|
@@ -196,7 +193,7 @@ export class AgentSubstrate {
|
|
|
196
193
|
* guard, best-effort diff basis, atomic publication, outcome shape. The
|
|
197
194
|
* guard is FORWARDED — a guarded create publishes with the distro-side
|
|
198
195
|
* no-replace link (atomic), and a `replaceIfVersion` write carries its
|
|
199
|
-
* version into the op, where the agent re-verifies it
|
|
196
|
+
* version into the op, where the agent re-verifies it before
|
|
200
197
|
* the rename and refuses with `FS_STALE_VERSION` on a mismatch.
|
|
201
198
|
*
|
|
202
199
|
* @param target - the resolved target.
|
|
@@ -247,8 +244,8 @@ export class AgentSubstrate {
|
|
|
247
244
|
* Edit a text file with the host backend's orchestration: probe, version
|
|
248
245
|
* guard, LF-normalized read, literal replacement, line-ending restore,
|
|
249
246
|
* atomic publication, outcome shape. The version the edit was based on
|
|
250
|
-
* rides the write op and is re-verified distro-side
|
|
251
|
-
* rename,
|
|
247
|
+
* rides the write op and is re-verified distro-side before the
|
|
248
|
+
* rename, narrowing but not eliminating the concurrent-write race.
|
|
252
249
|
*
|
|
253
250
|
* @param target - the resolved target.
|
|
254
251
|
* @param edit - the literal request.
|
package/lib/fsio-agent.d.ts
CHANGED
|
@@ -244,9 +244,8 @@ export declare class DistroFs {
|
|
|
244
244
|
* Publish `content` over `linuxPath` the POSIX way: the agent stages into a
|
|
245
245
|
* private 0700 sibling, fsyncs, chmods, and renames — or hard links for a
|
|
246
246
|
* guarded create. A `replace` publish carrying `expectedVersion` is
|
|
247
|
-
* re-verified by the agent against a fresh stat
|
|
248
|
-
*
|
|
249
|
-
* stale write refuses with `FS_STALE_VERSION` instead of landing.
|
|
247
|
+
* re-verified by the agent against a fresh stat before the rename. A change
|
|
248
|
+
* after that check can still be overwritten by the rename.
|
|
250
249
|
*
|
|
251
250
|
* @param linuxPath - the target path inside the distro.
|
|
252
251
|
* @param content - the full new content.
|
package/lib/fsio-agent.js
CHANGED
|
@@ -407,9 +407,8 @@ export class DistroFs {
|
|
|
407
407
|
* Publish `content` over `linuxPath` the POSIX way: the agent stages into a
|
|
408
408
|
* private 0700 sibling, fsyncs, chmods, and renames — or hard links for a
|
|
409
409
|
* guarded create. A `replace` publish carrying `expectedVersion` is
|
|
410
|
-
* re-verified by the agent against a fresh stat
|
|
411
|
-
*
|
|
412
|
-
* stale write refuses with `FS_STALE_VERSION` instead of landing.
|
|
410
|
+
* re-verified by the agent against a fresh stat before the rename. A change
|
|
411
|
+
* after that check can still be overwritten by the rename.
|
|
413
412
|
*
|
|
414
413
|
* @param linuxPath - the target path inside the distro.
|
|
415
414
|
* @param content - the full new content.
|
package/lib/index.d.ts
CHANGED
|
@@ -407,6 +407,8 @@ export declare class WslShellExecutor extends LocalBashExecutor {
|
|
|
407
407
|
sessionRuntimes: SessionRuntimes;
|
|
408
408
|
/** The runtimes this executor holds, by registry key. */
|
|
409
409
|
sessionRuntimeEntries: Map<string, SessionRuntimeEntry>;
|
|
410
|
+
/** Isolates calls that have no session identity. */
|
|
411
|
+
sessionRuntimeOwner: string;
|
|
410
412
|
/** The preflight's host runner; tests substitute it. */
|
|
411
413
|
sessionScopeProbe: (argv: string[]) => Promise<string>;
|
|
412
414
|
/** Distros whose session-runtime preflight has succeeded. */
|
|
@@ -491,7 +493,7 @@ export declare class WslShellExecutor extends LocalBashExecutor {
|
|
|
491
493
|
executionAgent(distro: string): WslAgent;
|
|
492
494
|
/**
|
|
493
495
|
* The persistent session runtime for one policy, when `sessionSandbox` is
|
|
494
|
-
* on. The executor holds one reference per `(distro, workspace, policy)` and
|
|
496
|
+
* on. The executor holds one reference per `(session, distro, workspace, policy)` and
|
|
495
497
|
* releases them when the session's scope disposes.
|
|
496
498
|
*
|
|
497
499
|
* @param policy - the resolved file-effect policy, when the executor has one.
|
|
@@ -502,6 +504,7 @@ export declare class WslShellExecutor extends LocalBashExecutor {
|
|
|
502
504
|
sessionRuntimeFor(policy: {
|
|
503
505
|
mode: string;
|
|
504
506
|
workspaceRoot: string;
|
|
507
|
+
sessionId?: string;
|
|
505
508
|
} | undefined, resolved: WslExecSpec, distro: string): SessionRuntimeEntry | undefined;
|
|
506
509
|
/**
|
|
507
510
|
* Run the session runtime's preflight once per executor. A failure is a
|
|
@@ -815,11 +818,11 @@ export declare class WslFileSystem extends LocalFileSystem {
|
|
|
815
818
|
sandbox: z<boolean, boolean, "defined">;
|
|
816
819
|
/**
|
|
817
820
|
* Run confined mutations in the session's systemd user scope — the same
|
|
818
|
-
* runtime the shell executor acquires for the same
|
|
819
|
-
* policy
|
|
820
|
-
*
|
|
821
|
-
* the
|
|
822
|
-
*
|
|
821
|
+
* runtime the shell executor acquires for the same session, distro,
|
|
822
|
+
* workspace and policy — instead of a per-policy confined resident. A
|
|
823
|
+
* missing session ID keeps the providers separate. The kernel enforcement
|
|
824
|
+
* is the runtime's mount profile; the scope stops when its providers
|
|
825
|
+
* release it. Requires systemd + `Linger`, like `wsl-shell`'s
|
|
823
826
|
* `sessionSandbox`; the preflight refuses with the remedy. Off for direct
|
|
824
827
|
* constructions; on in the shipped preset.
|
|
825
828
|
*/
|
|
@@ -890,11 +893,11 @@ export declare class WslFileSystem extends LocalFileSystem {
|
|
|
890
893
|
sandbox: z<boolean, boolean, "defined">;
|
|
891
894
|
/**
|
|
892
895
|
* Run confined mutations in the session's systemd user scope — the same
|
|
893
|
-
* runtime the shell executor acquires for the same
|
|
894
|
-
* policy
|
|
895
|
-
*
|
|
896
|
-
* the
|
|
897
|
-
*
|
|
896
|
+
* runtime the shell executor acquires for the same session, distro,
|
|
897
|
+
* workspace and policy — instead of a per-policy confined resident. A
|
|
898
|
+
* missing session ID keeps the providers separate. The kernel enforcement
|
|
899
|
+
* is the runtime's mount profile; the scope stops when its providers
|
|
900
|
+
* release it. Requires systemd + `Linger`, like `wsl-shell`'s
|
|
898
901
|
* `sessionSandbox`; the preflight refuses with the remedy. Off for direct
|
|
899
902
|
* constructions; on in the shipped preset.
|
|
900
903
|
*/
|
|
@@ -962,6 +965,8 @@ export declare class WslFileSystem extends LocalFileSystem {
|
|
|
962
965
|
sessionRuntimes: SessionRuntimes;
|
|
963
966
|
/** The runtimes this provider holds, by registry key. */
|
|
964
967
|
sessionRuntimeEntries: Map<string, SessionRuntimeEntry>;
|
|
968
|
+
/** Isolates calls that have no session identity. */
|
|
969
|
+
sessionRuntimeOwner: string;
|
|
965
970
|
/** The preflight's host runner; tests substitute it. */
|
|
966
971
|
sessionScopeProbe: (argv: string[]) => Promise<string>;
|
|
967
972
|
/** Distros whose session-runtime preflight has succeeded. */
|
|
@@ -1038,6 +1043,7 @@ export declare class WslFileSystem extends LocalFileSystem {
|
|
|
1038
1043
|
sessionRuntimeFor(policy: {
|
|
1039
1044
|
mode: string;
|
|
1040
1045
|
workspaceRoot: string;
|
|
1046
|
+
sessionId?: string;
|
|
1041
1047
|
}, distro: string): SessionRuntimeEntry | undefined;
|
|
1042
1048
|
/**
|
|
1043
1049
|
* Run the session runtime's preflight once per provider. Only success is
|
|
@@ -1082,6 +1088,7 @@ export declare class WslFileSystem extends LocalFileSystem {
|
|
|
1082
1088
|
mutationAgent(policy: {
|
|
1083
1089
|
mode: string;
|
|
1084
1090
|
workspaceRoot: string;
|
|
1091
|
+
sessionId?: string;
|
|
1085
1092
|
} | undefined, forDistro?: string): Promise<WslAgent>;
|
|
1086
1093
|
/**
|
|
1087
1094
|
* Map a substrate error onto the peer's `FsError`, so the tool layer reads
|
|
@@ -1292,8 +1299,8 @@ export declare class WslFileSystem extends LocalFileSystem {
|
|
|
1292
1299
|
* Fence the write by the per-call policy, then publish through the
|
|
1293
1300
|
* substrate. The fence's fresh target is the one checked and written, so a
|
|
1294
1301
|
* refusal costs no I/O and cannot be raced by a swapped symlink; the guard
|
|
1295
|
-
* rides the write op, where the agent re-verifies it
|
|
1296
|
-
*
|
|
1302
|
+
* rides the write op, where the agent re-verifies it before the rename;
|
|
1303
|
+
* the confined resident enforces the mount boundary in the kernel.
|
|
1297
1304
|
*
|
|
1298
1305
|
* @param target - the resolved target.
|
|
1299
1306
|
* @param content - the full new file content.
|
package/lib/index.js
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* @module dsh-plugin-wsl
|
|
41
41
|
*/
|
|
42
42
|
import z from "@deepseek-ai/schemastery";
|
|
43
|
+
import { randomUUID } from "node:crypto";
|
|
43
44
|
import { LocalBashExecutor } from "@deepseek-ai/dsh-bash-local";
|
|
44
45
|
import { LocalFileSystem } from "@deepseek-ai/dsh-fs-local";
|
|
45
46
|
import { FsError } from "@deepseek-ai/dsh-fs";
|
|
@@ -355,6 +356,7 @@ export class WslShellExecutor extends LocalBashExecutor {
|
|
|
355
356
|
this.processFacts = new Map();
|
|
356
357
|
this.sessionRuntimes = sessionRuntimes;
|
|
357
358
|
this.sessionRuntimeEntries = new Map();
|
|
359
|
+
this.sessionRuntimeOwner = randomUUID();
|
|
358
360
|
this.sessionScopeProbe = (argv) => runCapture(argv);
|
|
359
361
|
this.sessionRuntimePreflight = new Set();
|
|
360
362
|
this.autoSessionScope = new Map();
|
|
@@ -485,7 +487,7 @@ export class WslShellExecutor extends LocalBashExecutor {
|
|
|
485
487
|
}
|
|
486
488
|
/**
|
|
487
489
|
* The persistent session runtime for one policy, when `sessionSandbox` is
|
|
488
|
-
* on. The executor holds one reference per `(distro, workspace, policy)` and
|
|
490
|
+
* on. The executor holds one reference per `(session, distro, workspace, policy)` and
|
|
489
491
|
* releases them when the session's scope disposes.
|
|
490
492
|
*
|
|
491
493
|
* @param policy - the resolved file-effect policy, when the executor has one.
|
|
@@ -499,11 +501,13 @@ export class WslShellExecutor extends LocalBashExecutor {
|
|
|
499
501
|
return undefined;
|
|
500
502
|
const mode = policy?.mode ?? "danger-full-access";
|
|
501
503
|
const workspaceRoot = policy?.workspaceRoot ?? (configuredCwd(this.config) || resolved.workdir);
|
|
502
|
-
const
|
|
504
|
+
const sessionId = policy?.sessionId || resolved.dshEnv?.DSH_SESSION_ID || this.sessionRuntimeOwner;
|
|
505
|
+
const key = SessionRuntimes.key({ sessionId, distro, workspaceRoot, mode, maskWindowsDrive: effectiveMaskWindowsDrive(config) });
|
|
503
506
|
const held = this.sessionRuntimeEntries.get(key);
|
|
504
507
|
if (held !== undefined)
|
|
505
508
|
return held;
|
|
506
509
|
const entry = this.sessionRuntimes.acquire({
|
|
510
|
+
sessionId,
|
|
507
511
|
distro,
|
|
508
512
|
wslPath: config.wslPath,
|
|
509
513
|
workspaceRoot,
|
|
@@ -876,7 +880,7 @@ export class WslShellExecutor extends LocalBashExecutor {
|
|
|
876
880
|
: confined !== undefined ? confined.argv : this.distroArgv(resolved, shell);
|
|
877
881
|
// The managed `DSH_*` snapshot merges last, exactly as the one-shot path's
|
|
878
882
|
// spawn env does (`ShellExecSpec`: `dshEnv` wins over `env`). The agent
|
|
879
|
-
// applies these
|
|
883
|
+
// applies these to the individual command, so
|
|
880
884
|
// the resident path now carries `DSH_HOME`, `DSH_SESSION_ID` and the
|
|
881
885
|
// `DSH_WSL_*` facts too — without this the whole registry was invisible on
|
|
882
886
|
// the default path while the one-shot fallback forwarded it.
|
|
@@ -1074,11 +1078,11 @@ export class WslFileSystem extends LocalFileSystem {
|
|
|
1074
1078
|
sandbox: z.boolean().default(true),
|
|
1075
1079
|
/**
|
|
1076
1080
|
* Run confined mutations in the session's systemd user scope — the same
|
|
1077
|
-
* runtime the shell executor acquires for the same
|
|
1078
|
-
* policy
|
|
1079
|
-
*
|
|
1080
|
-
* the
|
|
1081
|
-
*
|
|
1081
|
+
* runtime the shell executor acquires for the same session, distro,
|
|
1082
|
+
* workspace and policy — instead of a per-policy confined resident. A
|
|
1083
|
+
* missing session ID keeps the providers separate. The kernel enforcement
|
|
1084
|
+
* is the runtime's mount profile; the scope stops when its providers
|
|
1085
|
+
* release it. Requires systemd + `Linger`, like `wsl-shell`'s
|
|
1082
1086
|
* `sessionSandbox`; the preflight refuses with the remedy. Off for direct
|
|
1083
1087
|
* constructions; on in the shipped preset.
|
|
1084
1088
|
*/
|
|
@@ -1147,6 +1151,7 @@ export class WslFileSystem extends LocalFileSystem {
|
|
|
1147
1151
|
this.agentInstances = new Map();
|
|
1148
1152
|
this.sessionRuntimes = sessionRuntimes;
|
|
1149
1153
|
this.sessionRuntimeEntries = new Map();
|
|
1154
|
+
this.sessionRuntimeOwner = randomUUID();
|
|
1150
1155
|
this.sessionScopeProbe = (argv) => runCapture(argv);
|
|
1151
1156
|
this.sessionRuntimePreflight = new Set();
|
|
1152
1157
|
this.autoSessionScope = new Map();
|
|
@@ -1263,11 +1268,13 @@ export class WslFileSystem extends LocalFileSystem {
|
|
|
1263
1268
|
if (!config.sessionSandbox)
|
|
1264
1269
|
return undefined;
|
|
1265
1270
|
const workspaceRoot = policy.workspaceRoot || config.cwd;
|
|
1266
|
-
const
|
|
1271
|
+
const sessionId = policy.sessionId || this.sessionRuntimeOwner;
|
|
1272
|
+
const key = SessionRuntimes.key({ sessionId, distro, workspaceRoot, mode: policy.mode, maskWindowsDrive: config.maskWindowsDrive });
|
|
1267
1273
|
const held = this.sessionRuntimeEntries.get(key);
|
|
1268
1274
|
if (held !== undefined)
|
|
1269
1275
|
return held;
|
|
1270
1276
|
const entry = this.sessionRuntimes.acquire({
|
|
1277
|
+
sessionId,
|
|
1271
1278
|
distro,
|
|
1272
1279
|
wslPath: config.wslPath,
|
|
1273
1280
|
workspaceRoot,
|
|
@@ -1751,8 +1758,8 @@ export class WslFileSystem extends LocalFileSystem {
|
|
|
1751
1758
|
* Fence the write by the per-call policy, then publish through the
|
|
1752
1759
|
* substrate. The fence's fresh target is the one checked and written, so a
|
|
1753
1760
|
* refusal costs no I/O and cannot be raced by a swapped symlink; the guard
|
|
1754
|
-
* rides the write op, where the agent re-verifies it
|
|
1755
|
-
*
|
|
1761
|
+
* rides the write op, where the agent re-verifies it before the rename;
|
|
1762
|
+
* the confined resident enforces the mount boundary in the kernel.
|
|
1756
1763
|
*
|
|
1757
1764
|
* @param target - the resolved target.
|
|
1758
1765
|
* @param content - the full new file content.
|
package/lib/session-runtime.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The session-scoped runtime registry (ADR 0001).
|
|
3
3
|
*
|
|
4
|
-
* One long-lived agent per `(distro, workspaceRoot, policy)`, shared by the
|
|
4
|
+
* One long-lived agent per `(sessionId, distro, workspaceRoot, policy)`, shared by the
|
|
5
5
|
* providers that need it and refcounted: the first `acquire` spawns the agent
|
|
6
6
|
* inside a systemd user scope, the last `release` closes the agent and stops
|
|
7
7
|
* the scope (the cgroup then reaps any background dev server a command left
|
|
@@ -43,6 +43,8 @@ export declare function probeSessionRuntime({ wslPath, distro, run }: {
|
|
|
43
43
|
}): Promise<string | null>;
|
|
44
44
|
/** One request for a session runtime. */
|
|
45
45
|
export interface SessionRuntimeRequest {
|
|
46
|
+
/** The session identity; absent requests each get a separate runtime. */
|
|
47
|
+
sessionId?: string;
|
|
46
48
|
/** The distro the runtime lives in. */
|
|
47
49
|
distro: string;
|
|
48
50
|
/** The `wsl.exe` path; defaults to `wsl.exe`. */
|
|
@@ -125,14 +127,14 @@ export declare class SessionRuntimes {
|
|
|
125
127
|
*/
|
|
126
128
|
constructor(options?: SessionRuntimeOptions);
|
|
127
129
|
/**
|
|
128
|
-
* The key a request addresses: distro, workspace, mode and mask. A policy
|
|
130
|
+
* The key a request addresses: session, distro, workspace, mode and mask. A policy
|
|
129
131
|
* change is a different runtime, because the mount profile is fixed for the
|
|
130
132
|
* agent's lifetime.
|
|
131
133
|
*
|
|
132
134
|
* @param request - the runtime request.
|
|
133
135
|
* @returns the registry key.
|
|
134
136
|
*/
|
|
135
|
-
static key(request: Pick<SessionRuntimeRequest, "distro" | "workspaceRoot" | "mode" | "maskWindowsDrive">): string;
|
|
137
|
+
static key(request: Pick<SessionRuntimeRequest, "distro" | "workspaceRoot" | "mode" | "maskWindowsDrive" | "sessionId">): string;
|
|
136
138
|
/**
|
|
137
139
|
* Acquire the runtime for one key, creating it on first use.
|
|
138
140
|
*
|
package/lib/session-runtime.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The session-scoped runtime registry (ADR 0001).
|
|
3
3
|
*
|
|
4
|
-
* One long-lived agent per `(distro, workspaceRoot, policy)`, shared by the
|
|
4
|
+
* One long-lived agent per `(sessionId, distro, workspaceRoot, policy)`, shared by the
|
|
5
5
|
* providers that need it and refcounted: the first `acquire` spawns the agent
|
|
6
6
|
* inside a systemd user scope, the last `release` closes the agent and stops
|
|
7
7
|
* the scope (the cgroup then reaps any background dev server a command left
|
|
@@ -83,6 +83,7 @@ const spawnSessionAgent = (options) => {
|
|
|
83
83
|
idleMs: options.idleMs,
|
|
84
84
|
leaseMs: options.leaseMs,
|
|
85
85
|
signalable: false,
|
|
86
|
+
serialRequests: false,
|
|
86
87
|
spawnTransport: ({ wslPath, distro, argvPrefix }) => spawn(wslPath, ["-d", distro, "--exec", ...argvPrefix, native.path], {
|
|
87
88
|
stdio: ["pipe", "pipe", "pipe"],
|
|
88
89
|
windowsHide: true,
|
|
@@ -137,7 +138,7 @@ export class SessionRuntimes {
|
|
|
137
138
|
this.token = options.token ?? defaultToken;
|
|
138
139
|
}
|
|
139
140
|
/**
|
|
140
|
-
* The key a request addresses: distro, workspace, mode and mask. A policy
|
|
141
|
+
* The key a request addresses: session, distro, workspace, mode and mask. A policy
|
|
141
142
|
* change is a different runtime, because the mount profile is fixed for the
|
|
142
143
|
* agent's lifetime.
|
|
143
144
|
*
|
|
@@ -145,7 +146,7 @@ export class SessionRuntimes {
|
|
|
145
146
|
* @returns the registry key.
|
|
146
147
|
*/
|
|
147
148
|
static key(request) {
|
|
148
|
-
return [request.distro, request.workspaceRoot, request.mode, request.maskWindowsDrive === true ? "masked" : "visible"].join("\u0000");
|
|
149
|
+
return [request.sessionId ?? "", request.distro, request.workspaceRoot, request.mode, request.maskWindowsDrive === true ? "masked" : "visible"].join("\u0000");
|
|
149
150
|
}
|
|
150
151
|
/**
|
|
151
152
|
* Acquire the runtime for one key, creating it on first use.
|
|
@@ -154,7 +155,7 @@ export class SessionRuntimes {
|
|
|
154
155
|
* @returns the live entry; the caller holds one reference.
|
|
155
156
|
*/
|
|
156
157
|
acquire(request) {
|
|
157
|
-
const key = SessionRuntimes.key(request);
|
|
158
|
+
const key = SessionRuntimes.key({ ...request, sessionId: request.sessionId || defaultToken() });
|
|
158
159
|
const existing = this.runtimes.get(key);
|
|
159
160
|
if (existing !== undefined) {
|
|
160
161
|
existing.refs += 1;
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "WSL execution environment",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Opens the project workspace inside a WSL Linux instance: command execution, shell scripts and file access all run in the Linux environment for the full Linux shell toolchain, with commands confined by bubblewrap."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "WSL 执行环境",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "在 WSL 的 Linux 实例中打开项目工作区,在对应 Linux 空间中进行命令执行、运行 shell 脚本和文件访问,以获得全套 Linux Shell 工具链收益,并通过 bubblewrap 对命令进行沙箱隔离"
|
|
5
5
|
}
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-wsl-env",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.16.0",
|
|
4
|
+
"description": "在 WSL 的 Linux 实例中打开项目工作区,在对应 Linux 空间中进行命令执行、运行 shell 脚本和文件访问,以获得全套 Linux Shell 工具链收益,并通过 bubblewrap 对命令进行沙箱隔离",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"dsh",
|
|
@@ -96,9 +96,17 @@
|
|
|
96
96
|
"README.md",
|
|
97
97
|
"docs/README.zh.md",
|
|
98
98
|
"docs/QUICKSTART.md",
|
|
99
|
+
"docs/screenshots/*.png",
|
|
99
100
|
"docs/PERFORMANCE.md",
|
|
101
|
+
"docs/COMPATIBILITY.md",
|
|
100
102
|
"docs/CONFIGURATION.md",
|
|
101
103
|
"docs/LIMITATIONS.md",
|
|
104
|
+
"docs/RELEASING.md",
|
|
105
|
+
"docs/REQUIREMENTS.zh.md",
|
|
106
|
+
"docs/root-fs-routing.md",
|
|
107
|
+
"docs/VALUE.zh.md",
|
|
108
|
+
"docs/UPSTREAM-OPEN-IN-APP.md",
|
|
109
|
+
"docs/adr/0002-native-agent.md",
|
|
102
110
|
"docs/PARITY.md",
|
|
103
111
|
"docs/PEER-PARITY.md",
|
|
104
112
|
"docs/UPSTREAM-FSIO-EXPORT.md",
|
|
@@ -125,9 +133,9 @@
|
|
|
125
133
|
"lint:types": "tsc --noEmit",
|
|
126
134
|
"build": "node scripts/build.mjs",
|
|
127
135
|
"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/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",
|
|
136
|
+
"test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=85 --test-coverage-branches=84 --test-coverage-functions=70 --test-coverage-exclude='scripts/**' --test-coverage-exclude='test/**' 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 test/probe-runner.test.mjs",
|
|
129
137
|
"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/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",
|
|
138
|
+
"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 test/probe-runner.test.mjs",
|
|
131
139
|
"lint:style": "node test/style.mjs",
|
|
132
140
|
"lint:links": "node scripts/check-links.mjs",
|
|
133
141
|
"prepublishOnly": "pnpm test",
|
|
@@ -139,6 +147,7 @@
|
|
|
139
147
|
"probe:terminal": "bash test/probe/terminal.sh",
|
|
140
148
|
"probe:sandbox": "bash test/probe/sandbox.sh",
|
|
141
149
|
"probe:sandbox-shell": "bash test/probe/sandbox-shell.sh",
|
|
150
|
+
"probe:interop-shell": "bash test/probe/interop-shell.sh",
|
|
142
151
|
"sync:windows": "bash test/probe/sync-to-windows.sh",
|
|
143
152
|
"probe:watch": "bash test/probe/watch.sh",
|
|
144
153
|
"probe:agent": "bash test/probe/agent.sh",
|
package/scripts/diagnose.sh
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# to change the machine. `--fix`/`--install` are refused here on purpose —
|
|
4
4
|
# diagnostics collected for a bug report must not repair anything.
|
|
5
5
|
#
|
|
6
|
-
# pnpm run diagnose
|
|
6
|
+
# pnpm run diagnose [distro] [--json]
|
|
7
7
|
#
|
|
8
8
|
# Doctor is `scripts/doctor.mjs`; see its header for what it covers. Exit status
|
|
9
9
|
# follows doctor: 0 when no check failed, 1 when one did.
|
package/scripts/doctor.mjs
CHANGED
|
@@ -255,7 +255,7 @@ function installRemedy(distro, family) {
|
|
|
255
255
|
* @returns {number} the exit status.
|
|
256
256
|
*/
|
|
257
257
|
function rejectReadOnly() {
|
|
258
|
-
console.error("doctor: --read-only refuses --fix/--install; run `pnpm run doctor --
|
|
258
|
+
console.error("doctor: --read-only refuses --fix/--install; run `pnpm run doctor --fix` instead");
|
|
259
259
|
return 2;
|
|
260
260
|
}
|
|
261
261
|
|
|
@@ -334,7 +334,7 @@ async function main(options) {
|
|
|
334
334
|
const bwrapPath = (await inDistro(distro, "command -v bwrap")).stdout.trim();
|
|
335
335
|
const install = installRemedy(distro, family);
|
|
336
336
|
if (bwrapPath.length === 0) {
|
|
337
|
-
check("sandbox.bwrap", "fail", "bubblewrap", "missing; every confined command fails closed without it", `${install}, or run "pnpm run bootstrap
|
|
337
|
+
check("sandbox.bwrap", "fail", "bubblewrap", "missing; every confined command fails closed without it", `${install}, or run "pnpm run bootstrap ${distro} --install"`);
|
|
338
338
|
} else {
|
|
339
339
|
check("sandbox.bwrap", "ok", "bubblewrap", bwrapPath);
|
|
340
340
|
const userns = await inDistro(distro, "bwrap --ro-bind / / -- true");
|
|
@@ -516,7 +516,7 @@ async function main(options) {
|
|
|
516
516
|
if (/^\s*-\s*id:\s*(wsl-shell|wsl-fs)\s*$/m.test(text)) trapped.push(entry.name);
|
|
517
517
|
}
|
|
518
518
|
if (trapped.length > 0) {
|
|
519
|
-
check("config.preset-trap", "warn", "Profile patches", `nested rows patched by id in: ${trapped.join(", ")}`, "wsl-shell and wsl-fs live inside preset-wsl; an id patch matches nothing and is
|
|
519
|
+
check("config.preset-trap", "warn", "Profile patches", `nested rows patched by id in: ${trapped.join(", ")}`, "wsl-shell and wsl-fs live inside preset-wsl; an id patch matches nothing and is ignored with a warning. Run \"pnpm run preset --check <patch>\" to get the complete block, or see docs/CONFIGURATION.md#configure-wsl-shell-and-wsl-fs");
|
|
520
520
|
} else {
|
|
521
521
|
check("config.profiles", "ok", "Profile patches", `${seen} profile patch(es) scanned`);
|
|
522
522
|
}
|