mindwire 0.1.38 → 0.1.41
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/README.md +19 -0
- package/dist/chunk-4XNTGVCI.js +306 -0
- package/dist/chunk-4XNTGVCI.js.map +1 -0
- package/dist/{chunk-CKTVMUS4.js → chunk-T2ZIA4TK.js} +50 -16
- package/dist/chunk-T2ZIA4TK.js.map +1 -0
- package/dist/cli.js +525 -251
- package/dist/cli.js.map +1 -1
- package/dist/computer/dashboard.d.ts +1 -0
- package/dist/computer/desktop.d.ts +17 -0
- package/dist/computer/directory-http.d.ts +1 -1
- package/dist/computer/discovery.d.ts +22 -0
- package/dist/computer/inspection.d.ts +30 -0
- package/dist/computer/lifecycle.d.ts +4 -2
- package/dist/computer/runtime-version.d.ts +6 -0
- package/dist/computer/service-update.d.ts +26 -0
- package/dist/computer/setup-prompt.d.ts +2 -0
- package/dist/computer/startup.d.ts +7 -0
- package/dist/computer/terminal-ui.d.ts +9 -0
- package/dist/computer.d.ts +2 -0
- package/dist/{connection-change-5F4WNCYV.js → connection-change-RHY2OKFJ.js} +3 -3
- package/dist/{connection-change-5F4WNCYV.js.map → connection-change-RHY2OKFJ.js.map} +1 -1
- package/dist/{controller-3SZI7TBW.js → controller-G2T76QDM.js} +67 -9
- package/dist/controller-G2T76QDM.js.map +1 -0
- package/dist/{controller-handoff-KV7WRDAI.js → controller-handoff-CDPPQ2YH.js} +3 -3
- package/dist/{controller-handoff-KV7WRDAI.js.map → controller-handoff-CDPPQ2YH.js.map} +1 -1
- package/dist/daemon-binary.d.ts +2 -0
- package/dist/index.cjs +41 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +41 -7
- package/dist/index.js.map +1 -1
- package/dist/project-sync.d.ts +29 -0
- package/dist/surfaces.d.ts +20 -1
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/dist/chunk-CKTVMUS4.js.map +0 -1
- package/dist/chunk-QODJKRFK.js +0 -20
- package/dist/chunk-QODJKRFK.js.map +0 -1
- package/dist/controller-3SZI7TBW.js.map +0 -1
package/README.md
CHANGED
|
@@ -23,6 +23,25 @@ npm i mindwire
|
|
|
23
23
|
|
|
24
24
|
## Quick start
|
|
25
25
|
|
|
26
|
+
To connect this computer to the Mindwire mobile app, install globally and run
|
|
27
|
+
`mindwire connect`. Run `mindwire` for the interactive status and management menu.
|
|
28
|
+
On a Mac, desktop access is a separate opt-in:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
mindwire desktop enable # Guided local Screen Sharing approval
|
|
32
|
+
mindwire desktop # Check setup or update the saved Mac login
|
|
33
|
+
mindwire desktop disable # Close desktop access and forget its login
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The phone uses its existing paired SSH tunnel and never receives the Mac password.
|
|
37
|
+
If macOS rejects the login, the CLI lets you retry the password or check Screen
|
|
38
|
+
Sharing permissions. Canceling or entering a wrong password keeps the existing
|
|
39
|
+
desktop setup and active connections unchanged.
|
|
40
|
+
Opening a workspace or checking status does not enable screen access. See the
|
|
41
|
+
[desktop contract](../../daemon/DESKTOP.md#personal-mac-desktop) for platform support,
|
|
42
|
+
permissions and the shared input protocol. Existing installations need a release
|
|
43
|
+
advertising `health().localDesktopVersion >= 1`.
|
|
44
|
+
|
|
26
45
|
```ts
|
|
27
46
|
import { Mindwire } from "mindwire";
|
|
28
47
|
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { websocketURL, readJSON, writeJSON, acquireProcessLock, computerClient, versionAtLeast } from './chunk-T2ZIA4TK.js';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { setTimeout } from 'timers/promises';
|
|
4
|
+
|
|
5
|
+
// src/computer/provider-info.ts
|
|
6
|
+
function connectionAddress(relay, host) {
|
|
7
|
+
return relay.kind === "none" ? `ssh:${host ?? "network"}` : relay.url ? websocketURL(relay.url) : `temporary:${relay.kind}`;
|
|
8
|
+
}
|
|
9
|
+
function connectionInfo(relay, host) {
|
|
10
|
+
if (relay.kind === "none") return { provider: "direct", address: host ? "persistent" : "network" };
|
|
11
|
+
return { provider: relay.kind, address: ["cloudflare", "ngrok"].includes(relay.kind) && !relay.url ? "temporary" : "persistent" };
|
|
12
|
+
}
|
|
13
|
+
function connectionDescription(info) {
|
|
14
|
+
const name = { oblien: "Oblien", cloudflare: "Cloudflare", ngrok: "ngrok", custom: "Your tunnel", direct: "Direct SSH / VPN" }[info.provider];
|
|
15
|
+
if (info.address === "temporary") return `${name} \xB7 temporary address. For reconnect after a restart, run mindwire connection to choose a persistent address.`;
|
|
16
|
+
if (info.address === "network") return `${name} \xB7 reachable network required. Use a VPN hostname for access across networks.`;
|
|
17
|
+
return `${name} \xB7 persistent address. Saved phones retry automatically when this computer is online.`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/computer/directory-http.ts
|
|
21
|
+
var DirectoryRequestError = class extends Error {
|
|
22
|
+
constructor(status, code, retryAfter = 0) {
|
|
23
|
+
super(
|
|
24
|
+
status === 0 ? "Couldn't reach the address directory securely. Check its HTTPS address and retry." : `The address directory rejected the request (${code}).`
|
|
25
|
+
);
|
|
26
|
+
this.status = status;
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.retryAfter = retryAfter;
|
|
29
|
+
}
|
|
30
|
+
status;
|
|
31
|
+
code;
|
|
32
|
+
retryAfter;
|
|
33
|
+
};
|
|
34
|
+
async function directoryRequest(url, path, body, signal) {
|
|
35
|
+
let response;
|
|
36
|
+
try {
|
|
37
|
+
response = await fetch(url + path, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
redirect: "error",
|
|
40
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(1e4)]) : AbortSignal.timeout(1e4),
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
Accept: "application/json"
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify(body)
|
|
46
|
+
});
|
|
47
|
+
} catch {
|
|
48
|
+
signal?.throwIfAborted();
|
|
49
|
+
throw new DirectoryRequestError(0, "unreachable");
|
|
50
|
+
}
|
|
51
|
+
const reader = response.body?.getReader();
|
|
52
|
+
const chunks = [];
|
|
53
|
+
let length = 0;
|
|
54
|
+
try {
|
|
55
|
+
if (reader)
|
|
56
|
+
for (; ; ) {
|
|
57
|
+
const { done, value } = await reader.read();
|
|
58
|
+
if (done) break;
|
|
59
|
+
length += value.byteLength;
|
|
60
|
+
if (length > 64 * 1024) throw new Error("The address directory returned an oversized response.");
|
|
61
|
+
chunks.push(value);
|
|
62
|
+
}
|
|
63
|
+
} finally {
|
|
64
|
+
void reader?.cancel().catch(() => {
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
let data;
|
|
68
|
+
try {
|
|
69
|
+
data = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
70
|
+
} catch {
|
|
71
|
+
throw new DirectoryRequestError(response.status, "invalid_response");
|
|
72
|
+
}
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const error = data?.error;
|
|
75
|
+
const code = typeof error === "string" ? error : error?.code;
|
|
76
|
+
const retryAfter = Number(response.headers.get("Retry-After"));
|
|
77
|
+
throw new DirectoryRequestError(
|
|
78
|
+
response.status,
|
|
79
|
+
typeof code === "string" && /^[a-z_]{1,64}$/.test(code) ? code : "directory_error",
|
|
80
|
+
Number.isFinite(retryAfter) ? Math.max(0, Math.min(60, retryAfter)) : 0
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return data;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/computer/discovery.ts
|
|
87
|
+
var DIRECTORY_API_PATH = "/api/computer-directory/v1";
|
|
88
|
+
var DIRECTORY_URL = `https://console.mindwire.sh${DIRECTORY_API_PATH}`;
|
|
89
|
+
var discoveryPreference = (directory) => readJSON(join(directory, "computer-discovery-preference.json"));
|
|
90
|
+
async function saveDiscoveryPreference(directory, value) {
|
|
91
|
+
const preference = { ...value, url: value.url ? directoryURL(value.url) : void 0 };
|
|
92
|
+
await writeJSON(join(directory, "computer-discovery-preference.json"), preference);
|
|
93
|
+
}
|
|
94
|
+
function directoryURL(value) {
|
|
95
|
+
let url;
|
|
96
|
+
try {
|
|
97
|
+
url = new URL(value);
|
|
98
|
+
} catch {
|
|
99
|
+
throw new Error("Enter the directory's HTTPS API URL.");
|
|
100
|
+
}
|
|
101
|
+
if (value.length > 2048 || /[\s\x00]/.test(value) || url.username || url.password || url.search || url.hash || url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) {
|
|
102
|
+
throw new Error("Use an HTTPS directory URL without credentials, a query, or a fragment.");
|
|
103
|
+
}
|
|
104
|
+
if (url.pathname === "/") url.pathname = DIRECTORY_API_PATH;
|
|
105
|
+
if (url.pathname.endsWith("/") || url.pathname.includes("//") || url.pathname.includes("%")) {
|
|
106
|
+
throw new Error("Use the exact directory API URL without a trailing slash.");
|
|
107
|
+
}
|
|
108
|
+
return url.href;
|
|
109
|
+
}
|
|
110
|
+
async function enableAddressDiscovery(options) {
|
|
111
|
+
const lock = await acquireProcessLock(join(options.directory, "discovery-setup.lock"), { signal: options.signal });
|
|
112
|
+
try {
|
|
113
|
+
let status;
|
|
114
|
+
try {
|
|
115
|
+
status = await options.computer.computer.discovery();
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error.status === 404)
|
|
118
|
+
throw new Error("Update the Mindwire service before enabling automatic address recovery.");
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
const preference = await discoveryPreference(options.directory);
|
|
122
|
+
if (options.reconcile && preference?.enabled === false) return status;
|
|
123
|
+
const url = directoryURL(
|
|
124
|
+
options.url ?? process.env.MINDWIRE_DIRECTORY_URL ?? status.url ?? preference?.url ?? DIRECTORY_URL
|
|
125
|
+
);
|
|
126
|
+
if (status.url && status.url !== url && (status.enabled || (status.sequence ?? 0) !== (status.publishedSequence ?? 0))) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"Disable the current directory with mindwire discovery disable and wait for its withdrawal before changing the directory URL."
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
await saveDiscoveryPreference(options.directory, { enabled: true, url });
|
|
132
|
+
if (!status.enabled || status.url !== url || status.error?.includes("enrollment")) {
|
|
133
|
+
options.prompt.print(`Enabling encrypted address recovery with ${new URL(url).host}\u2026`);
|
|
134
|
+
try {
|
|
135
|
+
const challenge = await directoryRequest(url, "/registrations/challenge", {
|
|
136
|
+
directoryId: status.directoryId
|
|
137
|
+
}, options.signal);
|
|
138
|
+
if (typeof challenge?.challenge !== "string" || challenge.challenge.length < 16 || challenge.challenge.length > 2048) {
|
|
139
|
+
throw new Error("The address directory returned an invalid enrollment challenge.");
|
|
140
|
+
}
|
|
141
|
+
const proof = await options.computer.computer.directoryEnrollment(url, challenge.challenge);
|
|
142
|
+
const enrolled = await directoryRequest(url, "/registrations", proof, options.signal);
|
|
143
|
+
if (enrolled?.directoryId !== status.directoryId)
|
|
144
|
+
throw new Error("The directory acknowledged a different computer.");
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof DirectoryRequestError) {
|
|
147
|
+
if (error.status === 404 || error.status === 501)
|
|
148
|
+
throw new Error(
|
|
149
|
+
"The address directory isn't available at this URL. Your existing connection still works; retry after the backend is deployed."
|
|
150
|
+
);
|
|
151
|
+
if (error.status === 409)
|
|
152
|
+
throw new Error(
|
|
153
|
+
"This computer's directory registration is revoked or conflicts with its saved identity. Contact the directory operator."
|
|
154
|
+
);
|
|
155
|
+
if (error.code === "account_required")
|
|
156
|
+
throw new Error(
|
|
157
|
+
"This directory still requires an account. Update its Mindwire backend to enable key-based registration, or choose another directory with --directory-url."
|
|
158
|
+
);
|
|
159
|
+
if (error.status === 401 || error.status === 403)
|
|
160
|
+
throw new Error(
|
|
161
|
+
"The directory couldn't verify this computer's key or challenge. Retry enrollment."
|
|
162
|
+
);
|
|
163
|
+
if (error.status === 429)
|
|
164
|
+
throw new Error(
|
|
165
|
+
"The directory's registration limit was reached. Retry later or use another directory."
|
|
166
|
+
);
|
|
167
|
+
if (error.status === 503)
|
|
168
|
+
throw new Error(
|
|
169
|
+
"The directory is temporarily unavailable or at capacity. Your existing connection still works."
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
status = await options.computer.computer.configureDiscovery(true, url);
|
|
175
|
+
}
|
|
176
|
+
if (options.waitForPublication === false) return status;
|
|
177
|
+
const deadline = Date.now() + 15e3;
|
|
178
|
+
while (Date.now() < deadline) {
|
|
179
|
+
status = await options.computer.computer.discovery();
|
|
180
|
+
if (status.enabled && status.sequence && status.publishedSequence === status.sequence) return status;
|
|
181
|
+
if (status.error?.includes("enrollment") || status.error?.includes("older revision"))
|
|
182
|
+
throw new Error(status.error);
|
|
183
|
+
await setTimeout(250, void 0, { signal: options.signal });
|
|
184
|
+
}
|
|
185
|
+
throw new Error(
|
|
186
|
+
"Address recovery is configured and will retry in the background. Run mindwire discovery status to check publication."
|
|
187
|
+
);
|
|
188
|
+
} finally {
|
|
189
|
+
await lock?.close();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function disableAddressDiscovery(directory, computer, reconcile = false) {
|
|
193
|
+
const lock = await acquireProcessLock(join(directory, "discovery-setup.lock"));
|
|
194
|
+
try {
|
|
195
|
+
const status = await computer.computer.discovery();
|
|
196
|
+
const preference = await discoveryPreference(directory);
|
|
197
|
+
if (reconcile && preference?.enabled === true) return status;
|
|
198
|
+
await saveDiscoveryPreference(directory, { enabled: false, url: status.url ?? preference?.url });
|
|
199
|
+
if (!status.enabled) return status;
|
|
200
|
+
return await computer.computer.configureDiscovery(false);
|
|
201
|
+
} finally {
|
|
202
|
+
await lock?.close();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async function reconcileAddressDiscovery(options) {
|
|
206
|
+
const preference = await discoveryPreference(options.directory);
|
|
207
|
+
const { discovery: status } = await options.computer.computer.info();
|
|
208
|
+
const automatic = options.config.relay.kind === "cloudflare" && !options.config.relay.url;
|
|
209
|
+
const enabled = preference?.enabled ?? (status?.enabled || automatic && !status?.url);
|
|
210
|
+
if (!enabled) {
|
|
211
|
+
if (preference?.enabled === false && status?.enabled) await disableAddressDiscovery(options.directory, options.computer, true);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (!status) throw new Error("Update the Mindwire service to enable automatic reconnection.");
|
|
215
|
+
const url = directoryURL(preference?.url ?? process.env.MINDWIRE_DIRECTORY_URL ?? status.url ?? DIRECTORY_URL);
|
|
216
|
+
if (status.enabled && status.url === url && !status.error?.includes("enrollment")) return;
|
|
217
|
+
await enableAddressDiscovery({
|
|
218
|
+
...options,
|
|
219
|
+
url,
|
|
220
|
+
waitForPublication: false,
|
|
221
|
+
reconcile: true,
|
|
222
|
+
prompt: {
|
|
223
|
+
print() {
|
|
224
|
+
},
|
|
225
|
+
open() {
|
|
226
|
+
},
|
|
227
|
+
question: async () => {
|
|
228
|
+
throw new Error("Unexpected interactive setup.");
|
|
229
|
+
},
|
|
230
|
+
secret: async () => {
|
|
231
|
+
throw new Error("Unexpected interactive setup.");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function discoveryDescription(status) {
|
|
237
|
+
if (!status?.enabled) return "Automatic address recovery is off. Enable it with mindwire discovery enable.";
|
|
238
|
+
if (status.error) return status.error;
|
|
239
|
+
return status.sequence && status.publishedSequence === status.sequence ? "Encrypted address recovery is on. Saved phones can find this computer when its tunnel address changes." : "Publishing the computer's encrypted address\u2026";
|
|
240
|
+
}
|
|
241
|
+
function activeUpdate(update) {
|
|
242
|
+
return !!update && ["queued", "downloading", "waiting", "restarting"].includes(update.status);
|
|
243
|
+
}
|
|
244
|
+
async function queueServiceUpdate(client, version, force = false) {
|
|
245
|
+
for (let attempt = 0; ; attempt++) {
|
|
246
|
+
const previous = await client.computer.updateStatus();
|
|
247
|
+
if (activeUpdate(previous) && (!force || previous.force)) return previous;
|
|
248
|
+
try {
|
|
249
|
+
return await client.computer.requestUpdate(activeUpdate(previous) ? previous.version : version, { force });
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error.status !== 409 || attempt >= 2) throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function updateComputer(options) {
|
|
256
|
+
const version = options.version.replace(/^v/, "");
|
|
257
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) throw new Error("Enter a Mindwire release version, such as 0.1.38.");
|
|
258
|
+
const connect = options.connect ?? (() => computerClient(options.directory));
|
|
259
|
+
const deadline = Date.now() + (options.timeoutMs ?? 12e4);
|
|
260
|
+
let requested, last = "";
|
|
261
|
+
for (; ; ) {
|
|
262
|
+
options.signal?.throwIfAborted();
|
|
263
|
+
if (!requested) {
|
|
264
|
+
const client = await connect();
|
|
265
|
+
const installed = (await client.health()).version.replace(/^v/, "");
|
|
266
|
+
if (installed === version || !options.allowDowngrade && versionAtLeast(installed, version))
|
|
267
|
+
return { version: installed, complete: true };
|
|
268
|
+
requested = await queueServiceUpdate(client, version, options.force);
|
|
269
|
+
}
|
|
270
|
+
const update = await readJSON(join(options.directory, "computer-update.json")) ?? requested;
|
|
271
|
+
if (update.id !== requested.id) {
|
|
272
|
+
requested = void 0;
|
|
273
|
+
} else {
|
|
274
|
+
const key = `${update.id}:${update.status}`;
|
|
275
|
+
if (last !== key) {
|
|
276
|
+
last = key;
|
|
277
|
+
options.onProgress?.(update);
|
|
278
|
+
}
|
|
279
|
+
if (update.status === "failed") throw new Error(update.error ?? "The service update failed. Run mindwire inspect for details.");
|
|
280
|
+
if (update.status === "complete") {
|
|
281
|
+
requested = void 0;
|
|
282
|
+
if (Date.now() < deadline) continue;
|
|
283
|
+
} else if (!options.force && update.status === "waiting") {
|
|
284
|
+
return { version, complete: false, update };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (Date.now() >= deadline) return { version, complete: false, update };
|
|
288
|
+
await setTimeout(500, void 0, { signal: options.signal });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function updateProgress(update) {
|
|
292
|
+
const phase = {
|
|
293
|
+
idle: "Ready",
|
|
294
|
+
queued: "Queued",
|
|
295
|
+
downloading: "Downloading verified release\u2026",
|
|
296
|
+
waiting: "Waiting for active work to finish",
|
|
297
|
+
restarting: "Restarting service\u2026",
|
|
298
|
+
complete: "Installed",
|
|
299
|
+
failed: "Update failed"
|
|
300
|
+
};
|
|
301
|
+
return `${update.version} \xB7 ${phase[update.status]}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export { activeUpdate, connectionAddress, connectionDescription, connectionInfo, disableAddressDiscovery, discoveryDescription, discoveryPreference, enableAddressDiscovery, queueServiceUpdate, reconcileAddressDiscovery, saveDiscoveryPreference, updateComputer, updateProgress };
|
|
305
|
+
//# sourceMappingURL=chunk-4XNTGVCI.js.map
|
|
306
|
+
//# sourceMappingURL=chunk-4XNTGVCI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/computer/provider-info.ts","../src/computer/directory-http.ts","../src/computer/discovery.ts","../src/computer/service-update.ts"],"names":["delay","join"],"mappings":"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","file":"chunk-4XNTGVCI.js","sourcesContent":["import type { ComputerConnectionInfo } from \"../computer.js\";\nimport type { RelayOptions } from \"./relay.js\";\nimport { websocketURL } from \"./relay.js\";\n\n/** Credential renewal does not change the address already saved by the phone. */\nexport function connectionAddress(relay: RelayOptions, host?: string): string {\n return relay.kind === \"none\" ? `ssh:${host ?? \"network\"}`\n : relay.url ? websocketURL(relay.url) : `temporary:${relay.kind}`;\n}\n\n/** Public, non-secret metadata. Route/SSH identity is independent of provider credentials. */\nexport function connectionInfo(relay: RelayOptions, host?: string): ComputerConnectionInfo {\n if (relay.kind === \"none\") return { provider: \"direct\", address: host ? \"persistent\" : \"network\" };\n return { provider: relay.kind, address: [\"cloudflare\", \"ngrok\"].includes(relay.kind) && !relay.url ? \"temporary\" : \"persistent\" };\n}\n\nexport function connectionDescription(info: ComputerConnectionInfo): string {\n const name = { oblien: \"Oblien\", cloudflare: \"Cloudflare\", ngrok: \"ngrok\", custom: \"Your tunnel\", direct: \"Direct SSH / VPN\" }[info.provider];\n if (info.address === \"temporary\") return `${name} · temporary address. For reconnect after a restart, run mindwire connection to choose a persistent address.`;\n if (info.address === \"network\") return `${name} · reachable network required. Use a VPN hostname for access across networks.`;\n return `${name} · persistent address. Saved phones retry automatically when this computer is online.`;\n}\n","export class DirectoryRequestError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n readonly retryAfter = 0,\n ) {\n super(\n status === 0\n ? \"Couldn't reach the address directory securely. Check its HTTPS address and retry.\"\n : `The address directory rejected the request (${code}).`,\n );\n }\n}\n\n/** Bounded, same-origin requests. No redirects, cookie jar or retained credentials. */\nexport async function directoryRequest<T>(url: string, path: string, body: unknown, signal?: AbortSignal): Promise<T> {\n let response: Response;\n try {\n response = await fetch(url + path, {\n method: \"POST\",\n redirect: \"error\",\n signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(10_000)]) : AbortSignal.timeout(10_000),\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n });\n } catch {\n signal?.throwIfAborted();\n throw new DirectoryRequestError(0, \"unreachable\");\n }\n const reader = response.body?.getReader();\n const chunks: Uint8Array[] = [];\n let length = 0;\n try {\n if (reader)\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n length += value.byteLength;\n if (length > 64 * 1024) throw new Error(\"The address directory returned an oversized response.\");\n chunks.push(value);\n }\n } finally {\n void reader?.cancel().catch(() => {});\n }\n let data: unknown;\n try {\n data = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n } catch {\n throw new DirectoryRequestError(response.status, \"invalid_response\");\n }\n if (!response.ok) {\n const error = (data as { error?: unknown } | null)?.error;\n const code = typeof error === \"string\" ? error : (error as { code?: unknown } | null)?.code;\n const retryAfter = Number(response.headers.get(\"Retry-After\"));\n throw new DirectoryRequestError(\n response.status,\n typeof code === \"string\" && /^[a-z_]{1,64}$/.test(code) ? code : \"directory_error\",\n Number.isFinite(retryAfter) ? Math.max(0, Math.min(60, retryAfter)) : 0,\n );\n }\n return data as T;\n}\n","import { join } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport type { Mindwire } from \"../client.js\";\nimport type { ComputerDiscoveryStatus } from \"../computer.js\";\nimport { acquireProcessLock } from \"./lock.js\";\nimport type { SetupPrompt } from \"./setup-prompt.js\";\nimport { directoryRequest, DirectoryRequestError } from \"./directory-http.js\";\nimport { readJSON, writeJSON, type ComputerConfig } from \"./lifecycle.js\";\n\nexport const DIRECTORY_API_PATH = \"/api/computer-directory/v1\";\nexport const DIRECTORY_URL = `https://console.mindwire.sh${DIRECTORY_API_PATH}`;\n\nexport interface DiscoveryPreference { enabled: boolean; url?: string }\nexport const discoveryPreference = (directory: string) =>\n readJSON<DiscoveryPreference>(join(directory, \"computer-discovery-preference.json\"));\n\n/** Desired setup is durable even if enrollment is temporarily offline. The\n * daemon remains the authority for signed publications and phone access. */\nexport async function saveDiscoveryPreference(directory: string, value: DiscoveryPreference): Promise<void> {\n const preference = { ...value, url: value.url ? directoryURL(value.url) : undefined };\n await writeJSON(join(directory, \"computer-discovery-preference.json\"), preference);\n}\n\nexport function directoryURL(value: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Enter the directory's HTTPS API URL.\");\n }\n if (\n value.length > 2048 ||\n /[\\s\\x00]/.test(value) ||\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n (url.protocol !== \"https:\" &&\n !(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)))\n ) {\n throw new Error(\"Use an HTTPS directory URL without credentials, a query, or a fragment.\");\n }\n if (url.pathname === \"/\") url.pathname = DIRECTORY_API_PATH;\n if (url.pathname.endsWith(\"/\") || url.pathname.includes(\"//\") || url.pathname.includes(\"%\")) {\n throw new Error(\"Use the exact directory API URL without a trailing slash.\");\n }\n return url.href;\n}\n\nexport async function enableAddressDiscovery(options: {\n directory: string;\n computer: Mindwire;\n prompt: SetupPrompt;\n url?: string;\n waitForPublication?: boolean;\n signal?: AbortSignal;\n reconcile?: boolean;\n}): Promise<ComputerDiscoveryStatus> {\n const lock = await acquireProcessLock(join(options.directory, \"discovery-setup.lock\"), { signal: options.signal });\n try {\n let status: ComputerDiscoveryStatus;\n try {\n status = await options.computer.computer.discovery();\n } catch (error) {\n if ((error as { status?: number }).status === 404)\n throw new Error(\"Update the Mindwire service before enabling automatic address recovery.\");\n throw error;\n }\n const preference = await discoveryPreference(options.directory);\n if (options.reconcile && preference?.enabled === false) return status;\n // Preserve a saved private directory across upgrades. Credentials belonging\n // to another provider are never read by this flow.\n const url = directoryURL(\n options.url ?? process.env.MINDWIRE_DIRECTORY_URL ?? status.url ?? preference?.url ?? DIRECTORY_URL,\n );\n if (\n status.url &&\n status.url !== url &&\n (status.enabled || (status.sequence ?? 0) !== (status.publishedSequence ?? 0))\n ) {\n throw new Error(\n \"Disable the current directory with mindwire discovery disable and wait for its withdrawal before changing the directory URL.\",\n );\n }\n await saveDiscoveryPreference(options.directory, { enabled: true, url });\n if (!status.enabled || status.url !== url || status.error?.includes(\"enrollment\")) {\n options.prompt.print(`Enabling encrypted address recovery with ${new URL(url).host}…`);\n try {\n const challenge = await directoryRequest<{ challenge: string }>(url, \"/registrations/challenge\", {\n directoryId: status.directoryId,\n }, options.signal);\n if (\n typeof challenge?.challenge !== \"string\" ||\n challenge.challenge.length < 16 ||\n challenge.challenge.length > 2048\n ) {\n throw new Error(\"The address directory returned an invalid enrollment challenge.\");\n }\n const proof = await options.computer.computer.directoryEnrollment(url, challenge.challenge);\n const enrolled = await directoryRequest<{ directoryId: string }>(url, \"/registrations\", proof, options.signal);\n if (enrolled?.directoryId !== status.directoryId)\n throw new Error(\"The directory acknowledged a different computer.\");\n } catch (error) {\n if (error instanceof DirectoryRequestError) {\n if (error.status === 404 || error.status === 501)\n throw new Error(\n \"The address directory isn't available at this URL. Your existing connection still works; retry after the backend is deployed.\",\n );\n if (error.status === 409)\n throw new Error(\n \"This computer's directory registration is revoked or conflicts with its saved identity. Contact the directory operator.\",\n );\n if (error.code === \"account_required\")\n throw new Error(\n \"This directory still requires an account. Update its Mindwire backend to enable key-based registration, or choose another directory with --directory-url.\",\n );\n if (error.status === 401 || error.status === 403)\n throw new Error(\n \"The directory couldn't verify this computer's key or challenge. Retry enrollment.\",\n );\n if (error.status === 429)\n throw new Error(\n \"The directory's registration limit was reached. Retry later or use another directory.\",\n );\n if (error.status === 503)\n throw new Error(\n \"The directory is temporarily unavailable or at capacity. Your existing connection still works.\",\n );\n }\n throw error;\n }\n status = await options.computer.computer.configureDiscovery(true, url);\n }\n if (options.waitForPublication === false) return status;\n const deadline = Date.now() + 15_000;\n while (Date.now() < deadline) {\n status = await options.computer.computer.discovery();\n if (status.enabled && status.sequence && status.publishedSequence === status.sequence) return status;\n if (status.error?.includes(\"enrollment\") || status.error?.includes(\"older revision\"))\n throw new Error(status.error);\n await delay(250, undefined, { signal: options.signal });\n }\n throw new Error(\n \"Address recovery is configured and will retry in the background. Run mindwire discovery status to check publication.\",\n );\n } finally {\n await lock?.close();\n }\n}\n\nexport async function disableAddressDiscovery(directory: string, computer: Mindwire, reconcile = false): Promise<ComputerDiscoveryStatus> {\n const lock = await acquireProcessLock(join(directory, \"discovery-setup.lock\"));\n try {\n const status = await computer.computer.discovery();\n const preference = await discoveryPreference(directory);\n if (reconcile && preference?.enabled === true) return status;\n await saveDiscoveryPreference(directory, { enabled: false, url: status.url ?? preference?.url });\n if (!status.enabled) return status;\n return await computer.computer.configureDiscovery(false);\n } finally { await lock?.close(); }\n}\n\n/** Runs once after startup and retries failed enrollment in the controller. A\n * healthy setup makes only a local status read: publication/renewal stays in Go.\n * Direct/VPN/custom connections never opt into a hosted directory implicitly. */\nexport async function reconcileAddressDiscovery(options: {\n directory: string; config: ComputerConfig; computer: Mindwire; signal?: AbortSignal;\n}): Promise<void> {\n const preference = await discoveryPreference(options.directory);\n const { discovery: status } = await options.computer.computer.info();\n const automatic = options.config.relay.kind === \"cloudflare\" && !options.config.relay.url;\n const enabled = preference?.enabled ?? (status?.enabled || automatic && !status?.url);\n if (!enabled) {\n if (preference?.enabled === false && status?.enabled) await disableAddressDiscovery(options.directory, options.computer, true);\n return;\n }\n if (!status) throw new Error(\"Update the Mindwire service to enable automatic reconnection.\");\n const url = directoryURL(preference?.url ?? process.env.MINDWIRE_DIRECTORY_URL ?? status.url ?? DIRECTORY_URL);\n if (status.enabled && status.url === url && !status.error?.includes(\"enrollment\")) return;\n await enableAddressDiscovery({ ...options, url, waitForPublication: false, reconcile: true,\n prompt: { print() {}, open() {}, question: async () => { throw new Error(\"Unexpected interactive setup.\"); },\n secret: async () => { throw new Error(\"Unexpected interactive setup.\"); } } });\n}\n\nexport function discoveryDescription(status: ComputerDiscoveryStatus | undefined): string {\n if (!status?.enabled) return \"Automatic address recovery is off. Enable it with mindwire discovery enable.\";\n if (status.error) return status.error;\n return status.sequence && status.publishedSequence === status.sequence\n ? \"Encrypted address recovery is on. Saved phones can find this computer when its tunnel address changes.\"\n : \"Publishing the computer's encrypted address…\";\n}\n","import { join } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport type { ComputerUpdate } from \"../computer.js\";\nimport type { Mindwire } from \"../client.js\";\nimport { computerClient, readJSON } from \"./lifecycle.js\";\nimport { versionAtLeast } from \"../version.js\";\n\ntype UpdateClient = Pick<Mindwire, \"health\" | \"computer\">;\nexport function activeUpdate(update: ComputerUpdate | undefined): update is ComputerUpdate {\n return !!update && [\"queued\", \"downloading\", \"waiting\", \"restarting\"].includes(update.status);\n}\n\n/** The daemon arbitrates requests. Join an earlier version before requesting the\n * next; promoting a request never creates a second download or bypasses its lease. */\nexport async function queueServiceUpdate(client: UpdateClient, version: string, force = false): Promise<ComputerUpdate> {\n for (let attempt = 0; ; attempt++) {\n const previous = await client.computer.updateStatus();\n if (activeUpdate(previous) && (!force || previous.force)) return previous;\n try {\n return await client.computer.requestUpdate(activeUpdate(previous) ? previous.version : version, { force });\n } catch (error) {\n if ((error as { status?: number }).status !== 409 || attempt >= 2) throw error;\n // Another caller won admission. Read the authoritative request and join it.\n }\n }\n}\n\nexport interface ServiceUpdateResult { version: string; complete: boolean; update?: ComputerUpdate }\n\n/** A CLI may close after queuing: the controller owns download/restart. Human\n * callers get a bounded result, including the exact older request being joined. */\nexport async function updateComputer(options: {\n directory: string; version: string; force?: boolean; allowDowngrade?: boolean;\n onProgress?: (update: ComputerUpdate) => void; signal?: AbortSignal; timeoutMs?: number;\n connect?: () => Promise<UpdateClient>;\n}):\nPromise<ServiceUpdateResult> {\n const version = options.version.replace(/^v/, \"\");\n if (!/^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) throw new Error(\"Enter a Mindwire release version, such as 0.1.38.\");\n const connect = options.connect ?? (() => computerClient(options.directory));\n const deadline = Date.now() + (options.timeoutMs ?? 120_000);\n let requested: ComputerUpdate | undefined, last = \"\";\n for (;;) {\n options.signal?.throwIfAborted();\n if (!requested) {\n const client = await connect();\n const installed = (await client.health()).version.replace(/^v/, \"\");\n if (installed === version || !options.allowDowngrade && versionAtLeast(installed, version))\n return { version: installed, complete: true };\n requested = await queueServiceUpdate(client, version, options.force);\n }\n // Progress survives the short API outage during restart. Only the daemon\n // accepts new requests; this read never changes or clears its status.\n const update = await readJSON<ComputerUpdate>(join(options.directory, \"computer-update.json\")) ?? requested;\n if (update.id !== requested.id) {\n // A concurrent update completed/advanced first. Recheck the live service.\n requested = undefined;\n } else {\n const key = `${update.id}:${update.status}`;\n if (last !== key) { last = key; options.onProgress?.(update); }\n if (update.status === \"failed\") throw new Error(update.error ?? \"The service update failed. Run mindwire inspect for details.\");\n if (update.status === \"complete\") {\n requested = undefined;\n if (Date.now() < deadline) continue;\n } else if (!options.force && update.status === \"waiting\") {\n return { version, complete: false, update };\n }\n }\n if (Date.now() >= deadline) return { version, complete: false, update };\n await delay(500, undefined, { signal: options.signal });\n }\n}\n\nexport function updateProgress(update: ComputerUpdate): string {\n const phase: Record<ComputerUpdate[\"status\"], string> = {\n idle: \"Ready\", queued: \"Queued\", downloading: \"Downloading verified release…\",\n waiting: \"Waiting for active work to finish\", restarting: \"Restarting service…\",\n complete: \"Installed\", failed: \"Update failed\",\n };\n return `${update.version} · ${phase[update.status]}`;\n}\n"]}
|
|
@@ -88,7 +88,7 @@ function computerPairingURI(invitation) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// src/version.ts
|
|
91
|
-
var SDK_VERSION = "0.1.
|
|
91
|
+
var SDK_VERSION = "0.1.41" ;
|
|
92
92
|
function versionAtLeast(actual, desired) {
|
|
93
93
|
if (actual === desired) return true;
|
|
94
94
|
if (!actual || !/^\d+\.\d+\.\d+$/.test(actual) || !/^\d+\.\d+\.\d+$/.test(desired)) return false;
|
|
@@ -139,7 +139,7 @@ async function ensureDaemonBinary(opts = {}) {
|
|
|
139
139
|
if (!request) throw new MindwireError("mindwire: fetch is unavailable; set daemonBin or MINDWIRE_DAEMON");
|
|
140
140
|
const [checksums, binary] = await Promise.all([request(`${release}/checksums.txt`), request(`${release}/${asset}`)]);
|
|
141
141
|
if (!checksums.ok || !binary.ok) {
|
|
142
|
-
if (!opts.releaseBaseUrl && !proc.process?.env?.MINDWIRE_RELEASE_BASE_URL) {
|
|
142
|
+
if (!opts.exactVersion && !opts.releaseBaseUrl && !proc.process?.env?.MINDWIRE_RELEASE_BASE_URL) {
|
|
143
143
|
const latest = await request("https://api.github.com/repos/oblien/mindwire/releases/latest");
|
|
144
144
|
if (latest.ok) {
|
|
145
145
|
const tag = (await latest.json()).tag_name;
|
|
@@ -1092,6 +1092,18 @@ var ProjectSyncError = class extends MindwireError {
|
|
|
1092
1092
|
};
|
|
1093
1093
|
var active = (o) => ["queued", "exporting", "preparing", "applying"].includes(o.status);
|
|
1094
1094
|
var prefix = "/workspace/sync";
|
|
1095
|
+
var selectionPaths = (paths = []) => {
|
|
1096
|
+
const sorted = [...new Set(paths.map((path5) => path5.replace(/\/$/, "")))].sort();
|
|
1097
|
+
const selected = new Set(sorted);
|
|
1098
|
+
return sorted.filter((path5) => {
|
|
1099
|
+
let parent = path5;
|
|
1100
|
+
while (parent.includes("/")) {
|
|
1101
|
+
parent = parent.slice(0, parent.lastIndexOf("/"));
|
|
1102
|
+
if (selected.has(parent)) return false;
|
|
1103
|
+
}
|
|
1104
|
+
return true;
|
|
1105
|
+
});
|
|
1106
|
+
};
|
|
1095
1107
|
function delay4(ms, signal) {
|
|
1096
1108
|
signal?.throwIfAborted();
|
|
1097
1109
|
return new Promise((resolve3, reject) => {
|
|
@@ -1114,7 +1126,16 @@ var ProjectSyncApi = class {
|
|
|
1114
1126
|
}
|
|
1115
1127
|
mw;
|
|
1116
1128
|
flights = /* @__PURE__ */ new Map();
|
|
1117
|
-
|
|
1129
|
+
async requireSelectionSupport() {
|
|
1130
|
+
if (((await this.mw.health()).projectSyncVersion ?? 0) < 2)
|
|
1131
|
+
throw new MindwireError("Update Mindwire on both workspaces to review and switch selected project files.");
|
|
1132
|
+
}
|
|
1133
|
+
async preview(request, options = {}) {
|
|
1134
|
+
await this.requireSelectionSupport();
|
|
1135
|
+
return this.mw.http.request("POST", `${prefix}/preview`, { body: request, ...options });
|
|
1136
|
+
}
|
|
1137
|
+
async export(request, options = {}) {
|
|
1138
|
+
await this.requireSelectionSupport();
|
|
1118
1139
|
return this.mw.http.request("POST", `${prefix}/exports`, { body: request, ...options });
|
|
1119
1140
|
}
|
|
1120
1141
|
import(request, options = {}) {
|
|
@@ -1199,6 +1220,7 @@ var ProjectSyncApi = class {
|
|
|
1199
1220
|
* throws ProjectSyncError carrying the complete conflict/recovery operation.
|
|
1200
1221
|
*/
|
|
1201
1222
|
switchTo(target, request, options = {}) {
|
|
1223
|
+
request = { ...request, includeIgnored: selectionPaths(request.includeIgnored) };
|
|
1202
1224
|
const intent = JSON.stringify(request);
|
|
1203
1225
|
const old = this.flights.get(request.id);
|
|
1204
1226
|
if (old) {
|
|
@@ -1214,7 +1236,8 @@ var ProjectSyncApi = class {
|
|
|
1214
1236
|
if (!(error instanceof ApiError) || error.status !== 404) throw error;
|
|
1215
1237
|
}
|
|
1216
1238
|
if (accepted) {
|
|
1217
|
-
if ((accepted.path ?? "") !== (request.path ?? "")
|
|
1239
|
+
if ((accepted.path ?? "") !== (request.path ?? "") || (accepted.selectionToken ?? "") !== (request.selectionToken ?? "") || JSON.stringify(selectionPaths(accepted.includeIgnored)) !== JSON.stringify(request.includeIgnored))
|
|
1240
|
+
throw new MindwireError("This sync ID belongs to a different destination folder or file selection.");
|
|
1218
1241
|
const operation = await target.wait(accepted.id, {
|
|
1219
1242
|
signal: options.signal,
|
|
1220
1243
|
onUpdate: (operation2) => options.onProgress?.({ phase: "apply", transferredBytes: 0, operation: operation2 })
|
|
@@ -1223,11 +1246,11 @@ var ProjectSyncApi = class {
|
|
|
1223
1246
|
return operation;
|
|
1224
1247
|
}
|
|
1225
1248
|
const health = await Promise.all([this.mw.health(), target.mw.health()]);
|
|
1226
|
-
if (health.some((h) => (h.projectSyncVersion ?? 0) <
|
|
1249
|
+
if (health.some((h) => (h.projectSyncVersion ?? 0) < 2)) throw new MindwireError("Update Mindwire on both workspaces to review and switch selected project files.");
|
|
1227
1250
|
let bytes = 0;
|
|
1228
1251
|
const emit2 = (phase, operation) => options.onProgress?.({ phase, transferredBytes: bytes, operation });
|
|
1229
1252
|
emit2("checkpoint");
|
|
1230
|
-
let o = await this.export({ id: `${request.id}-export`, projectId: request.projectId }, options);
|
|
1253
|
+
let o = await this.export({ id: `${request.id}-export`, projectId: request.projectId, includeIgnored: request.includeIgnored, selectionToken: request.selectionToken }, options);
|
|
1231
1254
|
o = await this.wait(o.id, { signal: options.signal, onUpdate: (value) => emit2("checkpoint", value) });
|
|
1232
1255
|
if (o.status !== "succeeded" || !o.resultCheckpointId) throw new ProjectSyncError(o);
|
|
1233
1256
|
emit2("transfer");
|
|
@@ -1235,7 +1258,7 @@ var ProjectSyncApi = class {
|
|
|
1235
1258
|
bytes += n;
|
|
1236
1259
|
emit2("transfer");
|
|
1237
1260
|
} });
|
|
1238
|
-
o = await target.import({ id: `${request.id}-import`, checkpointId: o.resultCheckpointId, path: request.path }, options);
|
|
1261
|
+
o = await target.import({ id: `${request.id}-import`, checkpointId: o.resultCheckpointId, path: request.path, includeIgnored: request.includeIgnored, selectionToken: request.selectionToken }, options);
|
|
1239
1262
|
o = await target.wait(o.id, { signal: options.signal, onUpdate: (value) => emit2("apply", value) });
|
|
1240
1263
|
if (o.status !== "succeeded") throw new ProjectSyncError(o);
|
|
1241
1264
|
return o;
|
|
@@ -1472,6 +1495,17 @@ var SurfacesApi = class {
|
|
|
1472
1495
|
bind(binding) {
|
|
1473
1496
|
return this.mw.http.request("PUT", "/surfaces/desktop/binding", { body: binding });
|
|
1474
1497
|
}
|
|
1498
|
+
localStatus() {
|
|
1499
|
+
return this.mw.http.request("GET", "/surfaces/desktop/local");
|
|
1500
|
+
}
|
|
1501
|
+
/** Local CLI only. The separate 0600 control credential is not part of phone pairing. */
|
|
1502
|
+
configureLocal(settings, localControlToken) {
|
|
1503
|
+
if (!localControlToken) throw new Error("Run mindwire desktop on your Mac to manage desktop access.");
|
|
1504
|
+
return this.mw.http.request("PUT", "/surfaces/desktop/local", {
|
|
1505
|
+
body: settings,
|
|
1506
|
+
headers: { "X-Mindwire-Local-Control": localControlToken }
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1475
1509
|
open(request) {
|
|
1476
1510
|
return this.mw.http.request("POST", "/surfaces/desktop/sessions", { body: request });
|
|
1477
1511
|
}
|
|
@@ -2416,7 +2450,7 @@ var NotifyApi = class {
|
|
|
2416
2450
|
};
|
|
2417
2451
|
|
|
2418
2452
|
// src/computer/lifecycle.ts
|
|
2419
|
-
var CONTROLLER_PROTOCOL =
|
|
2453
|
+
var CONTROLLER_PROTOCOL = 4;
|
|
2420
2454
|
var defaultStateDirectory = () => path4.join(homedir(), ".mindwire", "computer");
|
|
2421
2455
|
var defaultComputerConfig = () => ({ directory: homedir(), bind: "0.0.0.0", sshPort: 8791, websocketPort: 8792, relay: { kind: "cloudflare" } });
|
|
2422
2456
|
async function readJSON(file) {
|
|
@@ -2436,13 +2470,13 @@ async function writeJSON(file, value) {
|
|
|
2436
2470
|
await fs2.rm(temp, { force: true });
|
|
2437
2471
|
}
|
|
2438
2472
|
}
|
|
2439
|
-
async function computerClient(directory) {
|
|
2473
|
+
async function computerClient(directory, requestTimeoutMs = 3e3) {
|
|
2440
2474
|
const runtime = await readJSON(path4.join(directory, "computer-runtime.json"));
|
|
2441
2475
|
if (!runtime) throw new Error("Mindwire is not running. Run mindwire connect on the computer.");
|
|
2442
2476
|
const url = new URL(`http://${runtime.apiAddress}`);
|
|
2443
2477
|
if (!["127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password) throw new Error("Invalid local Mindwire address.");
|
|
2444
2478
|
const token = (await fs2.readFile(path4.join(directory, "daemon.token"), "utf8")).trim();
|
|
2445
|
-
const client = new Mindwire({ target: remote(url.origin, { token }), requestTimeoutMs
|
|
2479
|
+
const client = new Mindwire({ target: remote(url.origin, { token }), requestTimeoutMs });
|
|
2446
2480
|
const info = await client.computer.info();
|
|
2447
2481
|
if (info.computerId !== runtime.computerId || info.pid !== runtime.pid) throw new Error("The local Mindwire process changed. Retry the command.");
|
|
2448
2482
|
return client;
|
|
@@ -2519,7 +2553,7 @@ async function ensureComputer(directory, cliPath, patch = {}, onProgress, option
|
|
|
2519
2553
|
if (!compatible(controller2) || !await processStateAlive(controller2)) {
|
|
2520
2554
|
await ensureComputer(directory, cliPath, {}, onProgress, { ...options, waitForRoutes: false });
|
|
2521
2555
|
}
|
|
2522
|
-
const { changeComputerConnection } = await import('./connection-change-
|
|
2556
|
+
const { changeComputerConnection } = await import('./connection-change-RHY2OKFJ.js');
|
|
2523
2557
|
await changeComputerConnection(directory, Object.fromEntries(changed.map((key) => [key, patch[key]])), { signal: options.signal, onProgress });
|
|
2524
2558
|
return computerClient(directory);
|
|
2525
2559
|
}
|
|
@@ -2560,7 +2594,7 @@ async function ensureComputer(directory, cliPath, patch = {}, onProgress, option
|
|
|
2560
2594
|
let controller = await readJSON(path4.join(directory, "computer-controller.json"));
|
|
2561
2595
|
if (existing && controller && !compatible(controller) && await processStateAlive(controller)) {
|
|
2562
2596
|
onProgress?.("Refreshing the background connection service\u2026");
|
|
2563
|
-
const { handoffController } = await import('./controller-handoff-
|
|
2597
|
+
const { handoffController } = await import('./controller-handoff-CDPPQ2YH.js');
|
|
2564
2598
|
await handoffController(directory, controller, config, await existing.computer.info());
|
|
2565
2599
|
controller = void 0;
|
|
2566
2600
|
}
|
|
@@ -2594,7 +2628,7 @@ async function ensureComputer(directory, cliPath, patch = {}, onProgress, option
|
|
|
2594
2628
|
}
|
|
2595
2629
|
}
|
|
2596
2630
|
async function superviseComputer(directory) {
|
|
2597
|
-
const controller = await import('./controller-
|
|
2631
|
+
const controller = await import('./controller-G2T76QDM.js');
|
|
2598
2632
|
await controller.superviseComputer(directory);
|
|
2599
2633
|
}
|
|
2600
2634
|
async function stopComputer(directory, force = false) {
|
|
@@ -2618,6 +2652,6 @@ async function stopComputer(directory, force = false) {
|
|
|
2618
2652
|
throw new Error("Mindwire is still stopping. Check computer.log.");
|
|
2619
2653
|
}
|
|
2620
2654
|
|
|
2621
|
-
export { CONTROLLER_PROTOCOL, ProviderConnectionError, RelayOutput, SDK_VERSION, acquireProcessLock, adoptProcess, adoptRelay, cloudflareCommand, computerClient, computerPairingQRData, computerPairingURI, currentProcessIdentity, defaultComputerConfig, defaultStateDirectory, directRoutes, ensureComputer, ensureDaemonBinary, hasCertificateError, ownChild, processIdentity, processStateAlive, providerFailure, providerNeedsAction, readJSON, startRelay, stopComputer, superviseComputer, websocketURL, writeJSON };
|
|
2622
|
-
//# sourceMappingURL=chunk-
|
|
2623
|
-
//# sourceMappingURL=chunk-
|
|
2655
|
+
export { CONTROLLER_PROTOCOL, ProviderConnectionError, RelayOutput, SDK_VERSION, acquireProcessLock, adoptProcess, adoptRelay, cloudflareCommand, computerClient, computerPairingQRData, computerPairingURI, currentProcessIdentity, defaultComputerConfig, defaultStateDirectory, directRoutes, ensureComputer, ensureDaemonBinary, hasCertificateError, ownChild, processIdentity, processStateAlive, providerFailure, providerNeedsAction, readJSON, startRelay, stopComputer, superviseComputer, versionAtLeast, websocketURL, writeJSON };
|
|
2656
|
+
//# sourceMappingURL=chunk-T2ZIA4TK.js.map
|
|
2657
|
+
//# sourceMappingURL=chunk-T2ZIA4TK.js.map
|