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.
Files changed (52) hide show
  1. package/CHANGELOG.md +87 -14
  2. package/CONTRIBUTING.md +25 -33
  3. package/README.md +10 -10
  4. package/SECURITY.md +6 -4
  5. package/SUPPORT.md +8 -8
  6. package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
  7. package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
  8. package/agent/wsl-agent.sh +4 -5
  9. package/cordis.patch.yml +8 -1
  10. package/docs/ARCHITECTURE.md +42 -35
  11. package/docs/COMPATIBILITY.md +121 -0
  12. package/docs/CONFIGURATION.md +254 -273
  13. package/docs/LIMITATIONS.md +61 -26
  14. package/docs/MIGRATION.md +16 -13
  15. package/docs/PARITY.md +4 -3
  16. package/docs/PERFORMANCE.md +11 -9
  17. package/docs/QUICKSTART.md +46 -21
  18. package/docs/README.zh.md +7 -7
  19. package/docs/RELEASING.md +257 -0
  20. package/docs/REQUIREMENTS.zh.md +77 -0
  21. package/docs/ROADMAP-1.0.md +105 -61
  22. package/docs/ROADMAP.md +15 -9
  23. package/docs/UPSTREAM-FILE-REFERENCE-SEAM.md +3 -3
  24. package/docs/UPSTREAM-FSIO-EXPORT.md +7 -7
  25. package/docs/UPSTREAM-OPEN-IN-APP.md +57 -0
  26. package/docs/UPSTREAM-SESSION-NOTICE.md +4 -4
  27. package/docs/VALUE.zh.md +144 -0
  28. package/docs/adr/0002-native-agent.md +348 -0
  29. package/docs/root-fs-routing.md +186 -0
  30. package/docs/screenshots/picker.png +0 -0
  31. package/docs/screenshots/preview.png +0 -0
  32. package/docs/screenshots/sandbox.png +0 -0
  33. package/docs/screenshots/terminal.png +0 -0
  34. package/lib/agent-exec.d.ts +2 -3
  35. package/lib/agent-shared.js +1 -0
  36. package/lib/agent.d.ts +19 -8
  37. package/lib/agent.js +73 -13
  38. package/lib/fs-routing.d.ts +2 -1
  39. package/lib/fs-routing.js +13 -1
  40. package/lib/fs-substrate.d.ts +11 -9
  41. package/lib/fs-substrate.js +6 -9
  42. package/lib/fsio-agent.d.ts +2 -3
  43. package/lib/fsio-agent.js +2 -3
  44. package/lib/index.d.ts +20 -13
  45. package/lib/index.js +18 -11
  46. package/lib/session-runtime.d.ts +5 -3
  47. package/lib/session-runtime.js +5 -4
  48. package/locale/en.json +1 -1
  49. package/locale/zh.json +1 -1
  50. package/package.json +13 -4
  51. package/scripts/diagnose.sh +1 -1
  52. package/scripts/doctor.mjs +3 -3
@@ -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 one syscall before the rename: a concurrent writer
17
- * (an editor save, `git stash`, another session) wins, and the stale
18
- * write refuses with `FS_STALE_VERSION` instead of clobbering. That
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 one syscall before
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 one syscall before the
251
- * rename, so a concurrent writer wins instead of being clobbered.
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.
@@ -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 one syscall before the
248
- * rename: a concurrent writer (an editor save, a `git stash`) wins, and the
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 one syscall before the
411
- * rename: a concurrent writer (an editor save, a `git stash`) wins, and the
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 (distro, workspace,
819
- * policy) — instead of a per-policy confined resident. The kernel
820
- * enforcement is the runtime's mount profile, so commands and writes see
821
- * the same `/tmp` and workspace; the scope stops when the session's
822
- * providers release it. Requires systemd + `Linger`, like `wsl-shell`'s
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 (distro, workspace,
894
- * policy) — instead of a per-policy confined resident. The kernel
895
- * enforcement is the runtime's mount profile, so commands and writes see
896
- * the same `/tmp` and workspace; the scope stops when the session's
897
- * providers release it. Requires systemd + `Linger`, like `wsl-shell`'s
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 one syscall before the
1296
- * rename and the confined resident enforces it in the kernel.
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 key = SessionRuntimes.key({ distro, workspaceRoot, mode, maskWindowsDrive: effectiveMaskWindowsDrive(config) });
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 as SETENV frames immediately before the command's EXEC, so
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 (distro, workspace,
1078
- * policy) — instead of a per-policy confined resident. The kernel
1079
- * enforcement is the runtime's mount profile, so commands and writes see
1080
- * the same `/tmp` and workspace; the scope stops when the session's
1081
- * providers release it. Requires systemd + `Linger`, like `wsl-shell`'s
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 key = SessionRuntimes.key({ distro, workspaceRoot, mode: policy.mode, maskWindowsDrive: config.maskWindowsDrive });
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 one syscall before the
1755
- * rename and the confined resident enforces it in the kernel.
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.
@@ -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
  *
@@ -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": "Runs a DeepSeek Harness session against a WSL distro: shell, file reads/writes and command execution happen inside the distro, the directory picker and the GUI terminal target distro paths, and commands are confined by in-distro bubblewrap."
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": "让 DeepSeek Harness 以 WSL 子系统作为会话的执行环境:shell、文件读写与命令执行均在子系统内完成,目录选择器与 GUI 终端同样定位到子系统路径,并通过子系统内的 bubblewrap 对命令进行沙箱隔离"
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.15.0",
4
- "description": "让 DeepSeek Harness 以 WSL 子系统作为会话的执行环境:shell、文件读写与命令执行均在子系统内完成,目录选择器与 GUI 终端同样定位到子系统路径,并通过子系统内的 bubblewrap 对命令进行沙箱隔离",
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",
@@ -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 -- [distro] [--json]
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.
@@ -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 -- --fix` instead");
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 -- ${distro} --install"`);
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 silently ignored. Run \"pnpm run preset -- --check <patch>\" to get the complete block, or see docs/CONFIGURATION.md#overriding-the-rows-inside-preset-wsl");
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
  }