mindwire 0.1.26 → 0.1.28
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-L3USUTZZ.js → chunk-5WYSGXFN.js} +38 -9
- package/dist/chunk-5WYSGXFN.js.map +1 -0
- package/dist/cli.js +163 -32
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +11 -1
- package/dist/computer/connection-flow.d.ts +22 -0
- package/dist/computer/pairing-display.d.ts +3 -3
- package/dist/computer.d.ts +23 -3
- package/dist/{controller-CGSJZZZD.js → controller-5ZTNIHUV.js} +3 -3
- package/dist/{controller-CGSJZZZD.js.map → controller-5ZTNIHUV.js.map} +1 -1
- package/dist/index.cjs +35 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +35 -6
- package/dist/index.js.map +1 -1
- package/dist/service.d.ts +5 -1
- package/dist/types.d.ts +12 -0
- package/package.json +1 -1
- package/dist/chunk-L3USUTZZ.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { defaultStateDirectory, superviseComputer, ensureComputer, computerPairingURI, stopComputer, computerClient, SDK_VERSION, readJSON, writeJSON, currentProcessIdentity, processStateAlive, acquireProcessLock, computerPairingQRData } from './chunk-
|
|
2
|
+
import { defaultStateDirectory, superviseComputer, ensureComputer, computerPairingURI, stopComputer, computerClient, SDK_VERSION, readJSON, writeJSON, currentProcessIdentity, processStateAlive, acquireProcessLock, computerPairingQRData } from './chunk-5WYSGXFN.js';
|
|
3
3
|
import './chunk-T6TD76UA.js';
|
|
4
4
|
import { promisify, parseArgs } from 'util';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
@@ -7,16 +7,18 @@ import { realpathSync, mkdtempSync, chmodSync, writeFileSync, rmSync } from 'fs'
|
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import { createHash } from 'crypto';
|
|
10
|
+
import { hostname, homedir, tmpdir } from 'os';
|
|
10
11
|
import { createInterface } from 'readline/promises';
|
|
11
12
|
import { setTimeout as setTimeout$1 } from 'timers/promises';
|
|
12
13
|
import { execFile, spawn } from 'child_process';
|
|
13
|
-
import { homedir, tmpdir } from 'os';
|
|
14
14
|
import * as fs from 'fs/promises';
|
|
15
15
|
|
|
16
16
|
var enterScreen = "\x1B[?1049h\x1B[?25l";
|
|
17
17
|
var leaveScreen = "\x1B[0m\x1B[?25h\x1B[?1049l";
|
|
18
18
|
var clearScreen = "\x1B[H\x1B[2J";
|
|
19
|
-
|
|
19
|
+
function scanInstruction(invitation) {
|
|
20
|
+
return "mode" in invitation && invitation.mode === "reconnect" ? "Workspaces \u2192 your computer \u2192 Reconnect Computer \u2192 Scan Connection Code" : "Mindwire \u2192 Add computer \u2192 Scan QR code";
|
|
21
|
+
}
|
|
20
22
|
async function createPairingQR(invitation) {
|
|
21
23
|
const QR = await import('qrcode');
|
|
22
24
|
const data = computerPairingQRData(invitation);
|
|
@@ -34,16 +36,18 @@ async function createPairingQR(invitation) {
|
|
|
34
36
|
lines.push("\x1B[30;107m" + line + "\x1B[0m");
|
|
35
37
|
}
|
|
36
38
|
const svg = await QR.toString(data, { ...options, type: "svg" });
|
|
37
|
-
return { lines, columns: Math.max(width, scanInstruction.length) + 1, rows: lines.length + 5, svg };
|
|
39
|
+
return { lines, columns: Math.max(width, scanInstruction(invitation).length) + 1, rows: lines.length + 5, svg };
|
|
38
40
|
}
|
|
39
41
|
function escapeHTML(value) {
|
|
40
42
|
return value.replace(/[&<>"']/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ch]);
|
|
41
43
|
}
|
|
42
44
|
function browserPage(invitation, svg) {
|
|
45
|
+
const reconnect = "mode" in invitation && invitation.mode === "reconnect";
|
|
46
|
+
const title = reconnect ? "Reconnect your phone" : "Connect your phone";
|
|
43
47
|
return `<!doctype html>
|
|
44
48
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
45
49
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; base-uri 'none'; form-action 'none'">
|
|
46
|
-
<title
|
|
50
|
+
<title>${title} \xB7 Mindwire</title>
|
|
47
51
|
<style>
|
|
48
52
|
*{box-sizing:border-box}body{margin:0;background:#141416;color:#f5f5f7;font:16px/1.5 system-ui,sans-serif}
|
|
49
53
|
main{width:min(100% - 40px,520px);margin:32px auto;text-align:center}small{color:#a6a6af;letter-spacing:.16em}
|
|
@@ -55,18 +59,18 @@ button:disabled{opacity:.45;cursor:default}button:focus-visible{outline:3px soli
|
|
|
55
59
|
textarea{width:100%;margin-top:12px;padding:8px;color:inherit;background:#252528;border:1px solid #45454b;border-radius:8px}
|
|
56
60
|
[hidden]{display:none!important}#expiry{font-size:14px}footer{font-size:13px;color:#93939d;margin:18px 0}
|
|
57
61
|
</style></head><body><main>
|
|
58
|
-
<small>MINDWIRE</small><h1
|
|
59
|
-
<div class="code" id="code" role="img" aria-label="Mindwire
|
|
62
|
+
<small>MINDWIRE</small><h1>${title}</h1><p>${escapeHTML(invitation.name)}</p>
|
|
63
|
+
<div class="code" id="code" role="img" aria-label="Mindwire connection QR code">${svg}</div>
|
|
60
64
|
<p id="expiry" data-expires="${escapeHTML(invitation.expiresAt)}" aria-live="off"></p>
|
|
61
|
-
<div class="steps"><p>In Mindwire, open <strong>Add computer</strong> and scan
|
|
62
|
-
<p
|
|
63
|
-
<button type="button" id="copy">Copy pairing link</button>
|
|
65
|
+
<div class="steps"><p>${reconnect ? "In Mindwire, open your computer in <strong>Workspaces</strong>, then <strong>Reconnect Computer \u2192 Scan Connection Code</strong>." : "In Mindwire, open <strong>Add computer</strong> and scan."}</p>
|
|
66
|
+
<p>${reconnect ? "Your saved pairing and workspace stay the same. No new approval is needed." : "Then return to the terminal to approve your phone."}</p></div>
|
|
67
|
+
<button type="button" id="copy">Copy ${reconnect ? "reconnect" : "pairing"} link</button>
|
|
64
68
|
<textarea id="link" readonly hidden aria-label="Pairing link">${escapeHTML(computerPairingURI(invitation))}</textarea>
|
|
65
|
-
<footer>You can close this page after pairing.</footer>
|
|
69
|
+
<footer>You can close this page after ${reconnect ? "reconnecting" : "pairing"}.</footer>
|
|
66
70
|
</main><script>
|
|
67
71
|
const expiry=document.getElementById('expiry'),code=document.getElementById('code'),copy=document.getElementById('copy'),link=document.getElementById('link');
|
|
68
72
|
function tick(){const seconds=Math.max(0,Math.ceil((Date.parse(expiry.dataset.expires)-Date.now())/1000));
|
|
69
|
-
if(!seconds){code.remove();copy.disabled=true;link.value='';link.hidden=true;expiry.textContent='Code expired. Run mindwire connect for a new code.';return;}
|
|
73
|
+
if(!seconds){code.remove();copy.disabled=true;link.value='';link.hidden=true;expiry.textContent='Code expired. Run mindwire ${reconnect ? "reconnect" : "connect pair"} for a new code.';return;}
|
|
70
74
|
expiry.textContent='Expires in '+Math.floor(seconds/60)+':'+String(seconds%60).padStart(2,'0');}
|
|
71
75
|
tick();setInterval(tick,1000);
|
|
72
76
|
copy.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(link.value);copy.textContent='Copied';}
|
|
@@ -90,16 +94,22 @@ async function openBrowser(url) {
|
|
|
90
94
|
});
|
|
91
95
|
}
|
|
92
96
|
async function showPairingInvitation(invitation, options = {}) {
|
|
97
|
+
const reconnect = "mode" in invitation && invitation.mode === "reconnect";
|
|
98
|
+
const instruction = scanInstruction(invitation);
|
|
99
|
+
const nextStep = reconnect ? "Scan with a saved phone, then press Enter here." : "Return here to approve your phone.";
|
|
100
|
+
const waiting = reconnect ? "Your saved pairing stays the same." : "Waiting for your phone\u2026";
|
|
101
|
+
const command = reconnect ? "mindwire reconnect" : "mindwire connect pair";
|
|
93
102
|
const output = options.output ?? process.stdout;
|
|
94
103
|
const environment = options.environment ?? process.env;
|
|
95
104
|
const mode = options.mode ?? "auto";
|
|
96
105
|
const interactive = !!output.isTTY && environment.TERM !== "dumb";
|
|
97
106
|
const linkOnly = mode === "none" || !interactive && mode !== "browser";
|
|
98
107
|
if (linkOnly) {
|
|
99
|
-
|
|
108
|
+
const destination = reconnect ? "Workspaces \u2192 your computer \u2192 Reconnect Computer" : "Add computer";
|
|
109
|
+
output.write(`Paste this link in Mindwire \u2192 ${destination} (expires in 5 minutes):
|
|
100
110
|
${computerPairingURI(invitation)}
|
|
101
111
|
|
|
102
|
-
|
|
112
|
+
${waiting}
|
|
103
113
|
`);
|
|
104
114
|
return { close() {
|
|
105
115
|
} };
|
|
@@ -115,10 +125,10 @@ Waiting for your phone\u2026
|
|
|
115
125
|
if (closed || !useScreen) return;
|
|
116
126
|
let lines;
|
|
117
127
|
if (fits()) {
|
|
118
|
-
lines = [
|
|
128
|
+
lines = [instruction, "", ...qr.lines, "", waiting];
|
|
119
129
|
} else {
|
|
120
130
|
const size = `Terminal QR needs ${qr.columns} columns \xD7 ${qr.rows} rows.`;
|
|
121
|
-
lines = browser === "opened" ? ["QR opened in your browser.",
|
|
131
|
+
lines = browser === "opened" ? ["QR opened in your browser.", instruction, nextStep] : browser === "opening" ? ["Opening a resizable QR in your browser\u2026", nextStep] : browser === "failed" ? ["Couldn't open a browser here.", size, "Enlarge this terminal, or run:", `${command} --no-qr`] : [size, "Enlarge this terminal, or use --qr browser."];
|
|
122
132
|
lines = lines.map((line) => line.slice(0, Math.max(0, (output.columns ?? 80) - 1))).slice(0, Math.max(0, (output.rows ?? 24) - 1));
|
|
123
133
|
}
|
|
124
134
|
output.write(clearScreen + lines.join("\r\n"));
|
|
@@ -143,12 +153,12 @@ Waiting for your phone\u2026
|
|
|
143
153
|
if (closed) return;
|
|
144
154
|
if (useScreen) draw();
|
|
145
155
|
else output.write(browser === "opened" ? `QR opened in your browser.
|
|
146
|
-
${
|
|
147
|
-
|
|
156
|
+
${instruction}
|
|
157
|
+
${nextStep}
|
|
148
158
|
` : `Open this local QR page in a browser:
|
|
149
|
-
${pageURL ??
|
|
159
|
+
${pageURL ?? `Page unavailable. Run ${command} --no-qr for a link.`}
|
|
150
160
|
|
|
151
|
-
|
|
161
|
+
${waiting}
|
|
152
162
|
`);
|
|
153
163
|
})();
|
|
154
164
|
return browserAttempt;
|
|
@@ -401,11 +411,79 @@ async function watchComputer(directory, cliPath) {
|
|
|
401
411
|
}
|
|
402
412
|
}
|
|
403
413
|
|
|
414
|
+
// src/computer/connection-flow.ts
|
|
415
|
+
function connectionAction(value) {
|
|
416
|
+
if (value === void 0) return void 0;
|
|
417
|
+
if (value === "resume" || value === "reconnect" || value === "pair") return value;
|
|
418
|
+
throw new Error("Use mindwire connect, mindwire connect resume, mindwire reconnect, or mindwire connect pair.");
|
|
419
|
+
}
|
|
420
|
+
async function chooseConnectionAction(options) {
|
|
421
|
+
const paired = options.devices.filter((device) => !device.revoked);
|
|
422
|
+
if (options.requested) {
|
|
423
|
+
if (options.requested !== "pair" && paired.length === 0) {
|
|
424
|
+
throw new Error("No saved phones yet. Run mindwire connect to pair one first.");
|
|
425
|
+
}
|
|
426
|
+
return options.requested;
|
|
427
|
+
}
|
|
428
|
+
if (paired.length === 0) return "pair";
|
|
429
|
+
if (!options.question) return "resume";
|
|
430
|
+
for (; ; ) {
|
|
431
|
+
const answer = (await options.question(
|
|
432
|
+
"\n 1 Resume saved connection\n 2 Refresh address on a saved phone (QR)\n 3 Pair another phone\n\nChoose [1]: "
|
|
433
|
+
)).trim().toLowerCase();
|
|
434
|
+
if (answer === "" || answer === "1" || answer === "resume") return "resume";
|
|
435
|
+
if (answer === "2" || answer === "reconnect") return "reconnect";
|
|
436
|
+
if (answer === "3" || answer === "pair") return "pair";
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function chooseStartupAction(options) {
|
|
440
|
+
if (options.startup && options.noStartup) throw new Error("Choose either --startup or --no-startup.");
|
|
441
|
+
if (options.startup) return !options.state.enabled || !options.state.running ? true : void 0;
|
|
442
|
+
if (options.noStartup) return options.state.kind ? void 0 : false;
|
|
443
|
+
if (options.state.kind) return options.state.enabled && !options.state.running ? true : void 0;
|
|
444
|
+
if (!options.question) return void 0;
|
|
445
|
+
for (; ; ) {
|
|
446
|
+
const answer = (await options.question(
|
|
447
|
+
"\nStart Mindwire automatically after you sign in to this computer? [Y/n] "
|
|
448
|
+
)).trim().toLowerCase();
|
|
449
|
+
if (["", "y", "yes"].includes(answer)) return true;
|
|
450
|
+
if (["n", "no"].includes(answer)) return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function reconnectCode(info, name, now = /* @__PURE__ */ new Date()) {
|
|
454
|
+
if (info.routes.length === 0) throw new Error("No connection address is ready. Run mindwire status and retry once the tunnel is online.");
|
|
455
|
+
return {
|
|
456
|
+
version: 1,
|
|
457
|
+
mode: "reconnect",
|
|
458
|
+
computerId: info.computerId,
|
|
459
|
+
registryId: info.registryId,
|
|
460
|
+
fingerprint: info.fingerprint,
|
|
461
|
+
name,
|
|
462
|
+
routes: info.routes,
|
|
463
|
+
expiresAt: new Date(now.getTime() + 5 * 6e4).toISOString()
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function deviceSummary(devices) {
|
|
467
|
+
const saved = devices.filter((device) => !device.revoked);
|
|
468
|
+
const names = /* @__PURE__ */ new Map();
|
|
469
|
+
const safeName2 = (device) => device.name.replace(/[\x00-\x1f\x7f-\x9f]/g, "");
|
|
470
|
+
for (const device of saved) names.set(safeName2(device), (names.get(safeName2(device)) ?? 0) + 1);
|
|
471
|
+
return saved.sort((a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id)).map((device) => {
|
|
472
|
+
const name = safeName2(device);
|
|
473
|
+
const status = device.connected === true ? "connected" : "paired";
|
|
474
|
+
const identity = (names.get(name) ?? 0) > 1 ? ` \xB7 key ${device.id.slice(0, 8)}` : "";
|
|
475
|
+
return ` ${name} \xB7 ${status}${identity}`;
|
|
476
|
+
}).join("\n");
|
|
477
|
+
}
|
|
478
|
+
|
|
404
479
|
// src/cli.ts
|
|
405
480
|
var help = `Mindwire \u2014 connect this computer to your phone
|
|
406
481
|
|
|
407
482
|
npm install -g mindwire
|
|
408
|
-
mindwire connect
|
|
483
|
+
mindwire connect Resume a saved connection, or pair your first phone
|
|
484
|
+
mindwire connect resume Resume without creating another pairing
|
|
485
|
+
mindwire reconnect Refresh a saved phone's address with a QR code
|
|
486
|
+
mindwire connect pair Pair another phone with this computer
|
|
409
487
|
mindwire connect --relay none --host laptop.tailnet Use your existing VPN
|
|
410
488
|
mindwire connect --relay none Direct SSH only (reachable network required)
|
|
411
489
|
mindwire connect --relay cloudflare Cloudflare quick tunnel
|
|
@@ -431,7 +509,8 @@ Options:
|
|
|
431
509
|
--json Structured output (QR invitation includes a secret)
|
|
432
510
|
--qr auto|terminal|browser Fit the QR to your terminal or open a local page
|
|
433
511
|
--no-qr Print only the pairing link
|
|
434
|
-
--
|
|
512
|
+
--startup Enable automatic startup without a prompt
|
|
513
|
+
--no-startup Skip automatic startup setup
|
|
435
514
|
|
|
436
515
|
The default works across Wi-Fi and mobile networks. Mindwire privately downloads
|
|
437
516
|
a verified Cloudflare helper when needed. Relays carry encrypted SSH.
|
|
@@ -460,6 +539,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
460
539
|
"websocket-port": { type: "string" },
|
|
461
540
|
version: { type: "string" },
|
|
462
541
|
force: { type: "boolean" },
|
|
542
|
+
startup: { type: "boolean" },
|
|
463
543
|
"no-startup": { type: "boolean" }
|
|
464
544
|
} });
|
|
465
545
|
const command = positionals[0] ?? "help";
|
|
@@ -468,6 +548,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
468
548
|
return;
|
|
469
549
|
}
|
|
470
550
|
if (values.qr && !["auto", "terminal", "browser"].includes(values.qr)) throw new Error("Choose --qr auto, terminal or browser.");
|
|
551
|
+
if (values.startup && values["no-startup"]) throw new Error("Choose either --startup or --no-startup.");
|
|
471
552
|
const directory = path.resolve(values["state-dir"] ?? defaultStateDirectory());
|
|
472
553
|
const emit = (event, data = {}, text) => {
|
|
473
554
|
if (values.json) process.stdout.write(JSON.stringify({ event, ...data }) + "\n");
|
|
@@ -488,7 +569,8 @@ async function main(args = process.argv.slice(2)) {
|
|
|
488
569
|
emit("startup", state, state.enabled ? "Mindwire starts after you sign in to this computer and recovers automatically." : "Automatic startup is off. Use mindwire start to run the service.");
|
|
489
570
|
return;
|
|
490
571
|
}
|
|
491
|
-
if (["connect", "start"].includes(command)) {
|
|
572
|
+
if (["connect", "start", "reconnect"].includes(command)) {
|
|
573
|
+
const requestedAction = command === "reconnect" ? "reconnect" : command === "connect" ? connectionAction(positionals[1]) : void 0;
|
|
492
574
|
const patch = {};
|
|
493
575
|
if (values.directory) patch.directory = path.resolve(values.directory);
|
|
494
576
|
if (values.host) patch.host = values.host;
|
|
@@ -521,22 +603,71 @@ async function main(args = process.argv.slice(2)) {
|
|
|
521
603
|
const info = await client2.computer.info();
|
|
522
604
|
emit("ready", { computerId: info.computerId, routes: info.routes }, "Mindwire is running in the background.");
|
|
523
605
|
if (command === "start") return;
|
|
606
|
+
if (info.routes.some((route) => route.kind === "websocket")) {
|
|
607
|
+
emit("secure_connection", {}, "Your phone connects over end-to-end encrypted SSH. The tunnel carries encrypted traffic; only phones you approve can connect.");
|
|
608
|
+
}
|
|
609
|
+
const devices = await client2.computer.devices();
|
|
610
|
+
const saved = devices.filter((device) => !device.revoked);
|
|
611
|
+
if (saved.length) emit("saved_devices", { devices: saved }, `
|
|
612
|
+
Saved phones
|
|
613
|
+
${deviceSummary(saved)}`);
|
|
614
|
+
const question = !values.json && process.stdin.isTTY ? async (prompt) => {
|
|
615
|
+
const input = createInterface({ input: process.stdin, output: process.stderr });
|
|
616
|
+
try {
|
|
617
|
+
return await input.question(prompt);
|
|
618
|
+
} finally {
|
|
619
|
+
input.close();
|
|
620
|
+
}
|
|
621
|
+
} : void 0;
|
|
622
|
+
const action = await chooseConnectionAction({ requested: requestedAction, devices, question });
|
|
524
623
|
const startupPreference = await startupStatus(directory);
|
|
525
|
-
|
|
624
|
+
const startupAction = await chooseStartupAction({
|
|
625
|
+
state: startupPreference,
|
|
626
|
+
startup: values.startup,
|
|
627
|
+
noStartup: values["no-startup"],
|
|
628
|
+
question: action === "pair" && saved.length === 0 ? question : void 0
|
|
629
|
+
});
|
|
630
|
+
if (startupAction !== void 0) {
|
|
526
631
|
try {
|
|
527
|
-
const startup = await configureStartup(directory, fileURLToPath(import.meta.url),
|
|
528
|
-
emit("startup", startup, "Automatic startup enabled.
|
|
632
|
+
const startup = await configureStartup(directory, fileURLToPath(import.meta.url), startupAction);
|
|
633
|
+
emit("startup", startup, startup.enabled ? "Automatic startup enabled. Saved phones can reconnect after you sign in to this computer." : "Automatic startup is off. Run mindwire connect after restarting, or mindwire startup enable to change this.");
|
|
529
634
|
} catch {
|
|
530
|
-
emit("startup_unavailable", {}, "Mindwire is running, but
|
|
635
|
+
emit("startup_unavailable", {}, "Mindwire is running, but startup setup didn't complete. Run mindwire startup enable to check your system's startup service.");
|
|
531
636
|
}
|
|
637
|
+
} else {
|
|
638
|
+
emit("startup", startupPreference, startupPreference.enabled ? "Automatic startup is on. Change it with mindwire startup disable." : "Automatic startup is off. Enable it with mindwire startup enable.");
|
|
532
639
|
}
|
|
533
|
-
if (
|
|
534
|
-
emit("
|
|
640
|
+
if (action === "resume") {
|
|
641
|
+
emit("resumed", { computerId: info.computerId, devices: saved }, "\nReady for your saved phone. Open this computer in Mindwire.");
|
|
642
|
+
if (info.routes.some((route) => route.kind === "websocket" && new URL(route.url).hostname.endsWith(".trycloudflare.com"))) {
|
|
643
|
+
emit("reconnect_hint", {}, "If its temporary tunnel address changed, run mindwire reconnect to refresh your phone.");
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
535
646
|
}
|
|
536
|
-
if (
|
|
537
|
-
|
|
647
|
+
if (action === "reconnect") {
|
|
648
|
+
const code = reconnectCode(await client2.computer.info(), hostname());
|
|
649
|
+
emit("reconnect_code", { code, uri: computerPairingURI(code) });
|
|
650
|
+
if (values.json) return;
|
|
651
|
+
emit("reconnect_instructions", {}, "\nOn your iPhone: Workspaces \u2192 your computer \u2192 Reconnect Computer \u2192 Scan Connection Code.\nThis refreshes the saved connection; it does not pair another phone.");
|
|
652
|
+
const display2 = await showPairingInvitation(code, {
|
|
653
|
+
mode: values["no-qr"] ? "none" : values.qr ?? "auto"
|
|
654
|
+
});
|
|
655
|
+
try {
|
|
656
|
+
if (question) await question("\nScan with your saved phone, then press Enter to close this code. ");
|
|
657
|
+
else if (values.qr === "browser") {
|
|
658
|
+
emit("waiting", {}, "This code stays open until it expires. Press Ctrl+C to close it; Mindwire keeps running.");
|
|
659
|
+
await setTimeout$1(Math.max(0, Date.parse(code.expiresAt) - Date.now()));
|
|
660
|
+
}
|
|
661
|
+
} finally {
|
|
662
|
+
display2.close();
|
|
663
|
+
}
|
|
664
|
+
const connected = (await client2.computer.devices()).filter((device) => !device.revoked && device.connected);
|
|
665
|
+
emit(connected.length ? "connected" : "waiting_for_phone", { computerId: info.computerId, devices: connected }, connected.length ? `Connected:
|
|
666
|
+
${deviceSummary(connected)}
|
|
667
|
+
Mindwire continues running in the background.` : "Mindwire is running. Open Reconnect Computer on your iPhone and scan the code to refresh its address.");
|
|
668
|
+
return;
|
|
538
669
|
}
|
|
539
|
-
const invitation = await client2.computer.invite(info.routes);
|
|
670
|
+
const invitation = await client2.computer.invite((await client2.computer.info()).routes);
|
|
540
671
|
const uri = computerPairingURI(invitation);
|
|
541
672
|
emit("invitation", { invitation, uri });
|
|
542
673
|
const display = values.json ? void 0 : await showPairingInvitation(invitation, {
|