dsh-plugin-wsl-env 0.7.5 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +278 -0
- package/CONTRIBUTING.md +129 -39
- package/README.md +20 -20
- package/docs/ARCHITECTURE.md +6 -3
- package/docs/LIMITATIONS.md +13 -0
- package/docs/README.zh.md +20 -20
- package/lib/agent-confined.d.ts +42 -0
- package/lib/agent-confined.js +25 -41
- package/lib/agent-errors.d.ts +31 -0
- package/lib/agent-errors.js +19 -16
- package/lib/agent-exec.d.ts +159 -0
- package/lib/agent-exec.js +151 -165
- package/lib/agent-protocol.d.ts +192 -0
- package/lib/agent-protocol.js +76 -100
- package/lib/agent-shared.d.ts +27 -0
- package/lib/agent-shared.js +12 -12
- package/lib/agent.d.ts +353 -0
- package/lib/agent.js +536 -552
- package/lib/auto-preset.d.ts +110 -0
- package/lib/auto-preset.js +157 -141
- package/lib/bwrap.d.ts +90 -0
- package/lib/bwrap.js +18 -18
- package/lib/file-reference-wsl.d.ts +160 -0
- package/lib/file-reference-wsl.js +192 -173
- package/lib/fs-decisions.d.ts +107 -0
- package/lib/fs-decisions.js +64 -60
- package/lib/fs-routing.d.ts +182 -0
- package/lib/fs-routing.js +173 -116
- package/lib/fs-substrate.d.ts +258 -0
- package/lib/fs-substrate.js +248 -248
- package/lib/fs.d.ts +10 -0
- package/lib/fs.js +5 -0
- package/lib/fsio-agent.d.ts +307 -0
- package/lib/fsio-agent.js +414 -410
- package/lib/fsio-text.d.ts +115 -0
- package/lib/fsio-text.js +104 -84
- package/lib/git-route.d.ts +100 -0
- package/lib/git-route.js +46 -47
- package/lib/index.d.ts +1003 -0
- package/lib/index.js +1101 -1003
- package/lib/listing.d.ts +114 -0
- package/lib/listing.js +68 -55
- package/lib/paths.d.ts +182 -0
- package/lib/paths.js +134 -109
- package/lib/picker.d.ts +266 -0
- package/lib/picker.js +371 -351
- package/lib/ports.d.ts +44 -0
- package/lib/ports.js +39 -29
- package/lib/preset-choice.d.ts +33 -0
- package/lib/preset-choice.js +15 -7
- package/lib/probe-cache.d.ts +40 -0
- package/lib/probe-cache.js +35 -29
- package/lib/sandbox-core.d.ts +79 -0
- package/lib/sandbox-core.js +71 -88
- package/lib/sandbox.d.ts +48 -0
- package/lib/sandbox.js +4 -4
- package/lib/search-exec.d.ts +96 -0
- package/lib/search-exec.js +52 -79
- package/lib/search-route.d.ts +75 -0
- package/lib/search-route.js +30 -31
- package/lib/shell-env.d.ts +88 -0
- package/lib/shell-env.js +78 -74
- package/lib/shell.d.ts +10 -0
- package/lib/shell.js +5 -0
- package/lib/subprocess.d.ts +313 -0
- package/lib/subprocess.js +267 -262
- package/lib/terminal-activity.d.ts +98 -0
- package/lib/terminal-activity.js +29 -39
- package/lib/terminal-route.d.ts +27 -0
- package/lib/terminal-route.js +16 -8
- package/lib/watcher.d.ts +77 -0
- package/lib/watcher.js +120 -126
- package/lib/workspace-files-route.d.ts +132 -0
- package/lib/workspace-files-route.js +73 -81
- package/lib/workspace-files-wsl.d.ts +257 -0
- package/lib/workspace-files-wsl.js +311 -251
- package/lib/wsl.d.ts +266 -0
- package/lib/wsl.js +144 -155
- package/package.json +68 -20
|
@@ -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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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;
|