dsh-plugin-wsl-env 0.9.0 → 0.11.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 +456 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +85 -68
- package/SUPPORT.md +3 -3
- package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
- package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
- package/agent/wsl-agent.sh +83 -16
- package/cordis.patch.yml +41 -35
- package/docs/ARCHITECTURE.md +9 -1
- package/docs/CONFIGURATION.md +7 -4
- package/docs/LIMITATIONS.md +59 -18
- package/docs/PARITY.md +3 -1
- package/docs/PEER-PARITY.md +18 -11
- package/docs/README.zh.md +98 -74
- package/docs/ROADMAP.md +79 -0
- package/docs/UPSTREAM-FSIO-EXPORT.md +3 -3
- package/docs/UPSTREAM-TERMINAL-TITLE.md +9 -2
- package/examples/preset-wsl-session-sandbox.cordis.patch.yml +178 -0
- package/lib/agent-protocol.d.ts +10 -4
- package/lib/agent-protocol.js +10 -3
- package/lib/agent-shared.js +33 -5
- package/lib/agent.d.ts +187 -0
- package/lib/agent.js +311 -23
- package/lib/bwrap.d.ts +8 -0
- package/lib/bwrap.js +4 -1
- package/lib/fs-routing.d.ts +40 -4
- package/lib/fs-routing.js +80 -12
- package/lib/fsio-agent.js +1 -1
- package/lib/index.d.ts +134 -0
- package/lib/index.js +191 -3
- package/lib/native-agent.d.ts +124 -0
- package/lib/native-agent.js +198 -0
- package/lib/ports.d.ts +13 -2
- package/lib/ports.js +18 -5
- package/lib/preflight.js +24 -1
- package/lib/session-runtime.d.ts +161 -0
- package/lib/session-runtime.js +220 -0
- package/lib/session-scope.d.ts +124 -0
- package/lib/session-scope.js +191 -0
- package/lib/shell-env.js +10 -3
- package/lib/watcher.d.ts +25 -12
- package/lib/watcher.js +120 -32
- package/lib/workspace-files-route.d.ts +8 -4
- package/lib/workspace-files-route.js +42 -14
- package/lib/workspace-files-wsl.js +25 -8
- package/package.json +35 -25
package/lib/agent-protocol.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @module dsh-plugin-wsl/agent-protocol
|
|
15
15
|
*/
|
|
16
16
|
/** The protocol version this host side speaks; the agent must match. */
|
|
17
|
-
export declare const PROTOCOL_VERSION =
|
|
17
|
+
export declare const PROTOCOL_VERSION = 5;
|
|
18
18
|
/**
|
|
19
19
|
* Upper bound on one `read` op's payload, set by the HOST looping over windowed
|
|
20
20
|
* requests. Keeping each frame's decoded payload here means the agent's RES
|
|
@@ -127,15 +127,21 @@ export interface FsFrameRequest {
|
|
|
127
127
|
*/
|
|
128
128
|
export declare function encodeFsFrame({ id, op, args, timeoutMs }: FsFrameRequest): string[];
|
|
129
129
|
/**
|
|
130
|
-
* `HELLO|name|version|digest` — `digest` is the sha256 of the script file
|
|
131
|
-
* agent is executing (empty when it could not hash it); the host compares
|
|
132
|
-
* against its own shipped copy.
|
|
130
|
+
* `HELLO|name|version|digest|pid` — `digest` is the sha256 of the script file
|
|
131
|
+
* the agent is executing (empty when it could not hash it); the host compares
|
|
132
|
+
* it against its own shipped copy. `pid` is the agent shell's PID inside the
|
|
133
|
+
* distro, present from protocol 5 on: the host uses it to deliver an
|
|
134
|
+
* out-of-band `USR1` cancellation while the agent is blocked in `wait`, which
|
|
135
|
+
* the serial stdin loop cannot do by frame. It is absent when the agent
|
|
136
|
+
* itself runs inside a PID namespace (the confined resident), where that PID
|
|
137
|
+
* is not addressable from a second `wsl.exe`.
|
|
133
138
|
*/
|
|
134
139
|
export interface HelloMessage {
|
|
135
140
|
type: "hello";
|
|
136
141
|
name: string;
|
|
137
142
|
version: number;
|
|
138
143
|
digest: string;
|
|
144
|
+
pid?: number;
|
|
139
145
|
}
|
|
140
146
|
/** `PONG` — the liveness answer to `PING`. */
|
|
141
147
|
export interface PongMessage {
|
package/lib/agent-protocol.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @module dsh-plugin-wsl/agent-protocol
|
|
15
15
|
*/
|
|
16
16
|
/** The protocol version this host side speaks; the agent must match. */
|
|
17
|
-
export const PROTOCOL_VERSION =
|
|
17
|
+
export const PROTOCOL_VERSION = 5;
|
|
18
18
|
/**
|
|
19
19
|
* Upper bound on one `read` op's payload, set by the HOST looping over windowed
|
|
20
20
|
* requests. Keeping each frame's decoded payload here means the agent's RES
|
|
@@ -124,8 +124,15 @@ export function encodeFsFrame({ id, op, args, timeoutMs = 0 }) {
|
|
|
124
124
|
*/
|
|
125
125
|
export function parseAgentLine(line) {
|
|
126
126
|
if (line.startsWith("HELLO|")) {
|
|
127
|
-
const [, name, version, digest] = line.split("|");
|
|
128
|
-
|
|
127
|
+
const [, name, version, digest, pid] = line.split("|");
|
|
128
|
+
const agentPid = Number(pid);
|
|
129
|
+
return {
|
|
130
|
+
type: "hello",
|
|
131
|
+
name: name ?? "",
|
|
132
|
+
version: Number(version),
|
|
133
|
+
digest: digest ?? "",
|
|
134
|
+
...(Number.isSafeInteger(agentPid) && agentPid > 0 ? { pid: agentPid } : {}),
|
|
135
|
+
};
|
|
129
136
|
}
|
|
130
137
|
if (line === "PONG")
|
|
131
138
|
return { type: "pong" };
|
package/lib/agent-shared.js
CHANGED
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
* @module dsh-plugin-wsl/agent-shared
|
|
15
15
|
*/
|
|
16
16
|
import { createHash } from "node:crypto";
|
|
17
|
+
import { spawn } from "node:child_process";
|
|
17
18
|
import { readFileSync } from "node:fs";
|
|
18
19
|
import { fileURLToPath } from "node:url";
|
|
19
20
|
import { toLinuxPath } from "./paths.js";
|
|
20
|
-
import { WslAgent } from "./agent.js";
|
|
21
|
+
import { pinnedWindowsEnv, WslAgent } from "./agent.js";
|
|
22
|
+
import { configuredAgentBackend, ensureNativeBackend, NATIVE_AGENT_ENV } from "./native-agent.js";
|
|
21
23
|
const AGENT_SCRIPT_PATH = fileURLToPath(new URL("../agent/wsl-agent.sh", import.meta.url));
|
|
22
24
|
// The script the distro reads must be byte-identical to the copy this
|
|
23
25
|
// package shipped: the deployed layout reads it from the /mnt/c mirror, and
|
|
@@ -25,6 +27,9 @@ const AGENT_SCRIPT_PATH = fileURLToPath(new URL("../agent/wsl-agent.sh", import.
|
|
|
25
27
|
// Both resident factories (plain and confined) pin it.
|
|
26
28
|
const AGENT_SCRIPT_DIGEST = createHash("sha256").update(readFileSync(AGENT_SCRIPT_PATH)).digest("hex");
|
|
27
29
|
const sharedAgents = new Map();
|
|
30
|
+
// Distros where the native shared agent failed at runtime; the reads fail open
|
|
31
|
+
// to the shell instead of retrying the same broken binary on every call.
|
|
32
|
+
const nativeFailed = new Set();
|
|
28
33
|
/** The agent script as a host-side path, for factories that need both spellings. */
|
|
29
34
|
export const agentScriptPath = AGENT_SCRIPT_PATH;
|
|
30
35
|
/** The sha256 of the shipped agent script; every resident verifies its HELLO against it. */
|
|
@@ -37,9 +42,32 @@ export const agentScriptDigest = AGENT_SCRIPT_DIGEST;
|
|
|
37
42
|
*/
|
|
38
43
|
export function sharedAgent(distro) {
|
|
39
44
|
let agent = sharedAgents.get(distro);
|
|
40
|
-
if (agent
|
|
41
|
-
agent
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
if (agent !== undefined && !agent.unavailable)
|
|
46
|
+
return agent;
|
|
47
|
+
if (agent !== undefined)
|
|
48
|
+
nativeFailed.add(distro);
|
|
49
|
+
agent = new WslAgent({
|
|
50
|
+
distro,
|
|
51
|
+
scriptPath: toLinuxPath(AGENT_SCRIPT_PATH),
|
|
52
|
+
expectedDigest: AGENT_SCRIPT_DIGEST,
|
|
53
|
+
prepare: async () => {
|
|
54
|
+
if (configuredAgentBackend() === "shell" || nativeFailed.has(distro))
|
|
55
|
+
return undefined;
|
|
56
|
+
const prepared = await ensureNativeBackend({ wslPath: "wsl.exe", distro, source: process.env[NATIVE_AGENT_ENV] ?? "" });
|
|
57
|
+
if (prepared === undefined)
|
|
58
|
+
return undefined;
|
|
59
|
+
return {
|
|
60
|
+
scriptPath: prepared.path,
|
|
61
|
+
expectedDigest: prepared.digest,
|
|
62
|
+
signalable: false,
|
|
63
|
+
spawnTransport: ({ wslPath, distro: target, argvPrefix }) => spawn(wslPath, ["-d", target, "--exec", ...argvPrefix, prepared.path], {
|
|
64
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
65
|
+
windowsHide: true,
|
|
66
|
+
env: pinnedWindowsEnv(process.env),
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
sharedAgents.set(distro, agent);
|
|
44
72
|
return agent;
|
|
45
73
|
}
|
package/lib/agent.d.ts
CHANGED
|
@@ -59,6 +59,14 @@ export interface ExecResult {
|
|
|
59
59
|
export interface PendingRequest {
|
|
60
60
|
/** The request id, as the agent echoes it. */
|
|
61
61
|
id: string;
|
|
62
|
+
/** Which metric bucket this request belongs to. */
|
|
63
|
+
kind: "fs" | "exec";
|
|
64
|
+
/** When the frame was written, for the queue measurement. */
|
|
65
|
+
sentAt: number;
|
|
66
|
+
/** When the agent ACKed it, for the execution measurement. */
|
|
67
|
+
ackedAt?: number;
|
|
68
|
+
/** Whether the metrics recorded this request's settle. */
|
|
69
|
+
measured: boolean;
|
|
62
70
|
/** The frame lines (kept so a rebuild can re-send). */
|
|
63
71
|
lines: string[];
|
|
64
72
|
/** Settles a RES. */
|
|
@@ -73,6 +81,57 @@ export interface PendingRequest {
|
|
|
73
81
|
onAbort: (() => void) | null;
|
|
74
82
|
/** Arms `watchdog`; null when the request carries no timeout. */
|
|
75
83
|
armWatchdog: (() => void) | null;
|
|
84
|
+
/** Whether the agent has ACKed this request (it is the running one). */
|
|
85
|
+
started: boolean;
|
|
86
|
+
/** The caller cancelled it; the entry stays until the agent answers. */
|
|
87
|
+
cancelled: boolean;
|
|
88
|
+
/** The caller was already settled; a late answer only cleans up. */
|
|
89
|
+
settled: boolean;
|
|
90
|
+
}
|
|
91
|
+
/** Per-kind request metrics. */
|
|
92
|
+
export interface RequestMetrics {
|
|
93
|
+
/** Requests that settled (successful, failed or cancelled). */
|
|
94
|
+
requests: number;
|
|
95
|
+
/** Total queue wait (sent -> ACK), ms. */
|
|
96
|
+
queueMs: number;
|
|
97
|
+
/** Largest single queue wait, ms. */
|
|
98
|
+
queueMaxMs: number;
|
|
99
|
+
/** Total execution time (ACK -> settle), ms. */
|
|
100
|
+
execMs: number;
|
|
101
|
+
/** Largest single execution time, ms. */
|
|
102
|
+
execMaxMs: number;
|
|
103
|
+
/** Settled with an error (not cancellation). */
|
|
104
|
+
failed: number;
|
|
105
|
+
/** Cancelled by the caller. */
|
|
106
|
+
cancelled: number;
|
|
107
|
+
/** Rejected before tracking: queue full, already aborted, or send failed. */
|
|
108
|
+
rejected: number;
|
|
109
|
+
}
|
|
110
|
+
/** The agent's cumulative request metrics. */
|
|
111
|
+
export interface AgentMetrics {
|
|
112
|
+
/** FS frames. */
|
|
113
|
+
fs: RequestMetrics;
|
|
114
|
+
/** EXEC frames (commands, ping). */
|
|
115
|
+
exec: RequestMetrics;
|
|
116
|
+
/** Peak number of pending requests. */
|
|
117
|
+
inFlightMax: number;
|
|
118
|
+
}
|
|
119
|
+
/** The entry an async preparation may select before the first spawn. */
|
|
120
|
+
export interface WslAgentPrepared {
|
|
121
|
+
/** The agent entrypoint (the shell script or the native cache path). */
|
|
122
|
+
scriptPath: string;
|
|
123
|
+
/** The HELLO digest to verify. */
|
|
124
|
+
expectedDigest?: string;
|
|
125
|
+
/** Whether USR1 can reach the agent (false for the native KILL-frame path). */
|
|
126
|
+
signalable?: boolean;
|
|
127
|
+
/** The process factory for this entry. */
|
|
128
|
+
spawnTransport?: (options: {
|
|
129
|
+
wslPath: string;
|
|
130
|
+
distro: string;
|
|
131
|
+
scriptPath: string;
|
|
132
|
+
argvPrefix: string[];
|
|
133
|
+
leaseMs: number;
|
|
134
|
+
}) => ChildProcess;
|
|
76
135
|
}
|
|
77
136
|
/**
|
|
78
137
|
* The Windows environment the transport starts from, pinned.
|
|
@@ -134,13 +193,36 @@ export declare class WslAgent {
|
|
|
134
193
|
distro: string;
|
|
135
194
|
scriptPath: string;
|
|
136
195
|
argvPrefix: string[];
|
|
196
|
+
leaseMs: number;
|
|
137
197
|
}) => ChildProcess) | undefined;
|
|
198
|
+
/** The explicit lease, when the caller supplied one (independent of idleMs). */
|
|
199
|
+
configuredLease: number | undefined;
|
|
200
|
+
/** The injectable signal sender, when the caller supplied one. */
|
|
201
|
+
configuredSignal: ((options: {
|
|
202
|
+
wslPath: string;
|
|
203
|
+
distro: string;
|
|
204
|
+
pid: number;
|
|
205
|
+
signal: NodeJS.Signals;
|
|
206
|
+
}) => void) | undefined;
|
|
207
|
+
/** The agent shell's PID from its HELLO, when a second wsl.exe can address it. */
|
|
208
|
+
agentPid: number | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Whether a second `wsl.exe` can deliver `USR1` to the agent's HELLO pid.
|
|
211
|
+
* True for the plain agent; false for a resident inside a PID namespace. A
|
|
212
|
+
* session runtime has a prefix (scope + mounts) but no PID namespace, so it
|
|
213
|
+
* opts in explicitly.
|
|
214
|
+
*/
|
|
215
|
+
signalable: boolean;
|
|
216
|
+
/** The maximum requests allowed in flight on this resident. */
|
|
217
|
+
maxPending: number;
|
|
138
218
|
/** The live resident process, when any. */
|
|
139
219
|
process: ChildProcess | null;
|
|
140
220
|
/** The lifecycle state. */
|
|
141
221
|
state: "idle" | "starting" | "ready" | "dead";
|
|
142
222
|
/** The in-flight start, shared by concurrent callers. */
|
|
143
223
|
startPromise: Promise<void> | null;
|
|
224
|
+
/** The in-flight start's settler, so close() can fail a start it interrupts. */
|
|
225
|
+
startSettler: ((error?: unknown) => void) | null;
|
|
144
226
|
/** The next request id. */
|
|
145
227
|
nextRequestId: number;
|
|
146
228
|
/** The requests in flight, keyed by their current id. */
|
|
@@ -151,6 +233,10 @@ export declare class WslAgent {
|
|
|
151
233
|
idleTimer: NodeJS.Timeout | null;
|
|
152
234
|
/** The last failure, for `unavailableReason` and log messages. */
|
|
153
235
|
lastError: string;
|
|
236
|
+
/** Cumulative request metrics; see {@link metrics}. */
|
|
237
|
+
requestMetrics: AgentMetrics;
|
|
238
|
+
/** The optional async preparation hook. */
|
|
239
|
+
prepare: (() => Promise<WslAgentPrepared | undefined>) | undefined;
|
|
154
240
|
/**
|
|
155
241
|
* @param config - the agent configuration.
|
|
156
242
|
*/
|
|
@@ -166,6 +252,13 @@ export declare class WslAgent {
|
|
|
166
252
|
wslPath?: string;
|
|
167
253
|
/** Idle shutdown delay; 0 disables. */
|
|
168
254
|
idleMs?: number;
|
|
255
|
+
/**
|
|
256
|
+
* The client lease in ms, independent of `idleMs`. A session runtime holds
|
|
257
|
+
* the agent for the session's lifetime (no idle close) but still wants the
|
|
258
|
+
* lease's last-resort self-termination; a positive value here overrides the
|
|
259
|
+
* idle-derived lease.
|
|
260
|
+
*/
|
|
261
|
+
leaseMs?: number;
|
|
169
262
|
/**
|
|
170
263
|
* The sha256 of the agent script as this package shipped it; a HELLO whose
|
|
171
264
|
* digest differs is refused the same way a version mismatch is (the script
|
|
@@ -180,6 +273,13 @@ export declare class WslAgent {
|
|
|
180
273
|
* Empty for the plain agent.
|
|
181
274
|
*/
|
|
182
275
|
argvPrefix?: string[];
|
|
276
|
+
/**
|
|
277
|
+
* Whether a second `wsl.exe` can signal the agent's pid. Defaults to
|
|
278
|
+
* "true when the argv prefix is empty": the plain agent shares the distro's
|
|
279
|
+
* PID namespace, a confined resident does not, and a session runtime has a
|
|
280
|
+
* prefix but no PID namespace and must opt in.
|
|
281
|
+
*/
|
|
282
|
+
signalable?: boolean;
|
|
183
283
|
/** Hard ceiling in bytes on one stdout protocol line; defaults to {@link MAX_FRAME_BYTES}. */
|
|
184
284
|
frameCapBytes?: number;
|
|
185
285
|
/** Injectable process factory for tests. */
|
|
@@ -188,7 +288,25 @@ export declare class WslAgent {
|
|
|
188
288
|
distro: string;
|
|
189
289
|
scriptPath: string;
|
|
190
290
|
argvPrefix: string[];
|
|
291
|
+
leaseMs: number;
|
|
191
292
|
}) => ChildProcess;
|
|
293
|
+
/**
|
|
294
|
+
* Injectable out-of-band signal delivery for tests. The real path spawns a
|
|
295
|
+
* second `wsl.exe` to send USR1 to the agent's PID.
|
|
296
|
+
*/
|
|
297
|
+
signalTransport?: (options: {
|
|
298
|
+
wslPath: string;
|
|
299
|
+
distro: string;
|
|
300
|
+
pid: number;
|
|
301
|
+
signal: NodeJS.Signals;
|
|
302
|
+
}) => void;
|
|
303
|
+
/**
|
|
304
|
+
* Maximum requests in flight on one resident; a submission past it is
|
|
305
|
+
* refused instead of growing the request map without bound.
|
|
306
|
+
*/
|
|
307
|
+
maxPending?: number;
|
|
308
|
+
/** Optional async preparation selecting the entry before the first spawn. */
|
|
309
|
+
prepare?: () => Promise<WslAgentPrepared | undefined>;
|
|
192
310
|
});
|
|
193
311
|
/**
|
|
194
312
|
* Whether the agent is permanently out — the executor's fallback cue.
|
|
@@ -205,6 +323,18 @@ export declare class WslAgent {
|
|
|
205
323
|
* @returns resolved once the state is `ready`.
|
|
206
324
|
*/
|
|
207
325
|
start(): Promise<void>;
|
|
326
|
+
/**
|
|
327
|
+
* Apply an async preparation entry to this agent before the first spawn.
|
|
328
|
+
*
|
|
329
|
+
* @param entry - the selected entry.
|
|
330
|
+
*/
|
|
331
|
+
private applyPrepared;
|
|
332
|
+
/**
|
|
333
|
+
* Spawn the process and run the handshake.
|
|
334
|
+
*
|
|
335
|
+
* @returns resolved once the state is `ready`.
|
|
336
|
+
*/
|
|
337
|
+
private beginStart;
|
|
208
338
|
/**
|
|
209
339
|
* One stdout line from the agent. The first must be a matching HELLO.
|
|
210
340
|
*
|
|
@@ -217,6 +347,7 @@ export declare class WslAgent {
|
|
|
217
347
|
* The process died. Requests in flight ride exactly one rebuild; a second
|
|
218
348
|
* death is permanent.
|
|
219
349
|
*
|
|
350
|
+
* @param child - the process that exited.
|
|
220
351
|
* @param code - the exit code, null when killed by a signal.
|
|
221
352
|
* @param signalName - the killing signal, null on a normal exit.
|
|
222
353
|
*/
|
|
@@ -310,6 +441,53 @@ export declare class WslAgent {
|
|
|
310
441
|
* @returns resolved on PONG.
|
|
311
442
|
*/
|
|
312
443
|
ping(signal?: AbortSignal): Promise<void>;
|
|
444
|
+
/**
|
|
445
|
+
* Deliver an out-of-band signal to the resident shell. The serial stdin loop
|
|
446
|
+
* cannot consume a frame while a command runs, so cancellation is a signal;
|
|
447
|
+
* a second `wsl.exe` is the only channel that reaches the agent while it is
|
|
448
|
+
* blocked in `wait`.
|
|
449
|
+
*
|
|
450
|
+
* Only an agent that shares the distro's PID namespace is addressable: the
|
|
451
|
+
* plain agent, or a session runtime (its prefix is a scope plus mounts, but
|
|
452
|
+
* no PID namespace). A confined resident runs inside a PID namespace whose
|
|
453
|
+
* PIDs do not exist in the distro, so signalling its HELLO PID could hit an
|
|
454
|
+
* unrelated process; there the KILL frame remains the (delayed) fallback.
|
|
455
|
+
*
|
|
456
|
+
* @param signal - the signal to deliver (`SIGUSR1` is the cancel trap).
|
|
457
|
+
*/
|
|
458
|
+
/**
|
|
459
|
+
* The cumulative request metrics: queue wait (sent -> ACK) and execution
|
|
460
|
+
* time (ACK -> settle) per request kind, plus the peak in-flight count.
|
|
461
|
+
*
|
|
462
|
+
* @returns a snapshot the caller can keep.
|
|
463
|
+
*/
|
|
464
|
+
metrics(): AgentMetrics;
|
|
465
|
+
/** Reset the cumulative metrics (diagnostics/probes). */
|
|
466
|
+
resetMetrics(): void;
|
|
467
|
+
/**
|
|
468
|
+
* The metric bucket for a request kind.
|
|
469
|
+
* @param kind - fs or exec.
|
|
470
|
+
* @returns the bucket.
|
|
471
|
+
*/
|
|
472
|
+
private kindMetrics;
|
|
473
|
+
/**
|
|
474
|
+
* Record a request that never entered the pending map.
|
|
475
|
+
* @param kind - the bucket.
|
|
476
|
+
*/
|
|
477
|
+
private noteRejected;
|
|
478
|
+
/**
|
|
479
|
+
* Record the dequeue ACK: the request's queue wait.
|
|
480
|
+
* @param pending - the request.
|
|
481
|
+
*/
|
|
482
|
+
private recordAck;
|
|
483
|
+
/**
|
|
484
|
+
* Record a settle: the requests count, the execution time (or the queue wait
|
|
485
|
+
* for a request that never ACKed), and the outcome.
|
|
486
|
+
* @param pending - the request.
|
|
487
|
+
* @param error - the failure, when any.
|
|
488
|
+
*/
|
|
489
|
+
private recordSettle;
|
|
490
|
+
private signalAgent;
|
|
313
491
|
/**
|
|
314
492
|
* Shut the agent down: SHUTDOWN first, then kill after a short grace. The
|
|
315
493
|
* agent becomes reusable — a later `exec` starts a fresh process.
|
|
@@ -317,6 +495,15 @@ export declare class WslAgent {
|
|
|
317
495
|
* @returns resolved once the process is gone.
|
|
318
496
|
*/
|
|
319
497
|
close(): Promise<void>;
|
|
498
|
+
/**
|
|
499
|
+
* Release the host's ends of a dead child's stdio. A descendant that
|
|
500
|
+
* inherited a pipe's write end would otherwise keep the read side — and the
|
|
501
|
+
* fd — open until that descendant exits; the agent's own helpers are
|
|
502
|
+
* redirected at their spawn sites, but the host must not depend on it.
|
|
503
|
+
*
|
|
504
|
+
* @param child - the process whose streams are being released.
|
|
505
|
+
*/
|
|
506
|
+
private teardownStreams;
|
|
320
507
|
/**
|
|
321
508
|
* Write one frame to the live process.
|
|
322
509
|
*
|