mindwire 0.1.28 → 0.1.30
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/dist/{chunk-5WYSGXFN.js → chunk-RN7ERVJ2.js} +39 -12
- package/dist/chunk-RN7ERVJ2.js.map +1 -0
- package/dist/cli.js +87 -89
- package/dist/cli.js.map +1 -1
- package/dist/computer/connection-flow.d.ts +16 -3
- package/dist/computer/controller-handoff.d.ts +6 -0
- package/dist/computer/lifecycle.d.ts +4 -0
- package/dist/computer/relay-health.d.ts +11 -0
- package/dist/computer.d.ts +17 -1
- package/dist/{controller-5ZTNIHUV.js → controller-DIJKMWWU.js} +156 -11
- package/dist/controller-DIJKMWWU.js.map +1 -0
- package/dist/controller-handoff-4GXF4ATR.js +71 -0
- package/dist/controller-handoff-4GXF4ATR.js.map +1 -0
- package/dist/index.cjs +20 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -12
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +1 -0
- package/package.json +7 -3
- package/dist/chunk-5WYSGXFN.js.map +0 -1
- package/dist/controller-5ZTNIHUV.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readJSON, processStateAlive, currentProcessIdentity, ensureDaemonBinary, SDK_VERSION, computerClient, processIdentity, adoptProcess, writeJSON, directRoutes, ownChild } from './chunk-5WYSGXFN.js';
|
|
2
1
|
import { binaryCacheDirectory, cachedExecutable, verifyReleaseBytes, cacheExecutable, executableChecksum } from './chunk-3FR7BFAW.js';
|
|
2
|
+
import { readJSON, processStateAlive, currentProcessIdentity, ensureDaemonBinary, SDK_VERSION, computerClient, processIdentity, adoptProcess, writeJSON, directRoutes, CONTROLLER_PROTOCOL, ownChild } from './chunk-RN7ERVJ2.js';
|
|
3
3
|
import './chunk-T6TD76UA.js';
|
|
4
4
|
import { execFile, execFileSync, spawn } from 'child_process';
|
|
5
5
|
import { randomBytes } from 'crypto';
|
|
@@ -9,6 +9,10 @@ import * as path2 from 'path';
|
|
|
9
9
|
import { setTimeout as setTimeout$1 } from 'timers/promises';
|
|
10
10
|
import { tmpdir } from 'os';
|
|
11
11
|
import { promisify } from 'util';
|
|
12
|
+
import { request } from 'https';
|
|
13
|
+
import { lookup } from 'dns';
|
|
14
|
+
import { Resolver } from 'dns/promises';
|
|
15
|
+
import WebSocket from 'ws';
|
|
12
16
|
|
|
13
17
|
var CLOUDFLARED_VERSION = "2026.9.3";
|
|
14
18
|
var execute = promisify(execFile);
|
|
@@ -29,8 +33,8 @@ async function ensureCloudflareBinary(options = {}) {
|
|
|
29
33
|
const cached = await cachedExecutable(bin);
|
|
30
34
|
if (cached) return cached;
|
|
31
35
|
await options.onProgress?.("Downloading the secure connection helper\u2026");
|
|
32
|
-
const
|
|
33
|
-
const release = await
|
|
36
|
+
const request2 = options.fetch ?? fetch;
|
|
37
|
+
const release = await request2(
|
|
34
38
|
`https://api.github.com/repos/cloudflare/cloudflared/releases/tags/${CLOUDFLARED_VERSION}`,
|
|
35
39
|
{ headers: { Accept: "application/vnd.github+json" }, signal: AbortSignal.timeout(2e4) }
|
|
36
40
|
);
|
|
@@ -40,7 +44,7 @@ async function ensureCloudflareBinary(options = {}) {
|
|
|
40
44
|
const checksum = item?.digest?.match(/^sha256:([a-f0-9]{64})$/i)?.[1];
|
|
41
45
|
const expectedURL = `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/${asset}`;
|
|
42
46
|
if (!checksum || item?.browser_download_url !== expectedURL) throw new Error("The connection helper has no verified release asset for this computer.");
|
|
43
|
-
const downloaded = await
|
|
47
|
+
const downloaded = await request2(expectedURL, { signal: AbortSignal.timeout(12e4) });
|
|
44
48
|
if (!downloaded.ok) throw new Error("Couldn't download the connection helper. Check your internet connection and retry.");
|
|
45
49
|
const archive = new Uint8Array(await downloaded.arrayBuffer());
|
|
46
50
|
await verifyReleaseBytes(archive, checksum, asset);
|
|
@@ -192,6 +196,89 @@ async function startRelay(options, port, runtime = {}) {
|
|
|
192
196
|
throw error;
|
|
193
197
|
}
|
|
194
198
|
}
|
|
199
|
+
var relayLookup = (hostname, options, callback) => {
|
|
200
|
+
lookup(hostname, options, (error, address, family) => {
|
|
201
|
+
if (error?.code !== "ENOTFOUND" || !/^[a-z0-9-]+\.trycloudflare\.com$/i.test(hostname)) {
|
|
202
|
+
callback(error, address, family);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const resolver = new Resolver({ timeout: 3e3, tries: 1 });
|
|
206
|
+
void Promise.all([
|
|
207
|
+
options.family === 6 ? [] : resolver.resolve4(hostname).then((addresses) => addresses.map((address2) => ({ address: address2, family: 4 })), () => []),
|
|
208
|
+
options.family === 4 ? [] : resolver.resolve6(hostname).then((addresses) => addresses.map((address2) => ({ address: address2, family: 6 })), () => [])
|
|
209
|
+
]).then((results) => {
|
|
210
|
+
const addresses = results.flat();
|
|
211
|
+
if (!addresses.length) {
|
|
212
|
+
callback(error, address, family);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (options.all) callback(null, addresses);
|
|
216
|
+
else callback(null, addresses[0].address, addresses[0].family);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
async function checkRelay(route, options = {}) {
|
|
221
|
+
options.signal?.throwIfAborted();
|
|
222
|
+
if (route.kind !== "websocket") throw new Error("Expected an internet tunnel address.");
|
|
223
|
+
await new Promise((resolve2, reject) => {
|
|
224
|
+
const socket = new WebSocket(route.url, { maxPayload: 4096, perMessageDeflate: false, followRedirects: false, lookup: relayLookup });
|
|
225
|
+
let finished = false, banner = "";
|
|
226
|
+
const finish = (error) => {
|
|
227
|
+
if (finished) return;
|
|
228
|
+
finished = true;
|
|
229
|
+
clearTimeout(timer);
|
|
230
|
+
options.signal?.removeEventListener("abort", aborted);
|
|
231
|
+
socket.terminate();
|
|
232
|
+
if (error) reject(error);
|
|
233
|
+
else resolve2();
|
|
234
|
+
};
|
|
235
|
+
const aborted = () => finish(options.signal?.reason ?? new Error("Connection check cancelled."));
|
|
236
|
+
const timer = setTimeout(() => finish(new Error("The internet tunnel isn't responding. Reconnecting\u2026")), options.timeoutMs ?? 8e3);
|
|
237
|
+
socket.on("error", (error) => finish(new Error(
|
|
238
|
+
error.code === "ENOTFOUND" ? "The internet address isn't available. Checking the tunnel\u2026" : /(?:[Uu]nexpected server response|[Ee]xpected 101)/.test(error.message) ? "The internet tunnel isn't reaching Mindwire. Reconnecting\u2026" : "The internet tunnel couldn't connect. Retrying\u2026",
|
|
239
|
+
{ cause: error }
|
|
240
|
+
)));
|
|
241
|
+
socket.on("close", () => finish(new Error("The internet tunnel closed before reaching Mindwire. Retrying\u2026")));
|
|
242
|
+
socket.on("message", (data, binary) => {
|
|
243
|
+
if (!binary) {
|
|
244
|
+
finish(new Error("The internet tunnel returned an invalid SSH response."));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
banner += data.toString();
|
|
248
|
+
if (banner.length > 1024) {
|
|
249
|
+
finish(new Error("The internet tunnel returned an invalid SSH response."));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (banner.includes("\n")) {
|
|
253
|
+
finish(banner.split("\n")[0]?.trim() === "SSH-2.0-Mindwire" ? void 0 : new Error("The internet tunnel isn't connected to the Mindwire SSH service."));
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
options.signal?.addEventListener("abort", aborted, { once: true });
|
|
257
|
+
if (options.signal?.aborted) aborted();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async function relayProviderReachable(options, signal) {
|
|
261
|
+
if (options.kind !== "cloudflare" && options.kind !== "ngrok") return false;
|
|
262
|
+
signal?.throwIfAborted();
|
|
263
|
+
return new Promise((resolve2) => {
|
|
264
|
+
const url = options.kind === "cloudflare" ? "https://api.trycloudflare.com/" : "https://api.ngrok.com/";
|
|
265
|
+
let finished = false;
|
|
266
|
+
const done = (reachable) => {
|
|
267
|
+
if (finished) return;
|
|
268
|
+
finished = true;
|
|
269
|
+
clearTimeout(timer);
|
|
270
|
+
probe.destroy();
|
|
271
|
+
resolve2(reachable);
|
|
272
|
+
};
|
|
273
|
+
const probe = request(url, { method: "HEAD", signal }, (response) => {
|
|
274
|
+
response.destroy();
|
|
275
|
+
done(true);
|
|
276
|
+
});
|
|
277
|
+
const timer = setTimeout(() => done(false), 5e3);
|
|
278
|
+
probe.on("error", () => done(false));
|
|
279
|
+
probe.end();
|
|
280
|
+
});
|
|
281
|
+
}
|
|
195
282
|
|
|
196
283
|
// src/computer/controller.ts
|
|
197
284
|
function recoveryDelay(failures) {
|
|
@@ -206,11 +293,13 @@ async function superviseComputer(directory) {
|
|
|
206
293
|
const previous = await readJSON(controllerPath);
|
|
207
294
|
if (await processStateAlive(previous)) throw new Error("The computer already has a service controller.");
|
|
208
295
|
const owner = await currentProcessIdentity();
|
|
209
|
-
const phase = (message) => writeJSON(controllerPath, { ...owner, ready: false, phase: message, recovering: true });
|
|
296
|
+
const phase = (message) => writeJSON(controllerPath, { ...owner, protocol: CONTROLLER_PROTOCOL, cliVersion: SDK_VERSION, ready: false, phase: message, recovering: true });
|
|
210
297
|
await phase("Preparing Mindwire\u2026");
|
|
211
298
|
let child, relay, stopping = false;
|
|
212
299
|
let relayTask, updateTask;
|
|
213
300
|
let relayResult;
|
|
301
|
+
let checkTask;
|
|
302
|
+
let checkResult;
|
|
214
303
|
const abort = new AbortController();
|
|
215
304
|
const stop = () => {
|
|
216
305
|
stopping = true;
|
|
@@ -295,6 +384,9 @@ async function superviseComputer(directory) {
|
|
|
295
384
|
let daemonError, nextDaemonAt = 0, daemonFailures = 0;
|
|
296
385
|
let nextRelayAt = 0, relayFailures = 0, changingDaemon = false;
|
|
297
386
|
let daemonReadyAt = Date.now(), relayReadyAt = Date.now();
|
|
387
|
+
let relayReachable = false, relayWasReachable = false, relayHealthFailures = 0;
|
|
388
|
+
let relayFailedAt = 0;
|
|
389
|
+
let nextCheckAt = 0, checkedAt = 0;
|
|
298
390
|
try {
|
|
299
391
|
await phase("Checking the Mindwire service\u2026");
|
|
300
392
|
let binary = config.daemonBin ?? await ensureDaemonBinary({ version: config.version ?? SDK_VERSION, cacheDir: process.env.MINDWIRE_RELEASE_CACHE_DIR });
|
|
@@ -333,7 +425,7 @@ async function superviseComputer(directory) {
|
|
|
333
425
|
nextDaemonAt = Date.now() + recoveryDelay(++daemonFailures);
|
|
334
426
|
}
|
|
335
427
|
if (child?.alive() && Date.now() - daemonReadyAt > 6e4) daemonFailures = 0;
|
|
336
|
-
if (relay?.owner?.alive() && Date.now() - relayReadyAt > 6e4) relayFailures = 0;
|
|
428
|
+
if (relayReachable && relay?.owner?.alive() && Date.now() - relayReadyAt > 6e4) relayFailures = 0;
|
|
337
429
|
if (!changingDaemon && !child?.alive() && Date.now() >= nextDaemonAt) {
|
|
338
430
|
daemonError = "The service stopped. Restarting it\u2026";
|
|
339
431
|
await phase(daemonError);
|
|
@@ -352,6 +444,8 @@ async function superviseComputer(directory) {
|
|
|
352
444
|
}
|
|
353
445
|
if (relay?.owner && !relay.owner.alive()) {
|
|
354
446
|
relay = void 0;
|
|
447
|
+
relayReachable = false;
|
|
448
|
+
relayWasReachable = false;
|
|
355
449
|
relayError = "The internet tunnel disconnected. Reconnecting\u2026";
|
|
356
450
|
nextRelayAt = Date.now() + recoveryDelay(++relayFailures);
|
|
357
451
|
}
|
|
@@ -363,6 +457,11 @@ async function superviseComputer(directory) {
|
|
|
363
457
|
relay = result.handle;
|
|
364
458
|
relayError = void 0;
|
|
365
459
|
relayReadyAt = Date.now();
|
|
460
|
+
relayReachable = false;
|
|
461
|
+
relayWasReachable = false;
|
|
462
|
+
relayHealthFailures = 0;
|
|
463
|
+
relayFailedAt = 0;
|
|
464
|
+
nextCheckAt = 0;
|
|
366
465
|
if (relay.owner?.identity) await writeJSON(relayPath, {
|
|
367
466
|
owner: relay.owner.identity,
|
|
368
467
|
port: websocketPort,
|
|
@@ -395,8 +494,50 @@ async function superviseComputer(directory) {
|
|
|
395
494
|
relayResult = { error };
|
|
396
495
|
});
|
|
397
496
|
}
|
|
497
|
+
if (checkResult) {
|
|
498
|
+
const result = checkResult;
|
|
499
|
+
checkResult = void 0;
|
|
500
|
+
checkTask = void 0;
|
|
501
|
+
if (result.relay === relay) {
|
|
502
|
+
checkedAt = Date.now();
|
|
503
|
+
relayReachable = !result.error;
|
|
504
|
+
if (!result.error) {
|
|
505
|
+
relayWasReachable = true;
|
|
506
|
+
relayHealthFailures = 0;
|
|
507
|
+
relayFailedAt = 0;
|
|
508
|
+
relayError = void 0;
|
|
509
|
+
nextCheckAt = checkedAt + 3e4;
|
|
510
|
+
} else {
|
|
511
|
+
relayError = result.error instanceof Error ? result.error.message : "The internet tunnel couldn't connect. Retrying\u2026";
|
|
512
|
+
relayFailedAt ||= checkedAt;
|
|
513
|
+
relayHealthFailures++;
|
|
514
|
+
nextCheckAt = checkedAt + Math.min(15e3, recoveryDelay(relayHealthFailures));
|
|
515
|
+
if (result.canRestart) {
|
|
516
|
+
await relay.close();
|
|
517
|
+
relay = void 0;
|
|
518
|
+
relayWasReachable = false;
|
|
519
|
+
nextRelayAt = Date.now() + recoveryDelay(++relayFailures);
|
|
520
|
+
await fs.rm(relayPath, { force: true });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
const requestedCheck = await readJSON(path2.join(directory, "computer-check.json"));
|
|
526
|
+
if (config.relay.kind === "none") checkedAt = Math.max(checkedAt, requestedCheck?.requestedAt ?? 0);
|
|
527
|
+
if (relay?.route && !checkTask && (Date.now() >= nextCheckAt || (requestedCheck?.requestedAt ?? 0) > checkedAt)) {
|
|
528
|
+
const checking = relay;
|
|
529
|
+
checkTask = (async () => {
|
|
530
|
+
try {
|
|
531
|
+
await checkRelay(checking.route, { signal: abort.signal });
|
|
532
|
+
checkResult = { relay: checking };
|
|
533
|
+
} catch (error) {
|
|
534
|
+
const canRestart = !!checking.owner && relayHealthFailures >= 2 && relayFailedAt > 0 && Date.now() - relayFailedAt >= 9e4 && await relayProviderReachable(config.relay, abort.signal).catch(() => false);
|
|
535
|
+
checkResult = { relay: checking, error, canRestart };
|
|
536
|
+
}
|
|
537
|
+
})();
|
|
538
|
+
}
|
|
398
539
|
routes = directRoutes(config, sshPort);
|
|
399
|
-
if (relay?.route) routes.push(relay.route);
|
|
540
|
+
if (relay?.route && relayWasReachable) routes.push(relay.route);
|
|
400
541
|
const routeJSON = JSON.stringify(routes);
|
|
401
542
|
if (!changingDaemon && client && child?.alive() && routeJSON !== published) {
|
|
402
543
|
try {
|
|
@@ -405,12 +546,15 @@ async function superviseComputer(directory) {
|
|
|
405
546
|
} catch {
|
|
406
547
|
}
|
|
407
548
|
}
|
|
408
|
-
const ready = !!client && !!child?.alive() && !changingDaemon && routes.length > 0 && published === routeJSON && (config.relay.kind === "none" ||
|
|
549
|
+
const ready = !!client && !!child?.alive() && !changingDaemon && routes.length > 0 && published === routeJSON && (config.relay.kind === "none" || relayReachable);
|
|
409
550
|
const state = {
|
|
410
551
|
...owner,
|
|
552
|
+
protocol: CONTROLLER_PROTOCOL,
|
|
553
|
+
cliVersion: SDK_VERSION,
|
|
411
554
|
ready,
|
|
412
555
|
routes,
|
|
413
|
-
|
|
556
|
+
checkedAt,
|
|
557
|
+
phase: ready ? void 0 : daemonError ?? relayError ?? relayMessage,
|
|
414
558
|
error: daemonError ?? relayError,
|
|
415
559
|
recovering: !ready
|
|
416
560
|
};
|
|
@@ -481,6 +625,7 @@ async function superviseComputer(directory) {
|
|
|
481
625
|
} finally {
|
|
482
626
|
abort.abort();
|
|
483
627
|
await relayTask;
|
|
628
|
+
await checkTask;
|
|
484
629
|
await updateTask;
|
|
485
630
|
if (relayResult?.handle && relayResult.handle !== relay) await relayResult.handle.close();
|
|
486
631
|
await relay?.close();
|
|
@@ -492,5 +637,5 @@ async function superviseComputer(directory) {
|
|
|
492
637
|
}
|
|
493
638
|
|
|
494
639
|
export { recoveryDelay, superviseComputer };
|
|
495
|
-
//# sourceMappingURL=controller-
|
|
496
|
-
//# sourceMappingURL=controller-
|
|
640
|
+
//# sourceMappingURL=controller-DIJKMWWU.js.map
|
|
641
|
+
//# sourceMappingURL=controller-DIJKMWWU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/computer/cloudflare-binary.ts","../src/computer/relay.ts","../src/computer/relay-health.ts","../src/computer/controller.ts"],"names":["path","request","resolve","address","process","spawn","client","delay","owner","state"],"mappings":"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","file":"controller-DIJKMWWU.js","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { mkdtemp, writeFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport * as path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { binaryCacheDirectory, cachedExecutable, cacheExecutable, executableChecksum, verifyReleaseBytes } from \"../binary-cache.js\";\n\n// The bridge was tested against this release. The helper never updates itself or\n// changes a system installation; a newer Mindwire release can advance this pin.\nexport const CLOUDFLARED_VERSION = \"2026.9.3\";\nconst execute = promisify(execFile);\n\nexport async function ensureCloudflareBinary(options: {\n cacheDir?: string; platform?: string; arch?: string; fetch?: typeof fetch;\n onProgress?: (message: string) => void | Promise<void>;\n} = {}): Promise<string> {\n const platform = options.platform ?? process.platform, arch = options.arch ?? process.arch;\n const cpu = arch === \"x64\" ? \"amd64\" : arch === \"arm64\" ? \"arm64\" : \"\";\n if (!cpu || ![\"darwin\", \"linux\", \"win32\"].includes(platform) || platform === \"win32\" && cpu !== \"amd64\") {\n throw new Error(\"Install cloudflared for this computer, or select another connection provider.\");\n }\n const asset = `cloudflared-${platform === \"win32\" ? \"windows\" : platform}-${cpu}${platform === \"darwin\" ? \".tgz\" : platform === \"win32\" ? \".exe\" : \"\"}`;\n const bin = path.join(options.cacheDir ?? await binaryCacheDirectory(), \"cloudflared\", CLOUDFLARED_VERSION,\n `${platform}-${arch}`, platform === \"win32\" ? \"cloudflared.exe\" : \"cloudflared\");\n const cached = await cachedExecutable(bin);\n if (cached) return cached;\n await options.onProgress?.(\"Downloading the secure connection helper…\");\n const request = options.fetch ?? fetch;\n const release = await request(`https://api.github.com/repos/cloudflare/cloudflared/releases/tags/${CLOUDFLARED_VERSION}`,\n { headers: { Accept: \"application/vnd.github+json\" }, signal: AbortSignal.timeout(20_000) });\n if (!release.ok) throw new Error(\"Couldn't download the connection helper. Retry, install cloudflared yourself, or choose another provider.\");\n const metadata = await release.json() as { assets?: { name: string; digest?: string; browser_download_url: string }[] };\n const item = metadata.assets?.find(value => value.name === asset);\n const checksum = item?.digest?.match(/^sha256:([a-f0-9]{64})$/i)?.[1];\n const expectedURL = `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/${asset}`;\n if (!checksum || item?.browser_download_url !== expectedURL) throw new Error(\"The connection helper has no verified release asset for this computer.\");\n const downloaded = await request(expectedURL, { signal: AbortSignal.timeout(120_000) });\n if (!downloaded.ok) throw new Error(\"Couldn't download the connection helper. Check your internet connection and retry.\");\n const archive = new Uint8Array(await downloaded.arrayBuffer());\n await verifyReleaseBytes(archive, checksum, asset);\n let executable = archive;\n if (platform === \"darwin\") {\n const temporary = await mkdtemp(path.join(tmpdir(), \"mindwire-cloudflared-\"));\n try {\n const archivePath = path.join(temporary, asset);\n await writeFile(archivePath, archive, { mode: 0o600 });\n // Read only this exact archive member to stdout. No archive paths are\n // extracted into the filesystem and no downloaded script is executed.\n const result = await execute(\"/usr/bin/tar\", [\"-xzOf\", archivePath, \"cloudflared\"],\n { encoding: \"buffer\", maxBuffer: 128 * 1024 * 1024, timeout: 30_000 });\n executable = new Uint8Array(result.stdout);\n } finally { await rm(temporary, { recursive: true, force: true }); }\n }\n return cacheExecutable(bin, executable, await executableChecksum(executable));\n}\n\nexport async function cloudflareCommand(options: Parameters<typeof ensureCloudflareBinary>[0]): Promise<string> {\n try {\n await execute(\"cloudflared\", [\"--version\"], { timeout: 3000, windowsHide: true });\n return \"cloudflared\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw new Error(\"The installed cloudflared could not start. Repair it or choose another connection provider.\");\n return ensureCloudflareBinary(options);\n }\n}\n","import { spawn, type ChildProcess } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport type { ComputerRoute } from \"../computer.js\";\nimport { cloudflareCommand } from \"./cloudflare-binary.js\";\nimport { ownChild, type OwnedProcess } from \"./process.js\";\n\nexport interface RelayOptions {\n kind: \"none\" | \"cloudflare\" | \"ngrok\" | \"custom\";\n /** A stable, already configured hostname; custom relays must forward WebSocket upgrades. */\n url?: string;\n cloudflareTokenFile?: string;\n}\nexport interface RelayHandle { route?: ComputerRoute; process?: ChildProcess; owner?: OwnedProcess; close(): void | Promise<void> }\n\nexport function websocketURL(value: string): string {\n const url = new URL(value);\n if (url.protocol === \"https:\") url.protocol = \"wss:\";\n if (url.protocol !== \"wss:\" || !url.hostname || url.username || url.password || url.hash) {\n throw new Error(\"Relay URLs must use wss:// with no embedded credentials or fragment.\");\n }\n if (!url.pathname || url.pathname === \"/\") url.pathname = \"/ssh\";\n return url.toString();\n}\n\n/** Both providers forward encrypted SSH bytes to one loopback WebSocket bridge. */\nexport async function startRelay(options: RelayOptions, port: number, runtime: {\n cacheDir?: string; onProgress?: (message: string) => void | Promise<void>;\n signal?: AbortSignal; onSpawn?: (owner: OwnedProcess) => Promise<void>;\n} = {}): Promise<RelayHandle> {\n runtime.signal?.throwIfAborted();\n if (options.kind === \"none\") return { close() {} };\n if (options.kind === \"custom\") {\n if (!options.url) throw new Error(\"A custom relay needs --relay-url wss://your-host/ssh.\");\n return { route: { kind: \"websocket\", url: websocketURL(options.url) }, close() {} };\n }\n const origin = `http://127.0.0.1:${port}`;\n const env = { ...process.env };\n let command: string, args: string[];\n if (options.kind === \"cloudflare\") {\n command = await cloudflareCommand(runtime);\n await runtime.onProgress?.(\"Connecting through Cloudflare…\");\n if (options.cloudflareTokenFile) {\n if (!options.url) throw new Error(\"A named Cloudflare tunnel needs --relay-url for its configured hostname.\");\n env.TUNNEL_TOKEN = (await readFile(options.cloudflareTokenFile, \"utf8\")).trim();\n args = [\"tunnel\", \"--no-autoupdate\", \"run\"];\n } else {\n if (options.url) throw new Error(\"Use --cloudflare-token-file with a named tunnel, or --relay custom for an existing tunnel.\");\n args = [\"tunnel\", \"--no-autoupdate\", \"--url\", origin];\n }\n } else {\n command = \"ngrok\";\n args = [\"http\", origin, \"--log\", \"stdout\", \"--log-format\", \"json\"];\n if (options.url) {\n const url = new URL(websocketURL(options.url));\n args.push(\"--url\", `https://${url.host}`);\n }\n }\n runtime.signal?.throwIfAborted();\n const child = spawn(command, args, { env, stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true });\n let buffer = \"\";\n try {\n const address = new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => { cleanup(); reject(new Error(`${command} did not establish its tunnel. Check its account and tunnel configuration.`)); }, 45_000);\n const cleanup = () => { clearTimeout(timer); child.off(\"error\", fail); child.off(\"exit\", exited); runtime.signal?.removeEventListener(\"abort\", aborted); };\n const fail = (error: Error) => { cleanup(); reject(new Error(`Could not run ${command}. Install it on the computer first.`, { cause: error })); };\n const exited = () => { cleanup(); reject(new Error(`${command} exited before the tunnel connected. Check its account configuration.`)); };\n const aborted = () => { cleanup(); child.kill(); reject(runtime.signal?.reason ?? new Error(\"Tunnel startup cancelled.\")); };\n let cloudflareAddress = options.url;\n let cloudflareConnected = false;\n const receive = (chunk: Buffer) => {\n const received = buffer + chunk.toString();\n buffer = received.slice(-16_384);\n let found: string | undefined;\n if (options.kind === \"cloudflare\") {\n cloudflareAddress ??= received.match(/https:\\/\\/[a-z0-9-]+\\.trycloudflare\\.com/)?.[0];\n cloudflareConnected ||= /Registered tunnel connection/.test(received);\n // Quick tunnels print a reserved hostname before their outbound\n // connection succeeds. Do not invite a phone until that path is open.\n if (cloudflareConnected) found = cloudflareAddress;\n } else {\n for (const line of received.split(/\\r?\\n/)) {\n try {\n const event = JSON.parse(line) as { msg?: string; url?: string };\n if (event.msg === \"started tunnel\" && event.url?.startsWith(\"https://\")) found = event.url;\n } catch { /* Wait for a complete JSON log record. */ }\n }\n }\n if (found) { cleanup(); resolve(websocketURL(found)); }\n };\n child.once(\"error\", fail); child.once(\"exit\", exited);\n child.stdout!.on(\"data\", receive); child.stderr!.on(\"data\", receive);\n runtime.signal?.addEventListener(\"abort\", aborted, { once: true });\n if (runtime.signal?.aborted) aborted();\n });\n // Install the readiness listeners before recording process ownership. A fast\n // helper can print its hostname while its process identity is being read.\n void address.catch(() => {});\n const owner = await ownChild(child);\n await runtime.onSpawn?.(owner);\n const url = await address;\n runtime.signal?.throwIfAborted();\n // Drain provider output without copying potentially sensitive provider logs into ours.\n child.stdout!.removeAllListeners(\"data\"); child.stderr!.removeAllListeners(\"data\");\n child.stdout!.resume(); child.stderr!.resume();\n child.on(\"error\", () => {});\n return { route: { kind: \"websocket\", url }, process: child, owner, close: () => owner.close() };\n } catch (error) {\n child.on(\"error\", () => {});\n if (child.pid) await (await ownChild(child)).close();\n throw error;\n }\n}\n","import { request } from \"node:https\";\nimport { lookup } from \"node:dns\";\nimport { Resolver } from \"node:dns/promises\";\nimport type { LookupFunction } from \"node:net\";\nimport WebSocket from \"ws\";\nimport type { ComputerRoute } from \"../computer.js\";\nimport type { RelayOptions } from \"./relay.js\";\n\n/** macOS can retain NXDOMAIN in getaddrinfo after the system DNS server already\n * has the new quick-tunnel record. Retry that specific failure with the same\n * system DNS servers. TLS SNI/certificate validation still use the original host. */\nconst relayLookup: LookupFunction = (hostname, options, callback) => {\n lookup(hostname, options, (error, address, family) => {\n if ((error as NodeJS.ErrnoException | null)?.code !== \"ENOTFOUND\" || !/^[a-z0-9-]+\\.trycloudflare\\.com$/i.test(hostname)) {\n callback(error, address, family); return;\n }\n const resolver = new Resolver({ timeout: 3000, tries: 1 });\n void Promise.all([\n options.family === 6 ? [] : resolver.resolve4(hostname).then(addresses => addresses.map(address => ({ address, family: 4 })), () => []),\n options.family === 4 ? [] : resolver.resolve6(hostname).then(addresses => addresses.map(address => ({ address, family: 6 })), () => []),\n ]).then(results => {\n const addresses = results.flat();\n if (!addresses.length) { callback(error, address, family); return; }\n if (options.all) callback(null, addresses);\n else callback(null, addresses[0]!.address, addresses[0]!.family);\n });\n });\n};\n\n/** Check the actual WebSocket byte path, not just the helper's PID or its logs.\n * The phone still authenticates and pins the SSH host before sending secrets. */\nexport async function checkRelay(route: ComputerRoute, options: { signal?: AbortSignal; timeoutMs?: number } = {}): Promise<void> {\n options.signal?.throwIfAborted();\n if (route.kind !== \"websocket\") throw new Error(\"Expected an internet tunnel address.\");\n await new Promise<void>((resolve, reject) => {\n const socket = new WebSocket(route.url, { maxPayload: 4096, perMessageDeflate: false, followRedirects: false, lookup: relayLookup });\n let finished = false, banner = \"\";\n const finish = (error?: Error) => {\n if (finished) return;\n finished = true;\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", aborted);\n socket.terminate();\n if (error) reject(error); else resolve();\n };\n const aborted = () => finish(options.signal?.reason ?? new Error(\"Connection check cancelled.\"));\n const timer = setTimeout(() => finish(new Error(\"The internet tunnel isn't responding. Reconnecting…\")), options.timeoutMs ?? 8000);\n socket.on(\"error\", error => finish(new Error(\n (error as NodeJS.ErrnoException).code === \"ENOTFOUND\"\n ? \"The internet address isn't available. Checking the tunnel…\"\n : /(?:[Uu]nexpected server response|[Ee]xpected 101)/.test(error.message)\n ? \"The internet tunnel isn't reaching Mindwire. Reconnecting…\"\n : \"The internet tunnel couldn't connect. Retrying…\", { cause: error })));\n socket.on(\"close\", () => finish(new Error(\"The internet tunnel closed before reaching Mindwire. Retrying…\")));\n socket.on(\"message\", (data, binary) => {\n if (!binary) { finish(new Error(\"The internet tunnel returned an invalid SSH response.\")); return; }\n banner += data.toString();\n if (banner.length > 1024) { finish(new Error(\"The internet tunnel returned an invalid SSH response.\")); return; }\n if (banner.includes(\"\\n\")) {\n finish(banner.split(\"\\n\")[0]?.trim() === \"SSH-2.0-Mindwire\" ? undefined\n : new Error(\"The internet tunnel isn't connected to the Mindwire SSH service.\"));\n }\n });\n options.signal?.addEventListener(\"abort\", aborted, { once: true });\n if (options.signal?.aborted) aborted();\n });\n}\n\n/** Don't rotate a quick-tunnel address during an ordinary internet outage.\n * Let the provider reconnect with the same address until it is reachable again. */\nexport async function relayProviderReachable(options: RelayOptions, signal?: AbortSignal): Promise<boolean> {\n if (options.kind !== \"cloudflare\" && options.kind !== \"ngrok\") return false;\n signal?.throwIfAborted();\n return new Promise<boolean>(resolve => {\n const url = options.kind === \"cloudflare\" ? \"https://api.trycloudflare.com/\" : \"https://api.ngrok.com/\";\n let finished = false;\n const done = (reachable: boolean) => {\n if (finished) return;\n finished = true;\n clearTimeout(timer);\n probe.destroy();\n resolve(reachable);\n };\n const probe = request(url, { method: \"HEAD\", signal }, response => {\n response.destroy();\n done(true);\n });\n const timer = setTimeout(() => done(false), 5000);\n probe.on(\"error\", () => done(false));\n probe.end();\n });\n}\n","import { spawn, execFileSync } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport type { Mindwire } from \"../client.js\";\nimport { ensureDaemonBinary } from \"../daemon-binary.js\";\nimport { SDK_VERSION } from \"../version.js\";\nimport type { ComputerInfo, ComputerRoute, ComputerUpdate } from \"../computer.js\";\nimport { computerClient, directRoutes, readJSON, writeJSON, CONTROLLER_PROTOCOL, type ComputerConfig, type ControllerState } from \"./lifecycle.js\";\nimport { startRelay, type RelayHandle } from \"./relay.js\";\nimport { checkRelay, relayProviderReachable } from \"./relay-health.js\";\nimport { adoptProcess, ownChild, currentProcessIdentity, processStateAlive, processIdentity, type OwnedProcess, type ProcessIdentity } from \"./process.js\";\n\ninterface SavedRelay { owner: ProcessIdentity; port: number; options: string; route?: ComputerRoute }\nexport function recoveryDelay(failures: number): number { return Math.min(60_000, 1000 * 2 ** Math.min(6, Math.max(0, failures - 1))); }\n\n/** Own process lifecycle separately from any mobile view or pairing command. */\nexport async function superviseComputer(directory: string): Promise<void> {\n if ((await readJSON<{ stopped: boolean }>(path.join(directory, \"computer-stopped.json\")))?.stopped) return;\n let config = await readJSON<ComputerConfig>(path.join(directory, \"computer-config.json\"));\n if (!config) throw new Error(\"Computer configuration is missing.\");\n const controllerPath = path.join(directory, \"computer-controller.json\");\n const relayPath = path.join(directory, \"computer-relay.json\");\n const previous = await readJSON<ControllerState>(controllerPath);\n if (await processStateAlive(previous)) throw new Error(\"The computer already has a service controller.\");\n const owner = await currentProcessIdentity();\n const phase = (message: string) => writeJSON(controllerPath, { ...owner, protocol: CONTROLLER_PROTOCOL, cliVersion: SDK_VERSION, ready: false, phase: message, recovering: true });\n await phase(\"Preparing Mindwire…\");\n let child: OwnedProcess | undefined, relay: RelayHandle | undefined, stopping = false;\n let relayTask: Promise<void> | undefined, updateTask: Promise<void> | undefined;\n let relayResult: { handle?: RelayHandle; error?: unknown } | undefined;\n let checkTask: Promise<void> | undefined;\n let checkResult: { relay: RelayHandle; error?: unknown; canRestart?: boolean } | undefined;\n const abort = new AbortController();\n const stop = () => { stopping = true; abort.abort(); };\n process.on(\"SIGTERM\", stop); process.on(\"SIGINT\", stop);\n\n let token: string;\n try { token = (await fs.readFile(path.join(directory, \"daemon.token\"), \"utf8\")).trim(); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n token = randomBytes(32).toString(\"hex\");\n await fs.writeFile(path.join(directory, \"daemon.token\"), token, { mode: 0o600, flag: \"wx\" });\n }\n const daemonEnv = { ...process.env };\n if (process.platform === \"win32\") {\n try {\n const git = execFileSync(\"where.exe\", [\"git\"], { encoding: \"utf8\", timeout: 3000, windowsHide: true }).split(/\\r?\\n/)[0];\n if (git) {\n const shellDirectory = path.resolve(path.dirname(git), \"..\", \"bin\");\n await fs.access(path.join(shellDirectory, \"bash.exe\"));\n const pathKey = Object.keys(daemonEnv).find(key => key.toLowerCase() === \"path\") ?? \"PATH\";\n daemonEnv[pathKey] = `${shellDirectory}${path.delimiter}${daemonEnv[pathKey] ?? \"\"}`;\n }\n } catch { /* Git/Bash are checked only when their capabilities are used. */ }\n }\n const oldRuntime = await readJSON<ComputerInfo>(path.join(directory, \"computer-runtime.json\"));\n // An automatically selected port remains stable across controller/release restarts.\n let sshPort = config.sshPort || oldRuntime?.sshPort || 0;\n let websocketPort = config.websocketPort || oldRuntime?.websocketPort || 0;\n const launch = async (binary: string): Promise<Mindwire> => {\n await fs.rm(path.join(directory, \"computer-runtime.json\"), { force: true });\n const process = spawn(binary, [\"--computer\"], {\n cwd: config!.directory, stdio: [\"ignore\", \"inherit\", \"inherit\"], windowsHide: true,\n env: { ...daemonEnv, DAEMON_TOKEN: token, ADDR: \"127.0.0.1:0\", STATE_PATH: path.join(directory, \"agent-state.json\"),\n WORKSPACE_DB_PATH: path.join(directory, \"workspace.db\"), AGENT_CWD: config!.directory,\n COMPUTER_SSH_ADDR: `${config!.bind.includes(\":\") ? `[${config!.bind}]` : config!.bind}:${sshPort}`,\n COMPUTER_WS_ADDR: `127.0.0.1:${websocketPort}`, MINDWIRE_COMPUTER_MANAGED: \"1\" },\n });\n let failure: Error | undefined;\n process.on(\"error\", error => { failure = error; });\n await new Promise<void>((resolve, reject) => { process.once(\"spawn\", resolve); process.once(\"error\", reject); });\n child = await ownChild(process);\n for (let attempt = 0; attempt < 100; attempt++) {\n if (stopping) throw new Error(\"Mindwire is stopping.\");\n if (failure) throw failure;\n if (!child.alive()) throw new Error(\"Mindwire exited during startup. Check computer.log and the installed release.\");\n try {\n const client = await computerClient(directory);\n const info = await client.computer.info();\n if (info.pid !== child.pid) throw new Error(\"Another service owns this computer's state.\");\n const health = await client.health();\n if ((health as { computerConnectionVersion?: number }).computerConnectionVersion !== 1) throw new Error(\"Update Mindwire to a release that supports computer connections.\");\n sshPort = info.sshPort; websocketPort = info.websocketPort;\n return client;\n } catch (error) { if (attempt === 99) throw error; }\n await delay(100);\n }\n throw new Error(\"Mindwire startup timed out.\");\n };\n\n let routes: ComputerRoute[] = [], published = \"\";\n let client: Mindwire | undefined;\n let relayMessage = \"Preparing your internet connection…\", relayError: string | undefined;\n let daemonError: string | undefined, nextDaemonAt = 0, daemonFailures = 0;\n let nextRelayAt = 0, relayFailures = 0, changingDaemon = false;\n let daemonReadyAt = Date.now(), relayReadyAt = Date.now();\n let relayReachable = false, relayWasReachable = false, relayHealthFailures = 0;\n let relayFailedAt = 0;\n let nextCheckAt = 0, checkedAt = 0;\n try {\n await phase(\"Checking the Mindwire service…\");\n let binary = config.daemonBin ?? await ensureDaemonBinary({ version: config.version ?? SDK_VERSION, cacheDir: process.env.MINDWIRE_RELEASE_CACHE_DIR });\n // A killed controller must not restart a healthy daemon or lose its PTYs.\n try { client = await computerClient(directory); } catch { /* Cold start. */ }\n if (client) {\n const info = await client.computer.info();\n const identity = await processIdentity(info.pid);\n child = identity ? await adoptProcess(identity) : undefined;\n if (!child) throw new Error(\"Couldn't verify the existing Mindwire process. Leave it running and retry.\");\n sshPort = info.sshPort; websocketPort = info.websocketPort;\n } else {\n await phase(\"Starting the Mindwire service…\");\n client = await launch(binary);\n }\n\n const saved = await readJSON<SavedRelay>(relayPath);\n if (saved) {\n const owner = await adoptProcess(saved.owner);\n if (owner) {\n if (saved.route && saved.port === websocketPort && saved.options === JSON.stringify(config.relay)) {\n relay = { route: saved.route, owner, close: () => owner.close() };\n } else { await owner.close(); }\n }\n }\n\n let lastController = \"\";\n while (!stopping) {\n if (!changingDaemon && child && !child.alive()) {\n child = undefined; client = undefined;\n daemonError = \"The service stopped. Restarting it…\";\n nextDaemonAt = Date.now() + recoveryDelay(++daemonFailures);\n }\n if (child?.alive() && Date.now() - daemonReadyAt > 60_000) daemonFailures = 0;\n if (relayReachable && relay?.owner?.alive() && Date.now() - relayReadyAt > 60_000) relayFailures = 0;\n if (!changingDaemon && !child?.alive() && Date.now() >= nextDaemonAt) {\n daemonError = \"The service stopped. Restarting it…\";\n await phase(daemonError);\n try {\n client = await launch(binary); published = \"\"; daemonError = undefined; daemonReadyAt = Date.now();\n } catch (error) {\n if (child) await child.close();\n child = undefined; client = undefined;\n daemonError = error instanceof Error ? error.message : \"Service restart failed.\";\n nextDaemonAt = Date.now() + recoveryDelay(++daemonFailures);\n }\n }\n\n if (relay?.owner && !relay.owner.alive()) {\n relay = undefined;\n relayReachable = false; relayWasReachable = false;\n relayError = \"The internet tunnel disconnected. Reconnecting…\";\n nextRelayAt = Date.now() + recoveryDelay(++relayFailures);\n }\n if (relayResult) {\n const result = relayResult; relayResult = undefined; relayTask = undefined;\n if (result.handle) {\n relay = result.handle; relayError = undefined; relayReadyAt = Date.now();\n relayReachable = false; relayWasReachable = false; relayHealthFailures = 0; relayFailedAt = 0; nextCheckAt = 0;\n if (relay.owner?.identity) await writeJSON(relayPath, {\n owner: relay.owner.identity, port: websocketPort, options: JSON.stringify(config.relay), route: relay.route,\n } satisfies SavedRelay);\n } else {\n relayError = result.error instanceof Error ? result.error.message : \"The internet tunnel couldn't connect.\";\n nextRelayAt = Date.now() + recoveryDelay(++relayFailures);\n }\n }\n if (!relay && !relayTask && Date.now() >= nextRelayAt) {\n relayMessage = \"Reconnecting your internet tunnel…\";\n relayTask = startRelay(config.relay, websocketPort, {\n cacheDir: path.join(directory, \"tools\"), signal: abort.signal,\n onProgress: message => { relayMessage = message; },\n onSpawn: async owner => {\n if (owner.identity) await writeJSON(relayPath, {\n owner: owner.identity, port: websocketPort, options: JSON.stringify(config!.relay),\n } satisfies SavedRelay);\n },\n }).then(handle => { relayResult = { handle }; }, error => { relayResult = { error }; });\n }\n\n if (checkResult) {\n const result = checkResult; checkResult = undefined; checkTask = undefined;\n // A late failure from a replaced helper cannot invalidate its successor.\n if (result.relay === relay) {\n checkedAt = Date.now();\n relayReachable = !result.error;\n if (!result.error) {\n relayWasReachable = true; relayHealthFailures = 0; relayFailedAt = 0; relayError = undefined;\n nextCheckAt = checkedAt + 30_000;\n } else {\n relayError = result.error instanceof Error ? result.error.message : \"The internet tunnel couldn't connect. Retrying…\";\n relayFailedAt ||= checkedAt;\n relayHealthFailures++;\n nextCheckAt = checkedAt + Math.min(15_000, recoveryDelay(relayHealthFailures));\n if (result.canRestart) {\n // Only replace the tunnel after repeated failures with working\n // internet. The daemon, phone keys, chats and PTYs keep running.\n await relay!.close(); relay = undefined;\n relayWasReachable = false; nextRelayAt = Date.now() + recoveryDelay(++relayFailures);\n await fs.rm(relayPath, { force: true });\n }\n }\n }\n }\n const requestedCheck = await readJSON<{ requestedAt: number }>(path.join(directory, \"computer-check.json\"));\n if (config.relay.kind === \"none\") checkedAt = Math.max(checkedAt, requestedCheck?.requestedAt ?? 0);\n if (relay?.route && !checkTask && (Date.now() >= nextCheckAt || (requestedCheck?.requestedAt ?? 0) > checkedAt)) {\n const checking = relay;\n checkTask = (async () => {\n try {\n await checkRelay(checking.route!, { signal: abort.signal });\n checkResult = { relay: checking };\n } catch (error) {\n // Quick-tunnel DNS can initially return NXDOMAIN with a 60-second\n // negative TTL even after its connection registered. Replacing the\n // helper sooner would keep allocating fresh, negatively cached names.\n const canRestart = !!checking.owner && relayHealthFailures >= 2 && relayFailedAt > 0 && Date.now() - relayFailedAt >= 90_000\n && await relayProviderReachable(config!.relay, abort.signal).catch(() => false);\n checkResult = { relay: checking, error, canRestart };\n }\n })();\n }\n\n routes = directRoutes(config, sshPort);\n if (relay?.route && relayWasReachable) routes.push(relay.route);\n const routeJSON = JSON.stringify(routes);\n if (!changingDaemon && client && child?.alive() && routeJSON !== published) {\n try { await client.computer.setRoutes(routes); published = routeJSON; }\n catch { /* Retry publication; never claim the new routes are ready yet. */ }\n }\n const ready = !!client && !!child?.alive() && !changingDaemon && routes.length > 0 && published === routeJSON\n && (config.relay.kind === \"none\" || relayReachable);\n const state: ControllerState = { ...owner, protocol: CONTROLLER_PROTOCOL, cliVersion: SDK_VERSION, ready, routes, checkedAt,\n phase: ready ? undefined : daemonError ?? relayError ?? relayMessage, error: daemonError ?? relayError, recovering: !ready };\n const serialized = JSON.stringify(state);\n if (serialized !== lastController) { await writeJSON(controllerPath, state); lastController = serialized; }\n\n const updatePath = path.join(directory, \"computer-update.json\");\n const update = !updateTask && client && child?.alive() ? await readJSON<ComputerUpdate>(updatePath) : undefined;\n if (update && [\"queued\", \"downloading\", \"waiting\", \"restarting\"].includes(update.status)) {\n // Waiting for idle does not block relay recovery or address publication.\n updateTask = (async () => {\n const status = async (state: ComputerUpdate[\"status\"], error?: string) => writeJSON(updatePath,\n { ...update, status: state, error, updatedAt: new Date().toISOString() });\n try {\n await status(\"downloading\");\n const replacement = await ensureDaemonBinary({ version: update.version, cacheDir: process.env.MINDWIRE_RELEASE_CACHE_DIR });\n await status(\"waiting\");\n let lease: Awaited<ReturnType<Mindwire[\"service\"][\"acquireUpdate\"]>> | undefined;\n while (!stopping && !lease) {\n try { lease = await client!.service.acquireUpdate(); }\n catch (error) { if ((error as { status?: number }).status !== 409) throw error; await delay(2000); }\n }\n if (stopping || !lease) return;\n if (lease.pid !== child?.pid) { await client!.service.releaseUpdate(lease.id); throw new Error(\"The leased service process changed.\"); }\n changingDaemon = true;\n await status(\"restarting\");\n await child!.close();\n try {\n client = await launch(replacement);\n const health = await client.health();\n if (health.version.replace(/^v/, \"\") !== update.version) throw new Error(\"The replacement service reports a different release version.\");\n } catch (error) {\n if (child) await child.close();\n if (!stopping) client = await launch(binary);\n throw error;\n }\n binary = replacement;\n config = { ...config!, daemonBin: undefined, version: update.version };\n await writeJSON(path.join(directory, \"computer-config.json\"), config);\n await client.computer.setRoutes(routes); published = JSON.stringify(routes);\n await status(\"complete\");\n } catch (error) { await status(\"failed\", error instanceof Error ? error.message : \"Service update failed.\"); }\n finally { changingDaemon = false; published = \"\"; }\n })().finally(() => { updateTask = undefined; });\n }\n await delay(500);\n }\n } catch (error) {\n await writeJSON(controllerPath, { ...owner, ready: false, error: error instanceof Error ? error.message : \"Computer service failed.\" });\n throw error;\n } finally {\n abort.abort();\n await relayTask; await checkTask; await updateTask;\n if (relayResult?.handle && relayResult.handle !== relay) await relayResult.handle.close();\n await relay?.close();\n if (child) await child.close();\n process.off(\"SIGTERM\", stop); process.off(\"SIGINT\", stop);\n if (stopping) await writeJSON(controllerPath, { pid: 0, ready: false });\n }\n}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readJSON, processIdentity, writeJSON, processStateAlive } from './chunk-RN7ERVJ2.js';
|
|
2
|
+
import './chunk-T6TD76UA.js';
|
|
3
|
+
import { execFile } from 'child_process';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { setTimeout } from 'timers/promises';
|
|
7
|
+
|
|
8
|
+
var execute = promisify(execFile);
|
|
9
|
+
async function controllerProcess(pid, directory) {
|
|
10
|
+
const identity = await processIdentity(pid);
|
|
11
|
+
if (!identity) throw new Error("The computer controller changed. Retry the connection.");
|
|
12
|
+
let command, children;
|
|
13
|
+
if (process.platform === "win32") {
|
|
14
|
+
const result = await execute(
|
|
15
|
+
"powershell.exe",
|
|
16
|
+
[
|
|
17
|
+
"-NoProfile",
|
|
18
|
+
"-NonInteractive",
|
|
19
|
+
"-Command",
|
|
20
|
+
`$p=Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}'; $c=@(Get-CimInstance Win32_Process -Filter 'ParentProcessId = ${pid}'); @{command=$p.CommandLine; children=@($c | ForEach-Object { @{pid=$_.ProcessId; name=$_.Name} })} | ConvertTo-Json -Depth 4 -Compress`
|
|
21
|
+
],
|
|
22
|
+
{ timeout: 5e3, windowsHide: true }
|
|
23
|
+
);
|
|
24
|
+
const value = JSON.parse(result.stdout);
|
|
25
|
+
command = String(value.command ?? "").replaceAll('"', "");
|
|
26
|
+
children = value.children;
|
|
27
|
+
} else {
|
|
28
|
+
const [args, processes] = await Promise.all([
|
|
29
|
+
execute("/bin/ps", ["-p", String(pid), "-o", "args="], { timeout: 3e3 }),
|
|
30
|
+
execute("/bin/ps", ["-axo", "pid=,ppid=,comm="], { timeout: 3e3 })
|
|
31
|
+
]);
|
|
32
|
+
command = args.stdout.trim();
|
|
33
|
+
children = processes.stdout.split("\n").flatMap((line) => {
|
|
34
|
+
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
35
|
+
return match && Number(match[2]) === pid ? [{ pid: Number(match[1]), name: path.basename(match[3]) }] : [];
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
if (!command.endsWith(`dist${path.sep}cli.js _serve --state-dir ${directory}`) || !/(?:^|[\/\\])(?:node|bun)(?:\.exe)?\s/.test(command)) {
|
|
39
|
+
throw new Error("Couldn't verify the old Mindwire controller. Restart Mindwire on the computer before connecting.");
|
|
40
|
+
}
|
|
41
|
+
if ((await processIdentity(pid))?.start !== identity.start) throw new Error("The computer controller changed. Retry the connection.");
|
|
42
|
+
return { identity, children };
|
|
43
|
+
}
|
|
44
|
+
async function handoffController(directory, state, config, info) {
|
|
45
|
+
const { identity, children } = await controllerProcess(state.pid, directory);
|
|
46
|
+
if (state.start && state.start !== identity.start) throw new Error("The computer controller changed. Retry the connection.");
|
|
47
|
+
const relayFile = path.join(directory, "computer-relay.json");
|
|
48
|
+
if (!await readJSON(relayFile)) {
|
|
49
|
+
const executable = config.relay.kind === "cloudflare" ? "cloudflared" : config.relay.kind === "ngrok" ? "ngrok" : void 0;
|
|
50
|
+
const candidates = executable ? children.filter((child) => child.name === executable || child.name === executable + ".exe") : [];
|
|
51
|
+
const route = info.routes.find((route2) => route2.kind === "websocket");
|
|
52
|
+
const relayOwner = candidates.length === 1 ? await processIdentity(candidates[0].pid) : void 0;
|
|
53
|
+
if (route && relayOwner) await writeJSON(relayFile, {
|
|
54
|
+
owner: relayOwner,
|
|
55
|
+
port: info.websocketPort,
|
|
56
|
+
options: JSON.stringify(config.relay),
|
|
57
|
+
route
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!await processStateAlive(identity)) throw new Error("The computer controller changed. Retry the connection.");
|
|
61
|
+
process.kill(identity.pid, "SIGKILL");
|
|
62
|
+
for (let attempt = 0; attempt < 40; attempt++) {
|
|
63
|
+
if (!await processStateAlive(identity)) return;
|
|
64
|
+
await setTimeout(50);
|
|
65
|
+
}
|
|
66
|
+
throw new Error("The old Mindwire controller is still exiting. Retry the connection.");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { handoffController };
|
|
70
|
+
//# sourceMappingURL=controller-handoff-4GXF4ATR.js.map
|
|
71
|
+
//# sourceMappingURL=controller-handoff-4GXF4ATR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/computer/controller-handoff.ts"],"names":["route","delay"],"mappings":"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","file":"controller-handoff-4GXF4ATR.js","sourcesContent":["import { execFile } from \"node:child_process\";\nimport * as path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport type { ComputerInfo } from \"../computer.js\";\nimport { readJSON, writeJSON, type ComputerConfig, type ControllerState } from \"./lifecycle.js\";\nimport { processIdentity, processStateAlive, type ProcessIdentity } from \"./process.js\";\n\nconst execute = promisify(execFile);\n\nasync function controllerProcess(pid: number, directory: string): Promise<{ identity: ProcessIdentity; children: { pid: number; name: string }[] }> {\n const identity = await processIdentity(pid);\n if (!identity) throw new Error(\"The computer controller changed. Retry the connection.\");\n let command: string, children: { pid: number; name: string }[];\n if (process.platform === \"win32\") {\n const result = await execute(\"powershell.exe\", [\"-NoProfile\", \"-NonInteractive\", \"-Command\",\n `$p=Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}'; $c=@(Get-CimInstance Win32_Process -Filter 'ParentProcessId = ${pid}'); @{command=$p.CommandLine; children=@($c | ForEach-Object { @{pid=$_.ProcessId; name=$_.Name} })} | ConvertTo-Json -Depth 4 -Compress`],\n { timeout: 5000, windowsHide: true });\n const value = JSON.parse(result.stdout);\n command = String(value.command ?? \"\").replaceAll('\"', \"\");\n children = value.children;\n } else {\n const [args, processes] = await Promise.all([\n execute(\"/bin/ps\", [\"-p\", String(pid), \"-o\", \"args=\"], { timeout: 3000 }),\n execute(\"/bin/ps\", [\"-axo\", \"pid=,ppid=,comm=\"], { timeout: 3000 }),\n ]);\n command = args.stdout.trim();\n children = processes.stdout.split(\"\\n\").flatMap(line => {\n const match = line.trim().match(/^(\\d+)\\s+(\\d+)\\s+(.+)$/);\n return match && Number(match[2]) === pid ? [{ pid: Number(match[1]), name: path.basename(match[3]!) }] : [];\n });\n }\n // Old releases recorded only a PID. Verify the actual controller command before\n // signalling it; a reused PID must never affect an unrelated process.\n if (!command.endsWith(`dist${path.sep}cli.js _serve --state-dir ${directory}`)\n || !/(?:^|[\\/\\\\])(?:node|bun)(?:\\.exe)?\\s/.test(command)) {\n throw new Error(\"Couldn't verify the old Mindwire controller. Restart Mindwire on the computer before connecting.\");\n }\n if ((await processIdentity(pid))?.start !== identity.start) throw new Error(\"The computer controller changed. Retry the connection.\");\n return { identity, children };\n}\n\n/** Hand off an old CLI controller without stopping the daemon or its terminals.\n * Its graceful shutdown deliberately closes both children, so only the verified\n * supervisor is terminated; the new controller adopts the existing children. */\nexport async function handoffController(directory: string, state: ControllerState, config: ComputerConfig, info: ComputerInfo): Promise<void> {\n const { identity, children } = await controllerProcess(state.pid, directory);\n if (state.start && state.start !== identity.start) throw new Error(\"The computer controller changed. Retry the connection.\");\n const relayFile = path.join(directory, \"computer-relay.json\");\n if (!await readJSON(relayFile)) {\n // Releases before process adoption didn't record the provider child. Capture\n // just that controller's unique helper before it becomes an orphan.\n const executable = config.relay.kind === \"cloudflare\" ? \"cloudflared\" : config.relay.kind === \"ngrok\" ? \"ngrok\" : undefined;\n const candidates = executable ? children.filter(child => child.name === executable || child.name === executable + \".exe\") : [];\n const route = info.routes.find(route => route.kind === \"websocket\");\n const relayOwner = candidates.length === 1 ? await processIdentity(candidates[0]!.pid) : undefined;\n if (route && relayOwner) await writeJSON(relayFile, {\n owner: relayOwner, port: info.websocketPort, options: JSON.stringify(config.relay), route,\n });\n }\n if (!await processStateAlive(identity)) throw new Error(\"The computer controller changed. Retry the connection.\");\n process.kill(identity.pid, \"SIGKILL\");\n for (let attempt = 0; attempt < 40; attempt++) {\n if (!await processStateAlive(identity)) return;\n await delay(50);\n }\n throw new Error(\"The old Mindwire controller is still exiting. Retry the connection.\");\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -916,11 +916,19 @@ var ComputerApi = class {
|
|
|
916
916
|
pairing(id) {
|
|
917
917
|
return this.client.http.request("GET", `/computer/pairings/${encodeURIComponent(id)}`);
|
|
918
918
|
}
|
|
919
|
+
/** pairingVersion >= 2. Releases an unused QR when a saved phone reconnects.
|
|
920
|
+
* Returns false if a phone already submitted a request, which must be handled. */
|
|
921
|
+
closeUnusedPairing(id) {
|
|
922
|
+
return this.client.http.request("DELETE", `/computer/pairings/${encodeURIComponent(id)}`);
|
|
923
|
+
}
|
|
919
924
|
completePairing(id, requestId) {
|
|
920
925
|
return this.client.http.request("POST", `/computer/pairings/${encodeURIComponent(id)}/complete`, { body: { requestId } });
|
|
921
926
|
}
|
|
922
|
-
decide(id, requestId, approve) {
|
|
923
|
-
|
|
927
|
+
async decide(id, requestId, approve, options = {}) {
|
|
928
|
+
if (options.replaceDeviceIds?.length && ((await this.info()).pairingVersion ?? 0) < 2) {
|
|
929
|
+
throw new Error("Update the Mindwire service before replacing saved phone approvals.");
|
|
930
|
+
}
|
|
931
|
+
return this.client.http.request("POST", `/computer/pairings/${encodeURIComponent(id)}/decision`, { body: { requestId, approve, ...options } });
|
|
924
932
|
}
|
|
925
933
|
devices() {
|
|
926
934
|
return this.client.http.request("GET", "/computer/devices");
|
|
@@ -959,7 +967,16 @@ init_errors();
|
|
|
959
967
|
init_errors();
|
|
960
968
|
|
|
961
969
|
// src/version.ts
|
|
962
|
-
var SDK_VERSION = "0.1.
|
|
970
|
+
var SDK_VERSION = "0.1.30" ;
|
|
971
|
+
function versionAtLeast(actual, desired) {
|
|
972
|
+
if (actual === desired) return true;
|
|
973
|
+
if (!actual || !/^\d+\.\d+\.\d+$/.test(actual) || !/^\d+\.\d+\.\d+$/.test(desired)) return false;
|
|
974
|
+
const a = actual.split(".").map(Number), b = desired.split(".").map(Number);
|
|
975
|
+
for (let i = 0; i < 3; i++) {
|
|
976
|
+
if (a[i] !== b[i]) return a[i] > b[i];
|
|
977
|
+
}
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
963
980
|
|
|
964
981
|
// src/daemon-binary.ts
|
|
965
982
|
function supported(platform, arch) {
|
|
@@ -1833,15 +1850,6 @@ init_errors();
|
|
|
1833
1850
|
|
|
1834
1851
|
// src/target/host.ts
|
|
1835
1852
|
init_errors();
|
|
1836
|
-
function versionAtLeast(actual, desired) {
|
|
1837
|
-
if (actual === desired) return true;
|
|
1838
|
-
if (!actual || !/^\d+\.\d+\.\d+$/.test(actual) || !/^\d+\.\d+\.\d+$/.test(desired)) return false;
|
|
1839
|
-
const a = actual.split(".").map(Number), b = desired.split(".").map(Number);
|
|
1840
|
-
for (let i = 0; i < 3; i++) {
|
|
1841
|
-
if (a[i] !== b[i]) return a[i] > b[i];
|
|
1842
|
-
}
|
|
1843
|
-
return true;
|
|
1844
|
-
}
|
|
1845
1853
|
var versionCheckScript = String.raw`version_at_least() {
|
|
1846
1854
|
[ "$1" != "$2" ] || return 0
|
|
1847
1855
|
[[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "$2" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
|