@openagents-org/agent-launcher 0.2.183 → 0.2.185
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/icons/codearts.svg +9 -0
- package/package.json +1 -1
- package/registry.json +68 -0
- package/src/adapters/aider.js +2 -9
- package/src/adapters/amp.js +3 -15
- package/src/adapters/antigravity.js +3 -20
- package/src/adapters/base.js +307 -19
- package/src/adapters/claude.js +63 -47
- package/src/adapters/cline.js +2 -13
- package/src/adapters/codearts.js +316 -0
- package/src/adapters/codebuddy.js +3 -21
- package/src/adapters/codex.js +3 -13
- package/src/adapters/commandcode.js +3 -21
- package/src/adapters/copilot.js +2 -26
- package/src/adapters/cursor.js +8 -13
- package/src/adapters/deepseek.js +33 -68
- package/src/adapters/gemini.js +3 -20
- package/src/adapters/goose.js +4 -14
- package/src/adapters/hermes.js +2 -10
- package/src/adapters/index.js +4 -1
- package/src/adapters/kimi-stream.js +44 -2
- package/src/adapters/kimi.js +118 -34
- package/src/adapters/llm-direct.js +17 -6
- package/src/adapters/mini.js +3 -14
- package/src/adapters/nanoclaw.js +25 -13
- package/src/adapters/openclaw.js +41 -0
- package/src/adapters/opencode.js +66 -48
- package/src/adapters/openworker.js +21 -14
- package/src/adapters/pi.js +22 -29
- package/src/daemon.js +76 -5
- package/src/installer.js +48 -16
- package/src/paths.js +187 -92
- package/src/probe-mode.js +36 -0
- package/src/wsl.js +270 -46
package/src/wsl.js
CHANGED
|
@@ -27,20 +27,54 @@
|
|
|
27
27
|
* The mirror direction needs no marker: a path ending in .exe/.cmd/.bat while
|
|
28
28
|
* we are running inside a distro is unambiguously a Windows binary.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* Background resolution checks whether the default distro is already running
|
|
31
|
+
* before probing its login PATH or UNC mount. A stopped distro is left stopped;
|
|
32
|
+
* after the user starts it, the next scan can detect its CLIs. One PATH probe
|
|
33
|
+
* (5 min TTL) then serves later lookups through the \\wsl.localhost UNC mount.
|
|
34
|
+
*
|
|
35
|
+
* Inside the desktop app's main process none of that may block: starting
|
|
36
|
+
* wsl.exe can mean booting the WSL VM, and the first touch of the UNC mount
|
|
37
|
+
* waits on it too — seconds during which the window is frozen. There every
|
|
38
|
+
* probe answers from its cache and refreshes in the background (probe-mode.js).
|
|
34
39
|
*/
|
|
35
40
|
|
|
36
41
|
'use strict';
|
|
37
42
|
|
|
38
43
|
const fs = require('fs');
|
|
39
|
-
const { spawn: nodeSpawn, execFileSync } = require('child_process');
|
|
44
|
+
const { spawn: nodeSpawn, execFile, execFileSync } = require('child_process');
|
|
45
|
+
const { canBlock } = require('./probe-mode');
|
|
40
46
|
|
|
41
47
|
const IS_WINDOWS = process.platform === 'win32';
|
|
42
48
|
const PROBE_TIMEOUT_MS = 10000;
|
|
49
|
+
/** Which binaries were found. Short: an install inside the distro should show up. */
|
|
43
50
|
const CACHE_TTL_MS = 30 * 1000;
|
|
51
|
+
/**
|
|
52
|
+
* The distro list, its UNC root and its login PATH describe the machine, not
|
|
53
|
+
* any one install, and each costs a wsl.exe start — re-asking every 30s kept
|
|
54
|
+
* the VM (and, in the desktop app, the window) busy for nothing.
|
|
55
|
+
*/
|
|
56
|
+
const MACHINE_TTL_MS = 5 * 60 * 1000;
|
|
57
|
+
// A stopped distro must never be woken by background detection. Keep this
|
|
58
|
+
// short so a distro the user starts becomes visible on the next scan.
|
|
59
|
+
const RUNNING_TTL_MS = 2000;
|
|
60
|
+
|
|
61
|
+
function _fresh(cache, ttl) {
|
|
62
|
+
return !!cache.at && Date.now() - cache.at < ttl;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** execFile as a promise of stdout, or null on any failure. stdin is closed. */
|
|
66
|
+
function _execFileOut(file, argv, opts) {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
try {
|
|
69
|
+
const child = execFile(file, argv, { windowsHide: true, timeout: PROBE_TIMEOUT_MS, ...opts }, (err, stdout) => {
|
|
70
|
+
resolve(err ? null : stdout);
|
|
71
|
+
});
|
|
72
|
+
if (child.stdin) child.stdin.end();
|
|
73
|
+
} catch {
|
|
74
|
+
resolve(null);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
44
78
|
|
|
45
79
|
/** Marks a resolved path as living inside the distro: `wsl:/home/u/bin/claude`. */
|
|
46
80
|
const WSL_PREFIX = 'wsl:';
|
|
@@ -95,6 +129,38 @@ function isRunningInWsl() {
|
|
|
95
129
|
// ---------------------------------------------------------------------------
|
|
96
130
|
|
|
97
131
|
let distrosCache = { value: null, at: 0 };
|
|
132
|
+
let distrosRefresh = null;
|
|
133
|
+
let runningDistrosCache = { value: null, at: 0 };
|
|
134
|
+
let runningDistrosRefresh = null;
|
|
135
|
+
|
|
136
|
+
function _parseDistros(buf) {
|
|
137
|
+
return Buffer.from(buf)
|
|
138
|
+
.toString('utf16le')
|
|
139
|
+
.split(/\r?\n/)
|
|
140
|
+
.map((l) => l.replace(/\0/g, '').trim())
|
|
141
|
+
.filter(Boolean);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Record the distro list; a changed one invalidates everything derived from it. */
|
|
145
|
+
function _setDistros(names) {
|
|
146
|
+
const prev = distrosCache.value;
|
|
147
|
+
distrosCache = { value: names, at: Date.now() };
|
|
148
|
+
if (prev && prev.join('\0') !== names.join('\0')) {
|
|
149
|
+
uncRootCache = { value: null, at: 0 };
|
|
150
|
+
shellProbeCache = { value: null, at: 0 };
|
|
151
|
+
wslBinaryCache.clear();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function _refreshDistros() {
|
|
156
|
+
if (distrosCache.value && _fresh(distrosCache, MACHINE_TTL_MS)) return Promise.resolve();
|
|
157
|
+
if (!distrosRefresh) {
|
|
158
|
+
distrosRefresh = _execFileOut('wsl.exe', ['-l', '-q'], { encoding: 'buffer', maxBuffer: 1024 * 1024 })
|
|
159
|
+
.then((out) => _setDistros(out ? _parseDistros(out) : []))
|
|
160
|
+
.finally(() => { distrosRefresh = null; });
|
|
161
|
+
}
|
|
162
|
+
return distrosRefresh;
|
|
163
|
+
}
|
|
98
164
|
|
|
99
165
|
/**
|
|
100
166
|
* Registered, non-broken distro names, default first.
|
|
@@ -110,9 +176,13 @@ let distrosCache = { value: null, at: 0 };
|
|
|
110
176
|
*/
|
|
111
177
|
function wslDistros() {
|
|
112
178
|
if (!IS_WINDOWS) return [];
|
|
113
|
-
if (distrosCache.value &&
|
|
179
|
+
if (distrosCache.value && _fresh(distrosCache, MACHINE_TTL_MS)) {
|
|
114
180
|
return [...distrosCache.value];
|
|
115
181
|
}
|
|
182
|
+
if (!canBlock()) {
|
|
183
|
+
void _refreshDistros();
|
|
184
|
+
return [...(distrosCache.value || [])];
|
|
185
|
+
}
|
|
116
186
|
let names = [];
|
|
117
187
|
try {
|
|
118
188
|
const buf = execFileSync('wsl.exe', ['-l', '-q'], {
|
|
@@ -121,15 +191,11 @@ function wslDistros() {
|
|
|
121
191
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
122
192
|
maxBuffer: 1024 * 1024,
|
|
123
193
|
});
|
|
124
|
-
names = buf
|
|
125
|
-
.toString('utf16le')
|
|
126
|
-
.split(/\r?\n/)
|
|
127
|
-
.map((l) => l.replace(/\0/g, '').trim())
|
|
128
|
-
.filter(Boolean);
|
|
194
|
+
names = _parseDistros(buf);
|
|
129
195
|
} catch {
|
|
130
196
|
names = [];
|
|
131
197
|
}
|
|
132
|
-
|
|
198
|
+
_setDistros(names);
|
|
133
199
|
return [...names];
|
|
134
200
|
}
|
|
135
201
|
|
|
@@ -143,24 +209,100 @@ function defaultDistro() {
|
|
|
143
209
|
return wslDistros()[0] || null;
|
|
144
210
|
}
|
|
145
211
|
|
|
212
|
+
function _refreshRunningDistros() {
|
|
213
|
+
if (_fresh(runningDistrosCache, RUNNING_TTL_MS)) return Promise.resolve();
|
|
214
|
+
if (!runningDistrosRefresh) {
|
|
215
|
+
runningDistrosRefresh = _execFileOut('wsl.exe', ['-l', '--running', '-q'], {
|
|
216
|
+
encoding: 'buffer', maxBuffer: 1024 * 1024,
|
|
217
|
+
})
|
|
218
|
+
.then((out) => { runningDistrosCache = { value: out ? _parseDistros(out) : [], at: Date.now() }; })
|
|
219
|
+
.finally(() => { runningDistrosRefresh = null; });
|
|
220
|
+
}
|
|
221
|
+
return runningDistrosRefresh;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Query WSL's management state without executing a command inside a distro. */
|
|
225
|
+
function _defaultDistroRunning() {
|
|
226
|
+
const distro = defaultDistro();
|
|
227
|
+
if (!distro) return false;
|
|
228
|
+
if (!_fresh(runningDistrosCache, RUNNING_TTL_MS)) {
|
|
229
|
+
if (!canBlock()) {
|
|
230
|
+
void _refreshRunningDistros();
|
|
231
|
+
} else {
|
|
232
|
+
let names = [];
|
|
233
|
+
try {
|
|
234
|
+
names = _parseDistros(execFileSync('wsl.exe', ['-l', '--running', '-q'], {
|
|
235
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
236
|
+
windowsHide: true,
|
|
237
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
238
|
+
maxBuffer: 1024 * 1024,
|
|
239
|
+
}));
|
|
240
|
+
} catch { /* WSL unavailable: do not wake it */ }
|
|
241
|
+
runningDistrosCache = { value: names, at: Date.now() };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return (runningDistrosCache.value || []).includes(distro);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function _defaultDistroRunningAsync() {
|
|
248
|
+
await _refreshDistros();
|
|
249
|
+
const distro = (distrosCache.value || [])[0];
|
|
250
|
+
if (!distro) return false;
|
|
251
|
+
await _refreshRunningDistros();
|
|
252
|
+
return (runningDistrosCache.value || []).includes(distro);
|
|
253
|
+
}
|
|
254
|
+
|
|
146
255
|
// ---------------------------------------------------------------------------
|
|
147
256
|
// Windows side: reaching into the distro's filesystem without starting one
|
|
148
257
|
// ---------------------------------------------------------------------------
|
|
149
258
|
|
|
150
259
|
let uncRootCache = { value: null, at: 0 };
|
|
260
|
+
let uncRootRefresh = null;
|
|
261
|
+
|
|
262
|
+
const UNC_PREFIXES = ['\\\\wsl.localhost\\', '\\\\wsl$\\'];
|
|
263
|
+
|
|
264
|
+
function _refreshUncRoot() {
|
|
265
|
+
if (_fresh(uncRootCache, MACHINE_TTL_MS)) return Promise.resolve();
|
|
266
|
+
if (!uncRootRefresh) {
|
|
267
|
+
uncRootRefresh = (async () => {
|
|
268
|
+
if (!(await _defaultDistroRunningAsync())) return;
|
|
269
|
+
const distro = (distrosCache.value || [])[0];
|
|
270
|
+
if (!distro) return;
|
|
271
|
+
let root = null;
|
|
272
|
+
for (const prefix of UNC_PREFIXES) {
|
|
273
|
+
try {
|
|
274
|
+
await fs.promises.access(prefix + distro);
|
|
275
|
+
root = prefix + distro;
|
|
276
|
+
break;
|
|
277
|
+
} catch {
|
|
278
|
+
/* unreachable share — try the next spelling */
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
uncRootCache = { value: root, at: Date.now() };
|
|
282
|
+
})()
|
|
283
|
+
.catch(() => {})
|
|
284
|
+
.finally(() => { uncRootRefresh = null; });
|
|
285
|
+
}
|
|
286
|
+
return uncRootRefresh;
|
|
287
|
+
}
|
|
151
288
|
|
|
152
289
|
/**
|
|
153
290
|
* The UNC prefix that maps the default distro's root into Windows, or null.
|
|
154
291
|
* WSL2 serves \\wsl.localhost\<distro>; WSL1 and older builds only \\wsl$\.
|
|
155
292
|
*/
|
|
156
293
|
function _uncRoot() {
|
|
157
|
-
if (
|
|
294
|
+
if (!_defaultDistroRunning()) return null;
|
|
295
|
+
if (_fresh(uncRootCache, MACHINE_TTL_MS)) {
|
|
296
|
+
return uncRootCache.value;
|
|
297
|
+
}
|
|
298
|
+
if (!canBlock()) {
|
|
299
|
+
void _refreshUncRoot();
|
|
158
300
|
return uncRootCache.value;
|
|
159
301
|
}
|
|
160
302
|
const distro = defaultDistro();
|
|
161
303
|
if (!distro) return null;
|
|
162
304
|
let root = null;
|
|
163
|
-
for (const prefix of
|
|
305
|
+
for (const prefix of UNC_PREFIXES) {
|
|
164
306
|
const candidate = prefix + distro;
|
|
165
307
|
try {
|
|
166
308
|
if (fs.existsSync(candidate)) { root = candidate; break; }
|
|
@@ -198,9 +340,69 @@ function toUncPath(linuxPath) {
|
|
|
198
340
|
|
|
199
341
|
const PROBE_DELIM = '__OPENAGENTS_WSL__';
|
|
200
342
|
let shellProbeCache = { value: null, at: 0 };
|
|
343
|
+
let shellProbeRefresh = null;
|
|
344
|
+
|
|
345
|
+
const SHELL_PROBE_SCRIPT =
|
|
346
|
+
'echo ' + PROBE_DELIM + '; echo "$PATH"; echo "$HOME"; ' +
|
|
347
|
+
"wslpath -u 'C:\\' 2>/dev/null || echo /mnt/c/";
|
|
348
|
+
const SHELL_PROBE_ARGVS = [
|
|
349
|
+
['-e', 'bash', '-ilc', SHELL_PROBE_SCRIPT],
|
|
350
|
+
['-e', 'sh', '-lc', SHELL_PROBE_SCRIPT],
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
function _emptyShellProbe() {
|
|
354
|
+
return { dirs: [], home: null, automount: '/mnt/' };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** The probe's delimited output as {dirs, home, automount}, or null if unusable. */
|
|
358
|
+
function _parseShellProbe(raw) {
|
|
359
|
+
const idx = raw.indexOf(PROBE_DELIM);
|
|
360
|
+
if (idx === -1) return null;
|
|
361
|
+
const lines = raw.slice(idx + PROBE_DELIM.length).split(/\r?\n/).map((l) => l.trim());
|
|
362
|
+
const pathLine = lines[1];
|
|
363
|
+
const homeLine = lines[2];
|
|
364
|
+
const cRoot = lines[3];
|
|
365
|
+
if (!pathLine) return null;
|
|
366
|
+
const dirs = [];
|
|
367
|
+
for (const d of pathLine.split(':')) {
|
|
368
|
+
const dir = d.trim();
|
|
369
|
+
// /mnt/<drive> entries are the Windows PATH bounced back by interop.
|
|
370
|
+
// They hold Windows binaries, never the in-distro install we are after.
|
|
371
|
+
if (dir && dir.startsWith('/') && !/^\/mnt\/[a-z]\//i.test(dir) && !dirs.includes(dir)) {
|
|
372
|
+
dirs.push(dir);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// `wslpath -u 'C:\'` answers /mnt/c/ by default, but /etc/wsl.conf can
|
|
376
|
+
// move the automount root — derive it rather than hardcoding /mnt.
|
|
377
|
+
let automount = '/mnt/';
|
|
378
|
+
const m = /^(\/.*\/)c\/?$/i.exec(cRoot || '');
|
|
379
|
+
if (m) automount = m[1];
|
|
380
|
+
return { dirs, home: homeLine && homeLine.startsWith('/') ? homeLine : null, automount };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function _refreshShellProbe() {
|
|
384
|
+
if (_fresh(shellProbeCache, MACHINE_TTL_MS)) return Promise.resolve();
|
|
385
|
+
if (!shellProbeRefresh) {
|
|
386
|
+
shellProbeRefresh = (async () => {
|
|
387
|
+
if (!(await _defaultDistroRunningAsync())) return;
|
|
388
|
+
let out = null;
|
|
389
|
+
for (const argv of SHELL_PROBE_ARGVS) {
|
|
390
|
+
const raw = await _execFileOut('wsl.exe', argv, { encoding: 'utf-8', maxBuffer: 4 * 1024 * 1024 });
|
|
391
|
+
out = raw == null ? null : _parseShellProbe(raw);
|
|
392
|
+
if (out) break;
|
|
393
|
+
}
|
|
394
|
+
shellProbeCache = { value: out || _emptyShellProbe(), at: Date.now() };
|
|
395
|
+
// Lookups answered before the PATH was known searched too few dirs.
|
|
396
|
+
if (out) wslBinaryCache.clear();
|
|
397
|
+
})()
|
|
398
|
+
.catch(() => {})
|
|
399
|
+
.finally(() => { shellProbeRefresh = null; });
|
|
400
|
+
}
|
|
401
|
+
return shellProbeRefresh;
|
|
402
|
+
}
|
|
201
403
|
|
|
202
404
|
/**
|
|
203
|
-
* One wsl.exe call
|
|
405
|
+
* One wsl.exe call (5 min TTL) that answers everything later lookups
|
|
204
406
|
* need: the login PATH, $HOME, and the real automount root.
|
|
205
407
|
*
|
|
206
408
|
* `bash -ilc` — INTERACTIVE as well as login — because the version managers
|
|
@@ -211,18 +413,19 @@ let shellProbeCache = { value: null, at: 0 };
|
|
|
211
413
|
* stderr is dropped: an interactive shell with no tty warns about job control.
|
|
212
414
|
*/
|
|
213
415
|
function _shellProbe() {
|
|
214
|
-
const empty =
|
|
215
|
-
if (
|
|
416
|
+
const empty = _emptyShellProbe();
|
|
417
|
+
if (!_defaultDistroRunning()) return empty;
|
|
418
|
+
if (_fresh(shellProbeCache, MACHINE_TTL_MS)) {
|
|
419
|
+
return shellProbeCache.value || empty;
|
|
420
|
+
}
|
|
421
|
+
if (!canBlock()) {
|
|
422
|
+
void _refreshShellProbe();
|
|
216
423
|
return shellProbeCache.value || empty;
|
|
217
424
|
}
|
|
218
425
|
if (!isWslAvailable()) return empty;
|
|
219
426
|
|
|
220
|
-
const script =
|
|
221
|
-
'echo ' + PROBE_DELIM + '; echo "$PATH"; echo "$HOME"; ' +
|
|
222
|
-
"wslpath -u 'C:\\' 2>/dev/null || echo /mnt/c/";
|
|
223
|
-
|
|
224
427
|
let out = null;
|
|
225
|
-
for (const argv of
|
|
428
|
+
for (const argv of SHELL_PROBE_ARGVS) {
|
|
226
429
|
try {
|
|
227
430
|
const raw = execFileSync('wsl.exe', argv, {
|
|
228
431
|
encoding: 'utf-8',
|
|
@@ -231,29 +434,8 @@ function _shellProbe() {
|
|
|
231
434
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
232
435
|
maxBuffer: 4 * 1024 * 1024,
|
|
233
436
|
});
|
|
234
|
-
|
|
235
|
-
if (
|
|
236
|
-
const lines = raw.slice(idx + PROBE_DELIM.length).split(/\r?\n/).map((l) => l.trim());
|
|
237
|
-
const pathLine = lines[1];
|
|
238
|
-
const homeLine = lines[2];
|
|
239
|
-
const cRoot = lines[3];
|
|
240
|
-
if (!pathLine) continue;
|
|
241
|
-
const dirs = [];
|
|
242
|
-
for (const d of pathLine.split(':')) {
|
|
243
|
-
const dir = d.trim();
|
|
244
|
-
// /mnt/<drive> entries are the Windows PATH bounced back by interop.
|
|
245
|
-
// They hold Windows binaries, never the in-distro install we are after.
|
|
246
|
-
if (dir && dir.startsWith('/') && !/^\/mnt\/[a-z]\//i.test(dir) && !dirs.includes(dir)) {
|
|
247
|
-
dirs.push(dir);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
// `wslpath -u 'C:\'` answers /mnt/c/ by default, but /etc/wsl.conf can
|
|
251
|
-
// move the automount root — derive it rather than hardcoding /mnt.
|
|
252
|
-
let automount = '/mnt/';
|
|
253
|
-
const m = /^(\/.*\/)c\/?$/i.exec(cRoot || '');
|
|
254
|
-
if (m) automount = m[1];
|
|
255
|
-
out = { dirs, home: homeLine && homeLine.startsWith('/') ? homeLine : null, automount };
|
|
256
|
-
break;
|
|
437
|
+
out = _parseShellProbe(raw);
|
|
438
|
+
if (out) break;
|
|
257
439
|
} catch {
|
|
258
440
|
/* try the next shell */
|
|
259
441
|
}
|
|
@@ -290,6 +472,42 @@ function wslBinDirs() {
|
|
|
290
472
|
}
|
|
291
473
|
|
|
292
474
|
const wslBinaryCache = new Map();
|
|
475
|
+
const wslBinaryRefresh = new Map();
|
|
476
|
+
|
|
477
|
+
/** resolveWslBinary's search, off the calling thread. See probe-mode.js. */
|
|
478
|
+
function _refreshWslBinary(key, list) {
|
|
479
|
+
if (wslBinaryRefresh.has(key)) return wslBinaryRefresh.get(key);
|
|
480
|
+
const job = (async () => {
|
|
481
|
+
if (!(await _defaultDistroRunningAsync())) return;
|
|
482
|
+
let value = null;
|
|
483
|
+
if ((distrosCache.value || []).length) {
|
|
484
|
+
await Promise.all([_refreshUncRoot(), _refreshShellProbe()]);
|
|
485
|
+
if (uncRootCache.value) {
|
|
486
|
+
search:
|
|
487
|
+
for (const dir of wslBinDirs()) {
|
|
488
|
+
for (const name of list) {
|
|
489
|
+
const linuxPath = dir.replace(/\/+$/, '') + '/' + name;
|
|
490
|
+
const unc = toUncPath(linuxPath);
|
|
491
|
+
if (!unc) continue;
|
|
492
|
+
try {
|
|
493
|
+
if ((await fs.promises.stat(unc)).isFile()) {
|
|
494
|
+
value = WSL_PREFIX + linuxPath;
|
|
495
|
+
break search;
|
|
496
|
+
}
|
|
497
|
+
} catch {
|
|
498
|
+
/* unreadable path — keep looking */
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
wslBinaryCache.set(key, { value, at: Date.now() });
|
|
505
|
+
})()
|
|
506
|
+
.catch(() => {})
|
|
507
|
+
.finally(() => wslBinaryRefresh.delete(key));
|
|
508
|
+
wslBinaryRefresh.set(key, job);
|
|
509
|
+
return job;
|
|
510
|
+
}
|
|
293
511
|
|
|
294
512
|
/**
|
|
295
513
|
* Resolve an agent binary inside the default distro, or null.
|
|
@@ -297,18 +515,23 @@ const wslBinaryCache = new Map();
|
|
|
297
515
|
* Returns the path as the DISTRO sees it, behind the `wsl:` marker
|
|
298
516
|
* (`wsl:/home/u/.local/bin/claude`) — the inner path is what `wsl.exe -e` must
|
|
299
517
|
* be handed. Existence is proved through the UNC mount, so a machine with WSL
|
|
300
|
-
* installed but no distro pays
|
|
518
|
+
* installed but no running default distro pays only for WSL management queries.
|
|
301
519
|
*
|
|
302
520
|
* @param {string|string[]} names binary name(s), e.g. 'claude' or ['cursor-agent','agent']
|
|
303
521
|
* @returns {string|null} marked in-distro path
|
|
304
522
|
*/
|
|
305
523
|
function resolveWslBinary(names) {
|
|
306
524
|
if (!IS_WINDOWS) return null;
|
|
525
|
+
if (!_defaultDistroRunning()) return null;
|
|
307
526
|
const list = (Array.isArray(names) ? names : [names]).filter(Boolean);
|
|
308
527
|
if (!list.length) return null;
|
|
309
528
|
const key = list.join('\0');
|
|
310
529
|
const cached = wslBinaryCache.get(key);
|
|
311
530
|
if (cached && Date.now() - cached.at < CACHE_TTL_MS) return cached.value;
|
|
531
|
+
if (!canBlock()) {
|
|
532
|
+
void _refreshWslBinary(key, list);
|
|
533
|
+
return cached ? cached.value : null;
|
|
534
|
+
}
|
|
312
535
|
|
|
313
536
|
let value = null;
|
|
314
537
|
if (isWslAvailable() && _uncRoot()) {
|
|
@@ -348,6 +571,7 @@ function clearWslBinaryCache() {
|
|
|
348
571
|
/** Full reset, including the per-process probes. For tests and rare rescans. */
|
|
349
572
|
function clearWslCache() {
|
|
350
573
|
distrosCache = { value: null, at: 0 };
|
|
574
|
+
runningDistrosCache = { value: null, at: 0 };
|
|
351
575
|
uncRootCache = { value: null, at: 0 };
|
|
352
576
|
shellProbeCache = { value: null, at: 0 };
|
|
353
577
|
wslBinaryCache.clear();
|