dsh-plugin-wsl-env 0.7.5 → 0.9.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 (92) hide show
  1. package/CHANGELOG.md +345 -8
  2. package/CONTRIBUTING.md +221 -68
  3. package/README.md +50 -35
  4. package/SECURITY.md +18 -2
  5. package/SUPPORT.md +23 -7
  6. package/cordis.patch.yml +9 -0
  7. package/docs/ARCHITECTURE.md +200 -57
  8. package/docs/CONFIGURATION.md +180 -16
  9. package/docs/LIMITATIONS.md +91 -33
  10. package/docs/PARITY.md +66 -38
  11. package/docs/PEER-PARITY.md +95 -32
  12. package/docs/README.zh.md +49 -31
  13. package/docs/UPSTREAM-FSIO-EXPORT.md +159 -50
  14. package/docs/UPSTREAM-TERMINAL-TITLE.md +49 -14
  15. package/lib/agent-confined.d.ts +42 -0
  16. package/lib/agent-confined.js +25 -41
  17. package/lib/agent-errors.d.ts +31 -0
  18. package/lib/agent-errors.js +19 -16
  19. package/lib/agent-exec.d.ts +159 -0
  20. package/lib/agent-exec.js +151 -165
  21. package/lib/agent-protocol.d.ts +192 -0
  22. package/lib/agent-protocol.js +76 -100
  23. package/lib/agent-shared.d.ts +27 -0
  24. package/lib/agent-shared.js +12 -12
  25. package/lib/agent.d.ts +353 -0
  26. package/lib/agent.js +536 -552
  27. package/lib/auto-preset.d.ts +110 -0
  28. package/lib/auto-preset.js +157 -141
  29. package/lib/bwrap.d.ts +90 -0
  30. package/lib/bwrap.js +18 -18
  31. package/lib/file-reference-wsl.d.ts +160 -0
  32. package/lib/file-reference-wsl.js +192 -173
  33. package/lib/fs-decisions.d.ts +107 -0
  34. package/lib/fs-decisions.js +64 -60
  35. package/lib/fs-routing.d.ts +182 -0
  36. package/lib/fs-routing.js +173 -116
  37. package/lib/fs-substrate.d.ts +258 -0
  38. package/lib/fs-substrate.js +248 -248
  39. package/lib/fs.d.ts +10 -0
  40. package/lib/fs.js +5 -0
  41. package/lib/fsio-agent.d.ts +307 -0
  42. package/lib/fsio-agent.js +414 -410
  43. package/lib/fsio-text.d.ts +115 -0
  44. package/lib/fsio-text.js +104 -84
  45. package/lib/git-route.d.ts +100 -0
  46. package/lib/git-route.js +46 -47
  47. package/lib/index.d.ts +1003 -0
  48. package/lib/index.js +1106 -1003
  49. package/lib/listing.d.ts +114 -0
  50. package/lib/listing.js +68 -55
  51. package/lib/paths.d.ts +182 -0
  52. package/lib/paths.js +134 -109
  53. package/lib/picker.d.ts +266 -0
  54. package/lib/picker.js +371 -351
  55. package/lib/ports.d.ts +44 -0
  56. package/lib/ports.js +39 -29
  57. package/lib/preflight.d.ts +91 -0
  58. package/lib/preflight.js +94 -0
  59. package/lib/preset-choice.d.ts +33 -0
  60. package/lib/preset-choice.js +15 -7
  61. package/lib/probe-cache.d.ts +51 -0
  62. package/lib/probe-cache.js +43 -29
  63. package/lib/sandbox-core.d.ts +149 -0
  64. package/lib/sandbox-core.js +239 -87
  65. package/lib/sandbox.d.ts +48 -0
  66. package/lib/sandbox.js +4 -4
  67. package/lib/search-exec.d.ts +96 -0
  68. package/lib/search-exec.js +52 -79
  69. package/lib/search-route.d.ts +75 -0
  70. package/lib/search-route.js +30 -31
  71. package/lib/shell-env.d.ts +88 -0
  72. package/lib/shell-env.js +78 -74
  73. package/lib/shell.d.ts +10 -0
  74. package/lib/shell.js +5 -0
  75. package/lib/subprocess.d.ts +313 -0
  76. package/lib/subprocess.js +267 -262
  77. package/lib/terminal-activity.d.ts +98 -0
  78. package/lib/terminal-activity.js +29 -39
  79. package/lib/terminal-route.d.ts +27 -0
  80. package/lib/terminal-route.js +16 -8
  81. package/lib/watcher.d.ts +77 -0
  82. package/lib/watcher.js +120 -126
  83. package/lib/workspace-files-route.d.ts +132 -0
  84. package/lib/workspace-files-route.js +73 -81
  85. package/lib/workspace-files-wsl.d.ts +257 -0
  86. package/lib/workspace-files-wsl.js +311 -251
  87. package/lib/wsl.d.ts +266 -0
  88. package/lib/wsl.js +150 -155
  89. package/locale/preflight/en.json +6 -0
  90. package/locale/preflight/zh.json +6 -0
  91. package/package.json +78 -22
  92. package/scripts/diagnose.sh +92 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Pure builders and parsers for the distro-routed `workspace-files` service —
3
+ * the GUI file tree and previews for a `\\wsl.localhost\<distro>` workspace,
4
+ * served from inside the distro instead of Windows-side 9P walks.
5
+ *
6
+ * Peer-free by construction (no DSH imports): the service subclass imports
7
+ * this alongside the shipped `dsh-api-workspace-files` base class, and the
8
+ * unit tests import it standalone.
9
+ *
10
+ * This is a TypeScript source built to `lib/workspace-files-route.js`; edit
11
+ * THIS file and run `pnpm run build` — the artifact under `lib/` is generated,
12
+ * and `pnpm test` fails when it drifts.
13
+ *
14
+ * @module dsh-plugin-wsl/workspace-files-route
15
+ */
16
+ /**
17
+ * Translate one path-shaped value into distro coordinates: UNC identities map
18
+ * onto their Linux paths, drive-letter paths onto their `/mnt/<drive>` mounts,
19
+ * everything else passes through verbatim.
20
+ *
21
+ * @param value - the value to translate.
22
+ * @returns the distro-side spelling.
23
+ */
24
+ export declare function distroPath(value: string): string;
25
+ /**
26
+ * Whether a workspace root belongs to a distro (and therefore routes
27
+ * distro-side).
28
+ *
29
+ * @param workspaceRoot - the scope's workspace root, any coordinate system.
30
+ * @returns true for a `\\wsl.localhost\<distro>` UNC root.
31
+ */
32
+ export declare function isDistroWorkspace(workspaceRoot: string): boolean;
33
+ /**
34
+ * The Linux working path for one request: UNC absolutes map onto their Linux
35
+ * paths, POSIX absolutes and relative paths join onto the root.
36
+ *
37
+ * @param linuxRoot - the workspace root in Linux form.
38
+ * @param path - the requested path, any coordinate system.
39
+ * @returns the absolute Linux path.
40
+ */
41
+ export declare function linuxJoin(linuxRoot: string, path: string): string;
42
+ /**
43
+ * The record invocation for one path: line 1 is the no-follow kind (the
44
+ * upstream lstat gate), line 2 the follow-stat fields (the upstream final
45
+ * stat), tab-joined.
46
+ *
47
+ * @param file - absolute Linux path.
48
+ * @returns the agent argv.
49
+ */
50
+ export declare function statRecordArgv(file: string): string[];
51
+ /**
52
+ * Parse one stat record (kind + follow fields, tab-joined).
53
+ *
54
+ * @param line - the record line.
55
+ * @returns the upstream kind spelling, the byte size, and the synthesized
56
+ * opaque version.
57
+ */
58
+ export declare function parseStatRecord(line: string | Buffer): {
59
+ kind: string;
60
+ type: string;
61
+ size: number;
62
+ version: string;
63
+ };
64
+ /**
65
+ * Map a `stat` kind onto the wire's kind token.
66
+ *
67
+ * @param kind - the `stat -c %F` spelling.
68
+ * @returns the wire kind token.
69
+ */
70
+ export declare function wireKind(kind: string): string;
71
+ /**
72
+ * The read invocation for one line-paged text read: the stat record rides
73
+ * stderr (the caller builds the stat half of the response), the page rides
74
+ * stdout — one line beyond the page, so EOF is decidable from the yield.
75
+ * `$1` is the file, `$2` the 1-based first line, `$3` the line count.
76
+ *
77
+ * @param file - absolute Linux path.
78
+ * @param offset - first line to return (1-based).
79
+ * @param limit - maximum lines to return.
80
+ * @returns the agent argv.
81
+ */
82
+ export declare function pageArgv(file: string, offset: number, limit: number): string[];
83
+ /**
84
+ * The byte-window invocation: the stat record rides stderr, the window rides
85
+ * stdout. `$1` is the file, `$2` the byte offset, `$3` the byte count.
86
+ *
87
+ * @param file - absolute Linux path.
88
+ * @param offset - byte offset (0-based).
89
+ * @param length - byte count.
90
+ * @returns the agent argv.
91
+ */
92
+ export declare function bytesArgv(file: string, offset: number, length: number): string[];
93
+ /**
94
+ * Parse the stat record a read or byte-window relayed on stderr.
95
+ *
96
+ * @param stderr - the relay's raw stderr.
97
+ * @returns the parsed stat record, or undefined when stderr carries none.
98
+ */
99
+ export declare function parseStatRecordFromStderr(stderr: string | Buffer): {
100
+ kind: string;
101
+ type: string;
102
+ size: number;
103
+ version: string;
104
+ } | undefined;
105
+ /**
106
+ * The listing invocation for one directory: resolve the final component
107
+ * through symlinks, refuse paths outside the root and non-directories, then
108
+ * list one more entry than the cap so truncation is decidable from the yield.
109
+ * `-L` matches the upstream child semantics (a symlink to a directory lists
110
+ * as a directory, a broken link drops out); `-p` marks directories with the
111
+ * trailing `/` the parser keys on.
112
+ *
113
+ * @param linuxRoot - the workspace root in Linux form (containment boundary).
114
+ * @param linuxDir - the directory to list.
115
+ * @param maxEntries - the caller's entry cap.
116
+ * @returns the agent argv.
117
+ */
118
+ export declare function listDistroArgv(linuxRoot: string, linuxDir: string, maxEntries: number): string[];
119
+ /**
120
+ * Parse one capped `ls -1ALp` listing into wire entries.
121
+ *
122
+ * @param stdout - the listing's raw stdout.
123
+ * @param maxEntries - the entry cap the caller configured.
124
+ * @returns at most `maxEntries` entries and whether the level had more.
125
+ */
126
+ export declare function parseDirListing(stdout: string | Buffer, maxEntries: number): {
127
+ entries: Array<{
128
+ name: string;
129
+ type: "file" | "directory";
130
+ }>;
131
+ truncated: boolean;
132
+ };
@@ -7,11 +7,13 @@
7
7
  * this alongside the shipped `dsh-api-workspace-files` base class, and the
8
8
  * unit tests import it standalone.
9
9
  *
10
+ * This is a TypeScript source built to `lib/workspace-files-route.js`; edit
11
+ * THIS file and run `pnpm run build` — the artifact under `lib/` is generated,
12
+ * and `pnpm test` fails when it drifts.
13
+ *
10
14
  * @module dsh-plugin-wsl/workspace-files-route
11
15
  */
12
-
13
16
  import { isWslUnc, uncToPosix, windowsToLinuxMount } from "./paths.js";
14
-
15
17
  /**
16
18
  * Translate one path-shaped value into distro coordinates: UNC identities map
17
19
  * onto their Linux paths, drive-letter paths onto their `/mnt/<drive>` mounts,
@@ -21,15 +23,15 @@ import { isWslUnc, uncToPosix, windowsToLinuxMount } from "./paths.js";
21
23
  * @returns the distro-side spelling.
22
24
  */
23
25
  export function distroPath(value) {
24
- if (typeof value !== "string") return value;
25
- if (isWslUnc(value)) {
26
- const parsed = uncToPosix(value);
27
- return parsed === undefined ? value : parsed.linuxPath.length > 0 ? parsed.linuxPath : "/";
28
- }
29
- const mounted = windowsToLinuxMount(value);
30
- return mounted === undefined ? value : mounted;
26
+ if (typeof value !== "string")
27
+ return value;
28
+ if (isWslUnc(value)) {
29
+ const parsed = uncToPosix(value);
30
+ return parsed === undefined ? value : parsed.linuxPath.length > 0 ? parsed.linuxPath : "/";
31
+ }
32
+ const mounted = windowsToLinuxMount(value);
33
+ return mounted === undefined ? value : mounted;
31
34
  }
32
-
33
35
  /**
34
36
  * Whether a workspace root belongs to a distro (and therefore routes
35
37
  * distro-side).
@@ -38,9 +40,8 @@ export function distroPath(value) {
38
40
  * @returns true for a `\\wsl.localhost\<distro>` UNC root.
39
41
  */
40
42
  export function isDistroWorkspace(workspaceRoot) {
41
- return isWslUnc(workspaceRoot);
43
+ return isWslUnc(workspaceRoot);
42
44
  }
43
-
44
45
  /**
45
46
  * The Linux working path for one request: UNC absolutes map onto their Linux
46
47
  * paths, POSIX absolutes and relative paths join onto the root.
@@ -50,21 +51,19 @@ export function isDistroWorkspace(workspaceRoot) {
50
51
  * @returns the absolute Linux path.
51
52
  */
52
53
  export function linuxJoin(linuxRoot, path) {
53
- // The UNC check runs on the RAW spelling: the backslash-to-slash rewrite
54
- // below would leave `//wsl.localhost/...`, which the identity regex cannot
55
- // see.
56
- const parsed = uncToPosix(path);
57
- if (parsed !== undefined) return parsed.linuxPath.length > 0 ? parsed.linuxPath : "/";
58
- const text = String(path).replaceAll("\\", "/");
59
- if (text.startsWith("/")) return text.length > 1 ? text.replace(/\/+$/, "") : "/";
60
- const base = linuxRoot === "/" ? "" : linuxRoot;
61
- const joined = text.length === 0 ? base : `${base}/${text}`.replace(/\/\.(?=\/|$)/gu, "");
62
- return joined.length === 0 ? "/" : joined;
54
+ // The UNC check runs on the RAW spelling: the backslash-to-slash rewrite
55
+ // below would leave `//wsl.localhost/...`, which the identity regex cannot
56
+ // see.
57
+ const parsed = uncToPosix(path);
58
+ if (parsed !== undefined)
59
+ return parsed.linuxPath.length > 0 ? parsed.linuxPath : "/";
60
+ const text = String(path).replaceAll("\\", "/");
61
+ if (text.startsWith("/"))
62
+ return text.length > 1 ? text.replace(/\/+$/, "") : "/";
63
+ const base = linuxRoot === "/" ? "" : linuxRoot;
64
+ const joined = text.length === 0 ? base : `${base}/${text}`.replace(/\/\.(?=\/|$)/gu, "");
65
+ return joined.length === 0 ? "/" : joined;
63
66
  }
64
-
65
- /** The `stat` format string: kind, size, device, inode, mtimes — tab-joined. */
66
- const STAT_FORMAT = "%F\t%s\t%D\t%i\t%Y\t%Z";
67
-
68
67
  /**
69
68
  * The record invocation for one path: line 1 is the no-follow kind (the
70
69
  * upstream lstat gate), line 2 the follow-stat fields (the upstream final
@@ -74,14 +73,13 @@ const STAT_FORMAT = "%F\t%s\t%D\t%i\t%Y\t%Z";
74
73
  * @returns the agent argv.
75
74
  */
76
75
  export function statRecordArgv(file) {
77
- const script = [
78
- 'k=$(stat -c %F -- "$1") || exit 2',
79
- 's=$(stat -L -c %s\\t%D\\t%i\\t%Y\\t%Z -- "$1") || exit 2',
80
- "printf '%s\\t%s\\n' \"$k\" \"$s\"",
81
- ].join("\n");
82
- return ["sh", "-c", script, "sh", file];
76
+ const script = [
77
+ 'k=$(stat -c %F -- "$1") || exit 2',
78
+ 's=$(stat -L -c %s\\t%D\\t%i\\t%Y\\t%Z -- "$1") || exit 2',
79
+ "printf '%s\\t%s\\n' \"$k\" \"$s\"",
80
+ ].join("\n");
81
+ return ["sh", "-c", script, "sh", file];
83
82
  }
84
-
85
83
  /**
86
84
  * Parse one stat record (kind + follow fields, tab-joined).
87
85
  *
@@ -90,15 +88,14 @@ export function statRecordArgv(file) {
90
88
  * opaque version.
91
89
  */
92
90
  export function parseStatRecord(line) {
93
- const [kind, size, dev, ino, mtime, ctime] = String(line).trim().split("\t");
94
- return {
95
- kind,
96
- type: wireKind(kind),
97
- size: Number(size),
98
- version: `${dev}:${ino}:${size}:${mtime}:${ctime}`,
99
- };
91
+ const [kind, size, dev, ino, mtime, ctime] = String(line).trim().split("\t");
92
+ return {
93
+ kind,
94
+ type: wireKind(kind),
95
+ size: Number(size),
96
+ version: `${dev}:${ino}:${size}:${mtime}:${ctime}`,
97
+ };
100
98
  }
101
-
102
99
  /**
103
100
  * Map a `stat` kind onto the wire's kind token.
104
101
  *
@@ -106,10 +103,9 @@ export function parseStatRecord(line) {
106
103
  * @returns the wire kind token.
107
104
  */
108
105
  export function wireKind(kind) {
109
- const mapped = { "regular file": "file", directory: "directory", "symbolic link": "symlink" };
110
- return mapped[kind] ?? kind;
106
+ const mapped = { "regular file": "file", directory: "directory", "symbolic link": "symlink" };
107
+ return mapped[kind] ?? kind;
111
108
  }
112
-
113
109
  /**
114
110
  * The read invocation for one line-paged text read: the stat record rides
115
111
  * stderr (the caller builds the stat half of the response), the page rides
@@ -122,15 +118,14 @@ export function wireKind(kind) {
122
118
  * @returns the agent argv.
123
119
  */
124
120
  export function pageArgv(file, offset, limit) {
125
- const script = [
126
- 'k=$(stat -c %F -- "$1") || exit 2',
127
- 's=$(stat -L -c %s\\t%D\\t%i\\t%Y\\t%Z -- "$1") || exit 2',
128
- "printf '%s\\t%s\\n' \"$k\" \"$s\" >&2",
129
- 'sed -n "${2},$(( $2 + $3 ))p" -- "$1"',
130
- ].join("\n");
131
- return ["sh", "-c", script, "sh", file, String(offset), String(limit)];
121
+ const script = [
122
+ 'k=$(stat -c %F -- "$1") || exit 2',
123
+ 's=$(stat -L -c %s\\t%D\\t%i\\t%Y\\t%Z -- "$1") || exit 2',
124
+ "printf '%s\\t%s\\n' \"$k\" \"$s\" >&2",
125
+ 'sed -n "${2},$(( $2 + $3 ))p" -- "$1"',
126
+ ].join("\n");
127
+ return ["sh", "-c", script, "sh", file, String(offset), String(limit)];
132
128
  }
133
-
134
129
  /**
135
130
  * The byte-window invocation: the stat record rides stderr, the window rides
136
131
  * stdout. `$1` is the file, `$2` the byte offset, `$3` the byte count.
@@ -141,14 +136,13 @@ export function pageArgv(file, offset, limit) {
141
136
  * @returns the agent argv.
142
137
  */
143
138
  export function bytesArgv(file, offset, length) {
144
- const script = [
145
- 's=$(stat -L -c %s\\t%D\\t%i\\t%Y\\t%Z -- "$1") || exit 2',
146
- "printf '%s\\n' \"$s\" >&2",
147
- 'tail -c "+$(( $2 + 1 ))" -- "$1" | head -c "$3"',
148
- ].join("\n");
149
- return ["sh", "-c", script, "sh", file, String(offset), String(length)];
139
+ const script = [
140
+ 's=$(stat -L -c %s\\t%D\\t%i\\t%Y\\t%Z -- "$1") || exit 2',
141
+ "printf '%s\\n' \"$s\" >&2",
142
+ 'tail -c "+$(( $2 + 1 ))" -- "$1" | head -c "$3"',
143
+ ].join("\n");
144
+ return ["sh", "-c", script, "sh", file, String(offset), String(length)];
150
145
  }
151
-
152
146
  /**
153
147
  * Parse the stat record a read or byte-window relayed on stderr.
154
148
  *
@@ -156,12 +150,11 @@ export function bytesArgv(file, offset, length) {
156
150
  * @returns the parsed stat record, or undefined when stderr carries none.
157
151
  */
158
152
  export function parseStatRecordFromStderr(stderr) {
159
- const line = String(stderr)
160
- .split("\n")
161
- .find((candidate) => candidate.includes("\t"));
162
- return line === undefined ? undefined : parseStatRecord(line);
153
+ const line = String(stderr)
154
+ .split("\n")
155
+ .find((candidate) => candidate.includes("\t"));
156
+ return line === undefined ? undefined : parseStatRecord(line);
163
157
  }
164
-
165
158
  /**
166
159
  * The listing invocation for one directory: resolve the final component
167
160
  * through symlinks, refuse paths outside the root and non-directories, then
@@ -176,16 +169,15 @@ export function parseStatRecordFromStderr(stderr) {
176
169
  * @returns the agent argv.
177
170
  */
178
171
  export function listDistroArgv(linuxRoot, linuxDir, maxEntries) {
179
- const script = [
180
- "root=$1 dir=$2 max=$3",
181
- 'real=$(realpath -L -- "$dir") || exit 2',
182
- 'case "$real" in "$root") ;; "$root"/*) ;; *) echo "OUTSIDE:$real" >&2; exit 3;; esac',
183
- '[ -d "$real" ] || { echo "NOTDIR:$real" >&2; exit 4; }',
184
- 'ls -1ALp -- "$real" | head -n "$max"',
185
- ].join("\n");
186
- return ["sh", "-c", script, "sh", linuxRoot, linuxDir, maxEntries + 1];
172
+ const script = [
173
+ "root=$1 dir=$2 max=$3",
174
+ 'real=$(realpath -L -- "$dir") || exit 2',
175
+ 'case "$real" in "$root") ;; "$root"/*) ;; *) echo "OUTSIDE:$real" >&2; exit 3;; esac',
176
+ '[ -d "$real" ] || { echo "NOTDIR:$real" >&2; exit 4; }',
177
+ 'ls -1ALp -- "$real" | head -n "$max"',
178
+ ].join("\n");
179
+ return ["sh", "-c", script, "sh", linuxRoot, linuxDir, String(maxEntries + 1)];
187
180
  }
188
-
189
181
  /**
190
182
  * Parse one capped `ls -1ALp` listing into wire entries.
191
183
  *
@@ -194,12 +186,12 @@ export function listDistroArgv(linuxRoot, linuxDir, maxEntries) {
194
186
  * @returns at most `maxEntries` entries and whether the level had more.
195
187
  */
196
188
  export function parseDirListing(stdout, maxEntries) {
197
- const lines = String(stdout).split("\n").filter((line) => line.length > 0);
198
- const truncated = lines.length > maxEntries;
199
- const entries = lines.slice(0, maxEntries).map((line) => {
200
- const directory = line.endsWith("/");
201
- const name = directory ? line.slice(0, -1) : line;
202
- return { name, type: directory ? "directory" : "file" };
203
- });
204
- return { entries, truncated };
189
+ const lines = String(stdout).split("\n").filter((line) => line.length > 0);
190
+ const truncated = lines.length > maxEntries;
191
+ const entries = lines.slice(0, maxEntries).map((line) => {
192
+ const directory = line.endsWith("/");
193
+ const name = directory ? line.slice(0, -1) : line;
194
+ return { name, type: directory ? "directory" : "file" };
195
+ });
196
+ return { entries, truncated };
205
197
  }
@@ -0,0 +1,257 @@
1
+ /**
2
+ * The distro-routed `workspace-files` service: the GUI file tree and previews
3
+ * for a `\\wsl.localhost\<distro>` workspace are served from inside the distro
4
+ * through the resident agent, instead of Windows-side 9P walks.
5
+ *
6
+ * ## Route predicate
7
+ *
8
+ * Every override checks `scope.workspaceRoot` — a distro UNC routes
9
+ * distro-side, a drive path calls `super` (host native, unchanged).
10
+ * `changes` (the watch stream) always delegates to `super`: its feed rides
11
+ * `ctx.fs.watch`, whose behaviour is today's on both workspace kinds.
12
+ *
13
+ * ## What the distro side reproduces
14
+ *
15
+ * The wire shapes of `dsh-api-workspace-files`: listings capped with a
16
+ * `truncated` flag, stats of `{absolutePath, version, bytes?}`, line-paged
17
+ * text with NUL and UTF-8 refusal and the byte-cap error, and byte windows
18
+ * with EOF markers. Versions are synthesized from `stat` fields — opaque to
19
+ * the client, which only compares them. `absolutePath` is synthesized in the
20
+ * UNC form so the GUI's path display stays what it was on the host fs. Read
21
+ * refuses a final-component symlink exactly like the upstream `locateFile`
22
+ * lstat gate does; `list` follows one (matching its own symlink-through
23
+ * rule).
24
+ *
25
+ * The distro operations are standalone functions over an injected runner, so
26
+ * they are unit-testable with a fake agent; the class methods are thin
27
+ * coordinate guards around them.
28
+ *
29
+ * This is a TypeScript source built to `lib/workspace-files-wsl.js`; edit THIS
30
+ * file and run `pnpm run build` — the artifact under `lib/` is generated, and
31
+ * `pnpm test` fails when it drifts.
32
+ *
33
+ * @module dsh-plugin-wsl/workspace-files-wsl
34
+ */
35
+ import { WorkspaceFiles } from "@deepseek-ai/dsh-api-workspace-files";
36
+ import type { WorkspaceByteReadOptions, WorkspaceDirectoryListing, WorkspaceFileBytes, WorkspaceFileRange, WorkspaceFileScope, WorkspaceFileStat, WorkspaceFileText } from "@deepseek-ai/dsh-api-workspace-files";
37
+ import type { WslAgent } from "./agent.js";
38
+ /**
39
+ * This variant row's own validated config. The base class keeps `config`
40
+ * private and its public seam exposes no configuration channel, while the
41
+ * distro branch enforces the caps the row carries — so the read goes through
42
+ * this one documented cast. The keys mirror this row's `config` in
43
+ * `cordis.patch.yml`.
44
+ */
45
+ export interface WorkspaceFilesWslConfig {
46
+ /** The directory listing's entry cap. */
47
+ maxEntries: number;
48
+ /** The text read's line cap. */
49
+ maxLines: number;
50
+ /** The text read's byte cap. */
51
+ maxBytes: number;
52
+ /** The byte read's full-file cap. */
53
+ maxFileBytes: number;
54
+ }
55
+ /**
56
+ * One parsed distro directory listing. The route parser's own inference
57
+ * widens the entry kind to `string`; the relay only ever emits the two wire
58
+ * tokens below, and this is what the cast at the parse boundary restores.
59
+ */
60
+ export interface DistroListing {
61
+ /** The level's child entries. */
62
+ entries: Array<{
63
+ name: string;
64
+ type: "file" | "directory";
65
+ size?: number;
66
+ }>;
67
+ /** Whether the level had more entries than the cap. */
68
+ truncated: boolean;
69
+ }
70
+ /**
71
+ * List one distro directory through the runner.
72
+ *
73
+ * The agent argv confines the directory under the workspace root, refuses
74
+ * non-directories, and emits one line per child — a trailing `/` marks a
75
+ * directory (symlinks resolve through, broken links drop out, matching the
76
+ * upstream child semantics).
77
+ *
78
+ * @param object - the listing's inputs.
79
+ * @returns the listing, with `path` relative to the workspace root (empty for it).
80
+ */
81
+ export declare function distroList({ runner, distro, linuxRoot, path, maxEntries, signal }: {
82
+ /** The agent-like runner (`exec`). */
83
+ runner: WslAgent;
84
+ /** The distro name. */
85
+ distro: string;
86
+ /** The workspace root in Linux form. */
87
+ linuxRoot: string;
88
+ /** The requested directory, any coordinate system. */
89
+ path: string;
90
+ /** The entry cap. */
91
+ maxEntries: number;
92
+ /** Caller cancellation. */
93
+ signal?: AbortSignal;
94
+ }): Promise<{
95
+ path: string;
96
+ entries: Array<{
97
+ name: string;
98
+ type: "file" | "directory";
99
+ size?: number;
100
+ }>;
101
+ truncated: boolean;
102
+ }>;
103
+ /**
104
+ * Stat one distro path through the runner.
105
+ *
106
+ * @param object - the stat's inputs.
107
+ * @returns the wire stat row.
108
+ */
109
+ export declare function distroStat({ runner, distro, linuxRoot, path, distroWorkspaceRoot, signal }: {
110
+ /** The agent-like runner (`exec`). */
111
+ runner: WslAgent;
112
+ /** The distro name. */
113
+ distro: string;
114
+ /** The workspace root in Linux form. */
115
+ linuxRoot: string;
116
+ /** The requested path, any coordinate system. */
117
+ path: string;
118
+ /** The UNC workspace root, for the absolutePath display form. */
119
+ distroWorkspaceRoot: string;
120
+ /** Caller cancellation. */
121
+ signal?: AbortSignal;
122
+ }): Promise<{
123
+ absolutePath: string;
124
+ version: string;
125
+ bytes?: number;
126
+ }>;
127
+ /**
128
+ * Read one line-paged UTF-8 window through the runner, mirroring the
129
+ * upstream `cutPage` semantics: a NUL byte marks the page binary, a page
130
+ * above the byte cap is refused (never shortened), and `eof` is false exactly
131
+ * when a character exists past the page.
132
+ *
133
+ * @param object - the read's inputs.
134
+ * @returns the wire text page.
135
+ */
136
+ export declare function distroRead({ runner, distro, linuxRoot, path, offset, limit, maxBytes, distroWorkspaceRoot, signal }: {
137
+ /** The agent-like runner (`exec`). */
138
+ runner: WslAgent;
139
+ /** The distro name. */
140
+ distro: string;
141
+ /** The workspace root in Linux form. */
142
+ linuxRoot: string;
143
+ /** The requested path, any coordinate system. */
144
+ path: string;
145
+ /** First line to return (1-based). */
146
+ offset: number;
147
+ /** Maximum lines to return. */
148
+ limit: number;
149
+ /** The page's byte cap. */
150
+ maxBytes: number;
151
+ /** The UNC workspace root, for the absolutePath display form. */
152
+ distroWorkspaceRoot: string;
153
+ /** Caller cancellation. */
154
+ signal?: AbortSignal;
155
+ }): Promise<{
156
+ absolutePath: string;
157
+ version: string;
158
+ bytes?: number;
159
+ offset: number;
160
+ text: string;
161
+ lines: number;
162
+ eof: boolean;
163
+ }>;
164
+ /**
165
+ * Read one byte window (or the capped whole file) through the runner.
166
+ *
167
+ * @param object - the read's inputs.
168
+ * @returns the wire byte read.
169
+ */
170
+ export declare function distroReadBytes({ runner, distro, linuxRoot, path, offset, length, maxBytes, maxFileBytes, distroWorkspaceRoot, signal }: {
171
+ /** The agent-like runner (`exec`). */
172
+ runner: WslAgent;
173
+ /** The distro name. */
174
+ distro: string;
175
+ /** The workspace root in Linux form. */
176
+ linuxRoot: string;
177
+ /** The requested path, any coordinate system. */
178
+ path: string;
179
+ /** Byte offset (0-based). */
180
+ offset: number | undefined;
181
+ /** Byte count. */
182
+ length: number | undefined;
183
+ /** The window's inclusive byte cap; undefined leaves the window uncapped. */
184
+ maxBytes?: number | undefined;
185
+ /** The complete-file cap. */
186
+ maxFileBytes: number;
187
+ /** The UNC workspace root, for the absolutePath display form. */
188
+ distroWorkspaceRoot: string;
189
+ /** Caller cancellation. */
190
+ signal?: AbortSignal;
191
+ }): Promise<{
192
+ absolutePath: string;
193
+ version: string;
194
+ bytes?: number;
195
+ offset: number;
196
+ data: Uint8Array;
197
+ eof: boolean;
198
+ }>;
199
+ /**
200
+ * The distro-routed `workspace-files` service.
201
+ */
202
+ export declare class WorkspaceFilesWsl extends WorkspaceFiles {
203
+ static Config: import("@deepseek-ai/schemastery").default<import("@deepseek-ai/dsh-api-workspace-files").Config>;
204
+ /**
205
+ * The agent runner for one distro; a hook tests substitute.
206
+ *
207
+ * @param distro - the distro name.
208
+ * @returns the agent runner.
209
+ */
210
+ runnerFor(distro: string): WslAgent;
211
+ /**
212
+ * The distro coordinates of one workspace scope.
213
+ *
214
+ * @param workspaceRoot - the scope's workspace root.
215
+ * @returns the distro and its workspace root in Linux form.
216
+ */
217
+ coords(workspaceRoot: string): {
218
+ distro: string;
219
+ linuxRoot: string;
220
+ };
221
+ /**
222
+ * List one workspace directory; distro UNC roots list inside the distro.
223
+ *
224
+ * @param workspaceFileScope - the Session's file scope.
225
+ * @param path - the directory to list.
226
+ * @param signal - caller cancellation.
227
+ */
228
+ list(workspaceFileScope: WorkspaceFileScope, path: string, signal: AbortSignal): Promise<WorkspaceDirectoryListing>;
229
+ /**
230
+ * Stat one workspace file; distro UNC roots stat inside the distro.
231
+ *
232
+ * @param workspaceFileScope - the Session's file scope.
233
+ * @param path - the file to stat.
234
+ * @param signal - caller cancellation.
235
+ */
236
+ stat(workspaceFileScope: WorkspaceFileScope, path: string, signal: AbortSignal): Promise<WorkspaceFileStat>;
237
+ /**
238
+ * Read one line page; distro UNC roots read inside the distro.
239
+ *
240
+ * @param workspaceFileScope - the Session's file scope.
241
+ * @param path - the file to read.
242
+ * @param range - the line window.
243
+ * @param signal - caller cancellation.
244
+ */
245
+ read(workspaceFileScope: WorkspaceFileScope, path: string, range: WorkspaceFileRange, signal: AbortSignal): Promise<WorkspaceFileText>;
246
+ /**
247
+ * Read one byte window or the complete file; distro UNC roots read inside
248
+ * the distro.
249
+ *
250
+ * @param workspaceFileScope - the Session's file scope.
251
+ * @param path - the file to read.
252
+ * @param options - the byte window.
253
+ * @param signal - caller cancellation.
254
+ */
255
+ readBytes(workspaceFileScope: WorkspaceFileScope, path: string, options: WorkspaceByteReadOptions, signal: AbortSignal): Promise<WorkspaceFileBytes>;
256
+ }
257
+ export default WorkspaceFilesWsl;