@mutmutco/installer-launcher 0.1.12 → 0.1.13

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.
@@ -32,14 +32,342 @@ __export(index_exports, {
32
32
  runInstallEntry: () => runInstallEntry
33
33
  });
34
34
  module.exports = __toCommonJS(index_exports);
35
- var import_node_child_process3 = require("node:child_process");
36
- var import_node_fs9 = require("node:fs");
35
+ var import_node_crypto6 = require("node:crypto");
36
+
37
+ // src/acquisition.ts
38
+ var import_node_crypto3 = require("node:crypto");
39
+ var import_node_fs3 = require("node:fs");
40
+ var import_node_path3 = require("node:path");
41
+
42
+ // src/runtime.ts
43
+ var import_node_crypto = require("node:crypto");
44
+ var import_node_child_process = require("node:child_process");
45
+ var import_node_fs = require("node:fs");
46
+ var import_node_path = require("node:path");
47
+ var NODE_VERSION = "24.20.0";
48
+ var NPM_VERSION = "12.0.2";
49
+ var ARTIFACTS = {
50
+ "win32-x64": ["node-v24.20.0-win-x64.zip", "6cac9ffbca8f6a47091e4b5c772e0606049c3871cb67d900c0cedde630e545ba"],
51
+ "win32-arm64": ["node-v24.20.0-win-arm64.zip", "31c6799744de8a54601643098040c68c3697e56c94e407d61d0e5fa5f34191d7"],
52
+ "darwin-arm64": ["node-v24.20.0-darwin-arm64.tar.gz", "40e5607e5ecb3db9192723776da2d75d966260fc74a7a9e731c1bd67dda96bc8"],
53
+ "linux-x64": ["node-v24.20.0-linux-x64.tar.gz", "855d581f8a4eb1a8117e3426de25fe02770592febcfb31369aee1ffbfee9e8ec"],
54
+ "linux-arm64": ["node-v24.20.0-linux-arm64.tar.gz", "3515603e2487879a39bc75716f1a2affd027500c64ba50e845cf72cb33219013"]
55
+ };
56
+ var NPM_INTEGRITY = "uIXokLlBj6FpNUTQX1PmT5pz7BlIN9QlixX+zdaSNHsd0qUXsbDLr50xzY6Sw7cJVr0uzHKDOle0swmPW/p5Qw==";
57
+ async function verifiedDownload(url, path, algorithm, digest, fetchImpl) {
58
+ const response = await fetchImpl(url);
59
+ if (!response.ok) throw new Error(`runtime download failed (${response.status})`);
60
+ const bytes = Buffer.from(await response.arrayBuffer());
61
+ if ((0, import_node_crypto.createHash)(algorithm).update(bytes).digest(algorithm === "sha512" ? "base64" : "hex") !== digest) throw new Error("runtime archive checksum mismatch");
62
+ (0, import_node_fs.writeFileSync)(path, bytes);
63
+ }
64
+ function command(executable, args) {
65
+ const result = (0, import_node_child_process.spawnSync)(executable, args, { encoding: "utf8", windowsHide: true });
66
+ if (result.error || result.status !== 0) throw new Error(`runtime preparation failed: ${result.error?.message ?? result.stderr.trim()}`);
67
+ return result.stdout.trim();
68
+ }
69
+ async function acquireRuntime(dir, fetchImpl = fetch) {
70
+ const platform = `${process.platform}-${process.arch}`;
71
+ const artifact = ARTIFACTS[platform];
72
+ if (!artifact) throw new Error(`managed runtime does not support ${platform}`);
73
+ const runtimeDir = (0, import_node_path.join)(dir, "runtimes");
74
+ (0, import_node_fs.mkdirSync)(runtimeDir, { recursive: true });
75
+ const receipt = (0, import_node_path.join)(runtimeDir, `node-${NODE_VERSION}-npm-${NPM_VERSION}-${platform}.json`);
76
+ if ((0, import_node_fs.existsSync)(receipt)) {
77
+ const runtime2 = JSON.parse((0, import_node_fs.readFileSync)(receipt, "utf8"));
78
+ for (const executable of [runtime2.node, runtime2.npm]) {
79
+ const rel = (0, import_node_path.relative)((0, import_node_fs.realpathSync)(runtimeDir), (0, import_node_fs.realpathSync)(executable));
80
+ if (rel.startsWith("..") || (0, import_node_path.isAbsolute)(rel)) throw new Error("cached runtime escapes owned directory");
81
+ }
82
+ if (command(runtime2.node, ["--version"]) === `v${NODE_VERSION}` && command(runtime2.node, [runtime2.npm, "--version"]) === NPM_VERSION) return runtime2;
83
+ throw new Error("cached runtime validation failed");
84
+ }
85
+ const root = (0, import_node_fs.mkdtempSync)((0, import_node_path.join)(runtimeDir, "runtime-"));
86
+ const archive = (0, import_node_path.join)(root, artifact[0]);
87
+ await verifiedDownload(`https://nodejs.org/dist/v${NODE_VERSION}/${artifact[0]}`, archive, "sha256", artifact[1], fetchImpl);
88
+ const tar = process.platform === "win32" ? (0, import_node_path.join)(process.env.SystemRoot ?? "C:/Windows", "System32", "tar.exe") : "/usr/bin/tar";
89
+ command(tar, ["-xf", archive, "-C", root]);
90
+ const unpacked = (0, import_node_path.join)(root, artifact[0].replace(/\.(zip|tar\.gz)$/, ""));
91
+ const node = (0, import_node_path.join)(unpacked, process.platform === "win32" ? "node.exe" : "bin/node");
92
+ const npmArchive = (0, import_node_path.join)(root, "npm.tgz");
93
+ await verifiedDownload(`https://registry.npmjs.org/npm/-/npm-${NPM_VERSION}.tgz`, npmArchive, "sha512", NPM_INTEGRITY, fetchImpl);
94
+ const npmRoot = (0, import_node_path.join)(root, "npm");
95
+ (0, import_node_fs.mkdirSync)(npmRoot);
96
+ command(tar, ["-xf", npmArchive, "-C", npmRoot]);
97
+ const installedNpm = (0, import_node_path.join)(unpacked, process.platform === "win32" ? "node_modules/npm" : "lib/node_modules/npm");
98
+ if ((0, import_node_fs.existsSync)(installedNpm)) (0, import_node_fs.renameSync)(installedNpm, (0, import_node_path.join)(root, "npm-bundled"));
99
+ (0, import_node_fs.renameSync)((0, import_node_path.join)(npmRoot, "package"), installedNpm);
100
+ const npm = (0, import_node_path.join)(installedNpm, "bin/npm-cli.js");
101
+ if (command(node, ["--version"]) !== `v${NODE_VERSION}` || command(node, [npm, "--version"]) !== NPM_VERSION) throw new Error("downloaded runtime validation failed");
102
+ const runtime = { node, npm };
103
+ const temporary = `${receipt}.${(0, import_node_crypto.randomUUID)()}.tmp`;
104
+ try {
105
+ (0, import_node_fs.writeFileSync)(temporary, JSON.stringify(runtime), { flag: "wx", mode: 384, flush: true });
106
+ (0, import_node_fs.renameSync)(temporary, receipt);
107
+ } finally {
108
+ (0, import_node_fs.rmSync)(temporary, { force: true });
109
+ }
110
+ return runtime;
111
+ }
112
+
113
+ // src/store.ts
114
+ var import_node_fs2 = require("node:fs");
115
+ var import_node_crypto2 = require("node:crypto");
116
+ var import_node_os = require("node:os");
117
+ var import_node_path2 = require("node:path");
118
+ function defaultProductDir(product) {
119
+ if (process.platform === "win32") {
120
+ const base = process.env.LOCALAPPDATA ?? (0, import_node_path2.join)((0, import_node_os.tmpdir)(), "launcher-fallback");
121
+ return (0, import_node_path2.join)(base, product);
122
+ }
123
+ const home = process.env.HOME ?? (0, import_node_os.tmpdir)();
124
+ return (0, import_node_path2.join)(home, `.${product}`);
125
+ }
126
+ function resolveProductDir(product, explicit) {
127
+ return explicit ?? process.env.LAUNCHER_DIR ?? defaultProductDir(product);
128
+ }
129
+ function tokensPath(dir) {
130
+ return (0, import_node_path2.join)(dir, "tokens.json");
131
+ }
132
+ function statePath(dir) {
133
+ return (0, import_node_path2.join)(dir, "state.json");
134
+ }
135
+ function payloadDir(dir) {
136
+ const acquired = readState(dir)?.acquired;
137
+ if (acquired) return (0, import_node_path2.join)(dir, "candidates", acquired.candidate, "payload");
138
+ return (0, import_node_path2.join)(dir, "payload");
139
+ }
140
+ function readTokens(dir) {
141
+ try {
142
+ const data = JSON.parse((0, import_node_fs2.readFileSync)(tokensPath(dir), "utf8"));
143
+ if (typeof data.accessToken !== "string" || !data.accessToken) return null;
144
+ if (typeof data.expiresAt !== "number" || !Number.isFinite(data.expiresAt)) return null;
145
+ const tokens = { accessToken: data.accessToken, expiresAt: data.expiresAt };
146
+ if (typeof data.refreshToken === "string" && data.refreshToken) tokens.refreshToken = data.refreshToken;
147
+ if (typeof data.clientId === "string" && data.clientId) tokens.clientId = data.clientId;
148
+ return tokens;
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+ function writeTokens(dir, tokens) {
154
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
155
+ try {
156
+ (0, import_node_fs2.writeFileSync)(tokensPath(dir), `${JSON.stringify(tokens)}
157
+ `, { mode: 384 });
158
+ } catch {
159
+ (0, import_node_fs2.writeFileSync)(tokensPath(dir), `${JSON.stringify(tokens)}
160
+ `);
161
+ }
162
+ }
163
+ function clearTokens(dir) {
164
+ (0, import_node_fs2.rmSync)(tokensPath(dir), { force: true });
165
+ }
166
+ function readState(dir) {
167
+ try {
168
+ const data = JSON.parse((0, import_node_fs2.readFileSync)(statePath(dir), "utf8"));
169
+ if (typeof data.version !== "string" || !data.version) return null;
170
+ const state = { version: data.version, updatedAt: typeof data.updatedAt === "string" ? data.updatedAt : "" };
171
+ if (data.acquired) {
172
+ if (!/^[a-zA-Z0-9_-]+$/.test(data.acquired.candidate) || typeof data.acquired.node !== "string" || typeof data.acquired.entry !== "string") return null;
173
+ state.acquired = data.acquired;
174
+ }
175
+ return state;
176
+ } catch {
177
+ return null;
178
+ }
179
+ }
180
+ function writeState(dir, state) {
181
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
182
+ const temporary = `${statePath(dir)}.${(0, import_node_crypto2.randomUUID)()}.tmp`;
183
+ try {
184
+ (0, import_node_fs2.writeFileSync)(temporary, `${JSON.stringify(state, null, 2)}
185
+ `, { flag: "wx", mode: 384, flush: true });
186
+ (0, import_node_fs2.renameSync)(temporary, statePath(dir));
187
+ } finally {
188
+ (0, import_node_fs2.rmSync)(temporary, { force: true });
189
+ }
190
+ }
191
+ function wipeProductDir(dir) {
192
+ const payload = payloadDir(dir);
193
+ (0, import_node_fs2.rmSync)(tokensPath(dir), { force: true });
194
+ (0, import_node_fs2.rmSync)(statePath(dir), { force: true });
195
+ (0, import_node_fs2.rmSync)(payload, { force: true, recursive: true });
196
+ }
197
+
198
+ // src/acquisition.ts
199
+ function safePath(value) {
200
+ return typeof value === "string" && value.length > 0 && !/[\\:\0]/.test(value) && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
201
+ }
202
+ function argumentsValid(value) {
203
+ return Array.isArray(value) && value.every((arg) => typeof arg === "string" && !arg.includes("\0") && (!arg.includes("$") || arg === "$prefix" || arg === "$version"));
204
+ }
205
+ function readAcquisition(payload) {
206
+ const metadata = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path3.join)(payload, "payload.json"), "utf8"));
207
+ if (metadata.acquisition === void 0) return null;
208
+ const a = metadata.acquisition;
209
+ if (!a || a.schema !== 1 || !/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/.test(a.package) || !safePath(a.archive) || !a.archive.endsWith(".tgz") || !safePath(a.convergeEntry) || !safePath(a.rollbackEntry) || !safePath(a.runEntry) || !argumentsValid(a.convergeArgs) || !argumentsValid(a.rollbackArgs) || !argumentsValid(a.repairArgs) || !Array.isArray(a.platforms) || !a.platforms.length || a.platforms.some((p) => !["win32-x64", "win32-arm64", "darwin-arm64", "linux-x64", "linux-arm64"].includes(p))) {
210
+ throw new Error("invalid signed acquisition declaration");
211
+ }
212
+ return a;
213
+ }
214
+ function runtimeEnvironment(node, inherited, platform = process.platform) {
215
+ const env = { ...inherited };
216
+ let current = env.PATH ?? "";
217
+ if (platform === "win32") {
218
+ const keys = Object.keys(env).filter((key) => key.toLowerCase() === "path").sort();
219
+ current = (keys.length ? env[keys[0]] : "") ?? "";
220
+ for (const key of keys) delete env[key];
221
+ }
222
+ env.PATH = `${(0, import_node_path3.dirname)(node)}${platform === "win32" ? ";" : ":"}${current}`;
223
+ return env;
224
+ }
225
+ function lockInstallation(dir) {
226
+ (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
227
+ const path = (0, import_node_path3.join)(dir, "installation.lock");
228
+ const owner = `${process.pid}:${(0, import_node_crypto3.randomUUID)()}`;
229
+ try {
230
+ (0, import_node_fs3.writeFileSync)(path, owner, { flag: "wx", mode: 384 });
231
+ } catch (error) {
232
+ if (error.code !== "EEXIST") throw error;
233
+ const recovery = `${path}.recovery`;
234
+ try {
235
+ (0, import_node_fs3.writeFileSync)(recovery, owner, { flag: "wx", mode: 384, flush: true });
236
+ } catch {
237
+ throw new Error("installation lock recovery is already pending");
238
+ }
239
+ try {
240
+ const previous = (0, import_node_fs3.readFileSync)(path, "utf8");
241
+ const pid = Number(previous.split(":")[0]);
242
+ if (!Number.isInteger(pid) || pid <= 0) throw new Error("installation lock is malformed; recovery required");
243
+ try {
244
+ process.kill(pid, 0);
245
+ throw new Error("another installer is running");
246
+ } catch (probe) {
247
+ if (probe.code !== "ESRCH") throw probe;
248
+ }
249
+ (0, import_node_fs3.unlinkSync)(path);
250
+ (0, import_node_fs3.writeFileSync)(path, owner, { flag: "wx", mode: 384, flush: true });
251
+ } finally {
252
+ (0, import_node_fs3.unlinkSync)(recovery);
253
+ }
254
+ }
255
+ let released = false;
256
+ return () => {
257
+ if (!released && (0, import_node_fs3.readFileSync)(path, "utf8") === owner) (0, import_node_fs3.unlinkSync)(path);
258
+ released = true;
259
+ };
260
+ }
261
+ function pendingPath(dir) {
262
+ return (0, import_node_path3.join)(dir, "acquisition-pending.json");
263
+ }
264
+ function candidateRoot(dir, candidate) {
265
+ if (!/^[a-zA-Z0-9_-]+$/.test(candidate)) throw new Error("invalid acquisition candidate");
266
+ return (0, import_node_path3.join)(dir, "candidates", candidate);
267
+ }
268
+ function packageRoot(prefix, acquisition) {
269
+ return (0, import_node_path3.join)(prefix, "node_modules", acquisition.package);
270
+ }
271
+ function entry(root, path) {
272
+ const actual = (0, import_node_fs3.realpathSync)((0, import_node_path3.join)(root, path));
273
+ const rel = (0, import_node_path3.relative)((0, import_node_fs3.realpathSync)(root), actual);
274
+ if (rel.startsWith("..") || (0, import_node_path3.isAbsolute)(rel)) throw new Error("acquired entry escapes package");
275
+ return actual;
276
+ }
277
+ function argv(node, root, file, args, prefix, version) {
278
+ return [node, entry(root, file), ...args.map((arg) => arg === "$prefix" ? prefix : arg === "$version" ? version : arg)];
279
+ }
280
+ function acquisitionRepairCommand(dir, version) {
281
+ const state = readState(dir);
282
+ if (!state?.acquired || state.version !== version) throw new Error("installed acquisition selection is missing");
283
+ const root = candidateRoot(dir, state.acquired.candidate);
284
+ const acquisition = readAcquisition((0, import_node_path3.join)(root, "payload"));
285
+ if (!acquisition) throw new Error("installed acquisition declaration is missing");
286
+ const prefix = (0, import_node_path3.join)(root, "prefix");
287
+ const productRoot = packageRoot(prefix, acquisition);
288
+ const pkg = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path3.join)(productRoot, "package.json"), "utf8"));
289
+ if (pkg.name !== acquisition.package || pkg.version !== version) throw new Error("installed package identity does not match signed release");
290
+ return argv((0, import_node_fs3.realpathSync)(state.acquired.node), productRoot, acquisition.runEntry, acquisition.repairArgs, prefix, version);
291
+ }
292
+ async function recoverAcquisition(dir, run2, env) {
293
+ if (!(0, import_node_fs3.existsSync)(pendingPath(dir))) return;
294
+ const pending = JSON.parse((0, import_node_fs3.readFileSync)(pendingPath(dir), "utf8"));
295
+ if (pending.schema !== 1 || typeof pending.version !== "string" || typeof pending.node !== "string") throw new Error("invalid pending acquisition receipt");
296
+ const root = candidateRoot(dir, pending.candidate);
297
+ if (readState(dir)?.acquired?.candidate === pending.candidate) {
298
+ (0, import_node_fs3.unlinkSync)(pendingPath(dir));
299
+ return;
300
+ }
301
+ const acquisition = readAcquisition((0, import_node_path3.join)(root, "payload"));
302
+ if (!acquisition) throw new Error("pending acquisition lost its signed declaration");
303
+ const prefix = (0, import_node_path3.join)(root, "prefix");
304
+ const command2 = argv(pending.node, packageRoot(prefix, acquisition), acquisition.rollbackEntry, acquisition.rollbackArgs, prefix, pending.version);
305
+ if (!await run2(command2, root, runtimeEnvironment(pending.node, env))) throw new Error("installation recovery is pending; product rollback did not finish");
306
+ if (pending.previous) writeState(dir, pending.previous);
307
+ else if ((0, import_node_fs3.existsSync)(statePath(dir))) (0, import_node_fs3.unlinkSync)(statePath(dir));
308
+ (0, import_node_fs3.unlinkSync)(pendingPath(dir));
309
+ }
310
+ async function installAcquisition(dir, candidate, version, acquisition, options) {
311
+ if (!acquisition.platforms.includes(`${process.platform}-${process.arch}`)) throw new Error("this product does not support this platform");
312
+ const root = candidateRoot(dir, candidate);
313
+ const payload = (0, import_node_path3.join)(root, "payload");
314
+ const prefix = (0, import_node_path3.join)(root, "prefix");
315
+ (0, import_node_fs3.mkdirSync)(prefix);
316
+ const runtime = await (options.runtime ?? acquireRuntime)(dir, options.fetchImpl);
317
+ const env = runtimeEnvironment(runtime.node, options.env);
318
+ const npmEnv = { ...env };
319
+ for (const key of Object.keys(npmEnv)) {
320
+ if (/^npm_config_/i.test(key) || /^(NPM_TOKEN|NODE_AUTH_TOKEN|MM_INSTALLER_TOKEN)$/i.test(key)) delete npmEnv[key];
321
+ }
322
+ const npmrc = (0, import_node_path3.join)(root, "public.npmrc");
323
+ const globalrc = (0, import_node_path3.join)(root, "global.npmrc");
324
+ (0, import_node_fs3.writeFileSync)(npmrc, "registry=https://registry.npmjs.org/\n");
325
+ (0, import_node_fs3.writeFileSync)(globalrc, "");
326
+ const install = [
327
+ runtime.node,
328
+ runtime.npm,
329
+ "install",
330
+ "--prefix",
331
+ prefix,
332
+ "--ignore-scripts",
333
+ "--no-audit",
334
+ "--no-fund",
335
+ "--userconfig",
336
+ npmrc,
337
+ "--globalconfig",
338
+ globalrc,
339
+ "--registry",
340
+ "https://registry.npmjs.org/",
341
+ (0, import_node_path3.resolve)(payload, acquisition.archive)
342
+ ];
343
+ if (!await options.run(install, prefix, npmEnv)) throw new Error("local product archive installation failed");
344
+ const productRoot = packageRoot(prefix, acquisition);
345
+ const pkg = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path3.join)(productRoot, "package.json"), "utf8"));
346
+ if (pkg.name !== acquisition.package || pkg.version !== version) throw new Error("acquired package identity does not match signed release");
347
+ const runEntry = entry(productRoot, acquisition.runEntry);
348
+ const convergence = argv(runtime.node, productRoot, acquisition.convergeEntry, acquisition.convergeArgs, prefix, version);
349
+ entry(productRoot, acquisition.rollbackEntry);
350
+ const pending = { schema: 1, candidate, version, node: runtime.node, previous: readState(dir) };
351
+ (0, import_node_fs3.writeFileSync)(pendingPath(dir), JSON.stringify(pending), { flag: "wx", mode: 384, flush: true });
352
+ try {
353
+ if (!await options.run(convergence, root, env)) throw new Error("product convergence did not finish");
354
+ (options.commit ?? writeState)(dir, { version, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), acquired: { candidate, node: runtime.node, entry: runEntry } });
355
+ } catch (error) {
356
+ await recoverAcquisition(dir, options.run, env);
357
+ throw error;
358
+ }
359
+ (0, import_node_fs3.unlinkSync)(pendingPath(dir));
360
+ }
361
+
362
+ // src/index.ts
363
+ var import_node_child_process4 = require("node:child_process");
364
+ var import_node_fs11 = require("node:fs");
37
365
  var import_node_os4 = require("node:os");
38
- var import_node_path4 = require("node:path");
366
+ var import_node_path6 = require("node:path");
39
367
  var import_node_url2 = require("node:url");
40
368
 
41
369
  // ../face/src/face.ts
42
- var import_node_fs = require("node:fs");
370
+ var import_node_fs4 = require("node:fs");
43
371
 
44
372
  // ../face/src/products.ts
45
373
  var PRODUCTS = Object.freeze({
@@ -158,7 +486,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
158
486
  const record = { v: PROGRESS_PROTOCOL, step: stripColor(title), state: kind };
159
487
  if (typeof measure === "number") record.ms = Math.max(0, Math.round(measure * 1e3));
160
488
  try {
161
- (0, import_node_fs.writeSync)(progressFd, `${JSON.stringify(record)}
489
+ (0, import_node_fs4.writeSync)(progressFd, `${JSON.stringify(record)}
162
490
  `);
163
491
  return true;
164
492
  } catch {
@@ -225,7 +553,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
225
553
  var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
226
554
 
227
555
  // ../face/src/spinner.ts
228
- var import_node_fs2 = require("node:fs");
556
+ var import_node_fs5 = require("node:fs");
229
557
  var import_node_worker_threads = require("node:worker_threads");
230
558
  var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
231
559
  var WORKER_SOURCE = `
@@ -263,8 +591,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
263
591
  const write = (text) => {
264
592
  if (stream) stream.write(text);
265
593
  else {
266
- (0, import_node_fs2.writeSync)(2, text);
267
- if (transcriptPath) (0, import_node_fs2.appendFileSync)(transcriptPath, `${JSON.stringify({ channel: "spinner", text })}
594
+ (0, import_node_fs5.writeSync)(2, text);
595
+ if (transcriptPath) (0, import_node_fs5.appendFileSync)(transcriptPath, `${JSON.stringify({ channel: "spinner", text })}
268
596
  `);
269
597
  }
270
598
  };
@@ -337,10 +665,10 @@ var SPINNER_FRAMES = Object.freeze([...FRAMES]);
337
665
  var ALLOWED = new Set(Object.values(GLYPH));
338
666
 
339
667
  // ../face/src/run.ts
340
- var import_node_fs4 = require("node:fs");
668
+ var import_node_fs7 = require("node:fs");
341
669
 
342
670
  // ../face/src/outcome.ts
343
- var import_node_fs3 = require("node:fs");
671
+ var import_node_fs6 = require("node:fs");
344
672
  var counts = ["total", "updated", "failed"];
345
673
  var strings = ["version", "retry", "detail", "logPath"];
346
674
  var flags = ["dryRun", "installed", "deferred", "operationFailed"];
@@ -362,12 +690,12 @@ function validateInstallerOutcome(value) {
362
690
  return { ...facts };
363
691
  }
364
692
  function writeInstallerOutcome(path, value) {
365
- (0, import_node_fs3.writeFileSync)(path, JSON.stringify(validateInstallerOutcome(value)), { encoding: "utf8", mode: 384 });
693
+ (0, import_node_fs6.writeFileSync)(path, JSON.stringify(validateInstallerOutcome(value)), { encoding: "utf8", mode: 384 });
366
694
  }
367
695
  function readInstallerOutcome(path) {
368
696
  let text;
369
697
  try {
370
- text = (0, import_node_fs3.readFileSync)(path, "utf8");
698
+ text = (0, import_node_fs6.readFileSync)(path, "utf8");
371
699
  } catch (error) {
372
700
  if (error.code === "ENOENT") return void 0;
373
701
  throw error;
@@ -459,7 +787,7 @@ function createInstallerRun(value, options = {}) {
459
787
  if (!text) return;
460
788
  write(text, channel);
461
789
  if (env.MM_FACE_TRANSCRIPT) {
462
- (0, import_node_fs4.appendFileSync)(env.MM_FACE_TRANSCRIPT, `${JSON.stringify({ channel, text: recorded })}
790
+ (0, import_node_fs7.appendFileSync)(env.MM_FACE_TRANSCRIPT, `${JSON.stringify({ channel, text: recorded })}
463
791
  `, "utf8");
464
792
  }
465
793
  };
@@ -594,17 +922,17 @@ function createInstallerRun(value, options = {}) {
594
922
  }
595
923
 
596
924
  // src/autoupdate.ts
597
- var import_node_child_process = require("node:child_process");
598
- var import_node_fs5 = require("node:fs");
599
- var import_node_os = require("node:os");
600
- var import_node_path = require("node:path");
925
+ var import_node_child_process2 = require("node:child_process");
926
+ var import_node_fs8 = require("node:fs");
927
+ var import_node_os2 = require("node:os");
928
+ var import_node_path4 = require("node:path");
601
929
  function schedulePlatform(override) {
602
930
  const platform = override ?? process.platform;
603
931
  if (platform === "win32" || platform === "darwin" || platform === "linux") return platform;
604
932
  return null;
605
933
  }
606
- function defaultExec(command, args) {
607
- const result = (0, import_node_child_process.spawnSync)(command, args, { encoding: "utf8", windowsHide: true });
934
+ function defaultExec(command2, args) {
935
+ const result = (0, import_node_child_process2.spawnSync)(command2, args, { encoding: "utf8", windowsHide: true });
608
936
  if (result.error) return { code: 1, stdout: "", stderr: result.error.message };
609
937
  return {
610
938
  code: typeof result.status === "number" ? result.status : 1,
@@ -614,8 +942,8 @@ function defaultExec(command, args) {
614
942
  }
615
943
  function homeOf(options) {
616
944
  if (options.homeDir) return options.homeDir;
617
- if (process.platform === "win32") return process.env.USERPROFILE ?? (0, import_node_os.tmpdir)();
618
- return process.env.HOME ?? (0, import_node_os.tmpdir)();
945
+ if (process.platform === "win32") return process.env.USERPROFILE ?? (0, import_node_os2.tmpdir)();
946
+ return process.env.HOME ?? (0, import_node_os2.tmpdir)();
619
947
  }
620
948
  function scheduleName(config) {
621
949
  return `${config.binName} autoupdate`;
@@ -626,13 +954,13 @@ function scheduleLabel(config) {
626
954
  function quoteWindows(arg) {
627
955
  return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
628
956
  }
629
- function enableSchedule(config, command, options = {}) {
957
+ function enableSchedule(config, command2, options = {}) {
630
958
  const platform = schedulePlatform(options.platform);
631
959
  if (!platform) throw new Error(`autoupdate is not supported on ${options.platform ?? process.platform}.`);
632
960
  const exec = options.exec ?? defaultExec;
633
961
  const home = homeOf(options);
634
962
  if (platform === "win32") {
635
- const taskLine = command.map(quoteWindows).join(" ");
963
+ const taskLine = command2.map(quoteWindows).join(" ");
636
964
  const result2 = exec("schtasks", ["/Create", "/TN", scheduleName(config), "/TR", taskLine, "/SC", "HOURLY", "/IT", "/F"]);
637
965
  if (result2.code !== 0) {
638
966
  throw new Error(`could not turn autoupdate on: ${firstLine(result2.stdout || result2.stderr) || `exit ${result2.code}`}`);
@@ -641,10 +969,10 @@ function enableSchedule(config, command, options = {}) {
641
969
  }
642
970
  if (platform === "darwin") {
643
971
  const label2 = scheduleLabel(config);
644
- const dir2 = (0, import_node_path.join)(home, "Library", "LaunchAgents");
645
- (0, import_node_fs5.mkdirSync)(dir2, { recursive: true });
646
- const plist = (0, import_node_path.join)(dir2, `${label2}.plist`);
647
- (0, import_node_fs5.writeFileSync)(plist, darwinPlist(label2, command));
972
+ const dir2 = (0, import_node_path4.join)(home, "Library", "LaunchAgents");
973
+ (0, import_node_fs8.mkdirSync)(dir2, { recursive: true });
974
+ const plist = (0, import_node_path4.join)(dir2, `${label2}.plist`);
975
+ (0, import_node_fs8.writeFileSync)(plist, darwinPlist(label2, command2));
648
976
  exec("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${label2}`]);
649
977
  const result2 = exec("launchctl", ["bootstrap", `gui/${process.getuid?.() ?? 501}`, plist]);
650
978
  if (result2.code !== 0) {
@@ -653,10 +981,10 @@ function enableSchedule(config, command, options = {}) {
653
981
  return;
654
982
  }
655
983
  const label = scheduleLabel(config);
656
- const dir = (0, import_node_path.join)(home, ".config", "systemd", "user");
657
- (0, import_node_fs5.mkdirSync)(dir, { recursive: true });
658
- (0, import_node_fs5.writeFileSync)((0, import_node_path.join)(dir, `${label}.service`), linuxService(command));
659
- (0, import_node_fs5.writeFileSync)((0, import_node_path.join)(dir, `${label}.timer`), linuxTimer(label));
984
+ const dir = (0, import_node_path4.join)(home, ".config", "systemd", "user");
985
+ (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
986
+ (0, import_node_fs8.writeFileSync)((0, import_node_path4.join)(dir, `${label}.service`), linuxService(command2));
987
+ (0, import_node_fs8.writeFileSync)((0, import_node_path4.join)(dir, `${label}.timer`), linuxTimer(label));
660
988
  const reload = exec("systemctl", ["--user", "daemon-reload"]);
661
989
  if (reload.code !== 0) {
662
990
  throw new Error(`could not turn autoupdate on: ${firstLine(reload.stderr || reload.stdout) || `exit ${reload.code}`}`);
@@ -681,14 +1009,14 @@ function disableSchedule(config, options = {}) {
681
1009
  if (platform === "darwin") {
682
1010
  const label2 = scheduleLabel(config);
683
1011
  exec("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${label2}`]);
684
- (0, import_node_fs5.rmSync)((0, import_node_path.join)(home, "Library", "LaunchAgents", `${label2}.plist`), { force: true });
1012
+ (0, import_node_fs8.rmSync)((0, import_node_path4.join)(home, "Library", "LaunchAgents", `${label2}.plist`), { force: true });
685
1013
  return;
686
1014
  }
687
1015
  const label = scheduleLabel(config);
688
- const dir = (0, import_node_path.join)(home, ".config", "systemd", "user");
1016
+ const dir = (0, import_node_path4.join)(home, ".config", "systemd", "user");
689
1017
  exec("systemctl", ["--user", "disable", "--now", `${label}.timer`]);
690
- (0, import_node_fs5.rmSync)((0, import_node_path.join)(dir, `${label}.service`), { force: true });
691
- (0, import_node_fs5.rmSync)((0, import_node_path.join)(dir, `${label}.timer`), { force: true });
1018
+ (0, import_node_fs8.rmSync)((0, import_node_path4.join)(dir, `${label}.service`), { force: true });
1019
+ (0, import_node_fs8.rmSync)((0, import_node_path4.join)(dir, `${label}.timer`), { force: true });
692
1020
  }
693
1021
  function querySchedule(config, options = {}) {
694
1022
  const platform = schedulePlatform(options.platform);
@@ -706,20 +1034,20 @@ function querySchedule(config, options = {}) {
706
1034
  return state2;
707
1035
  }
708
1036
  if (platform === "darwin") {
709
- const plist = (0, import_node_path.join)(home, "Library", "LaunchAgents", `${scheduleLabel(config)}.plist`);
710
- if (!(0, import_node_fs5.existsSync)(plist)) return { supported: true, enabled: false };
1037
+ const plist = (0, import_node_path4.join)(home, "Library", "LaunchAgents", `${scheduleLabel(config)}.plist`);
1038
+ if (!(0, import_node_fs8.existsSync)(plist)) return { supported: true, enabled: false };
711
1039
  return { supported: true, enabled: true, cadence: "hourly" };
712
1040
  }
713
- const timer = (0, import_node_path.join)(home, ".config", "systemd", "user", `${scheduleLabel(config)}.timer`);
714
- if (!(0, import_node_fs5.existsSync)(timer)) return { supported: true, enabled: false };
1041
+ const timer = (0, import_node_path4.join)(home, ".config", "systemd", "user", `${scheduleLabel(config)}.timer`);
1042
+ if (!(0, import_node_fs8.existsSync)(timer)) return { supported: true, enabled: false };
715
1043
  const state = { supported: true, enabled: true, cadence: "hourly" };
716
1044
  const shown = exec("systemctl", ["--user", "show", `${scheduleLabel(config)}.service`, "-p", "ExecMainStartTimestamp", "--value"]);
717
1045
  const stamp = (shown.stdout ?? "").trim();
718
1046
  if (shown.code === 0 && stamp && stamp !== "n/a") state.lastRun = stamp;
719
1047
  return state;
720
1048
  }
721
- function darwinPlist(label, command) {
722
- const args = command.map((arg) => ` <string>${xmlEscape(arg)}</string>`).join("\n");
1049
+ function darwinPlist(label, command2) {
1050
+ const args = command2.map((arg) => ` <string>${xmlEscape(arg)}</string>`).join("\n");
723
1051
  return `<?xml version="1.0" encoding="UTF-8"?>
724
1052
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
725
1053
  <plist version="1.0">
@@ -736,8 +1064,8 @@ ${args}
736
1064
  </plist>
737
1065
  `;
738
1066
  }
739
- function linuxService(command) {
740
- const line = command.map((arg) => /[\s"\\]/.test(arg) ? `"${arg.replace(/(["\\])/g, "\\$1")}"` : arg).join(" ");
1067
+ function linuxService(command2) {
1068
+ const line = command2.map((arg) => /[\s"\\]/.test(arg) ? `"${arg.replace(/(["\\])/g, "\\$1")}"` : arg).join(" ");
741
1069
  return `[Unit]
742
1070
  Description=${"Hourly update check"}
743
1071
  [Service]
@@ -769,7 +1097,7 @@ function firstLine(text) {
769
1097
  }
770
1098
 
771
1099
  // src/config.ts
772
- var import_node_fs6 = require("node:fs");
1100
+ var import_node_fs9 = require("node:fs");
773
1101
  var import_node_sea = require("node:sea");
774
1102
 
775
1103
  // src/module-url.ts
@@ -792,10 +1120,10 @@ function loadProductConfig(options = {}) {
792
1120
  const explicit = options.configPath ?? process.env.LAUNCHER_CONFIG;
793
1121
  const devFallback = new URL("../config/product.template.json", moduleUrl());
794
1122
  if (explicit) {
795
- return parseProductConfig((0, import_node_fs6.readFileSync)(explicit, "utf8"));
1123
+ return parseProductConfig((0, import_node_fs9.readFileSync)(explicit, "utf8"));
796
1124
  }
797
1125
  try {
798
- return parseProductConfig((0, import_node_fs6.readFileSync)(devFallback, "utf8"));
1126
+ return parseProductConfig((0, import_node_fs9.readFileSync)(devFallback, "utf8"));
799
1127
  } catch {
800
1128
  }
801
1129
  try {
@@ -850,14 +1178,14 @@ function field(record, key) {
850
1178
  }
851
1179
 
852
1180
  // src/login-github.ts
853
- var import_node_child_process2 = require("node:child_process");
854
- var realSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
1181
+ var import_node_child_process3 = require("node:child_process");
1182
+ var realSleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
855
1183
  function openBrowser(url) {
856
1184
  if (process.env.LAUNCHER_NO_OPEN === "1") return;
857
1185
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
858
1186
  const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
859
1187
  try {
860
- const child = (0, import_node_child_process2.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
1188
+ const child = (0, import_node_child_process3.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
861
1189
  child.on("error", () => {
862
1190
  });
863
1191
  child.unref();
@@ -971,7 +1299,7 @@ async function refreshAccessToken(config, refreshToken, fetchImpl = fetch) {
971
1299
  }
972
1300
 
973
1301
  // src/login-google.ts
974
- var import_node_crypto = require("node:crypto");
1302
+ var import_node_crypto4 = require("node:crypto");
975
1303
  var import_node_http = require("node:http");
976
1304
  var b64url = (bytes) => bytes.toString("base64url");
977
1305
  async function loginGoogle(options) {
@@ -979,7 +1307,7 @@ async function loginGoogle(options) {
979
1307
  const server = options.host.replace(/\/+$/, "");
980
1308
  const timeoutMs = options.timeoutMs ?? 5 * 6e4;
981
1309
  const listener = (0, import_node_http.createServer)();
982
- await new Promise((resolve2) => listener.listen(0, "127.0.0.1", resolve2));
1310
+ await new Promise((resolve3) => listener.listen(0, "127.0.0.1", resolve3));
983
1311
  const port = listener.address().port;
984
1312
  const redirectUri = `http://127.0.0.1:${port}/callback`;
985
1313
  try {
@@ -993,9 +1321,9 @@ async function loginGoogle(options) {
993
1321
  if (typeof clientId !== "string" || !clientId) {
994
1322
  throw new Error("the sign-in server returned a malformed registration");
995
1323
  }
996
- const verifier = b64url((0, import_node_crypto.randomBytes)(32));
997
- const challenge = b64url((0, import_node_crypto.createHash)("sha256").update(verifier).digest());
998
- const state = b64url((0, import_node_crypto.randomBytes)(16));
1324
+ const verifier = b64url((0, import_node_crypto4.randomBytes)(32));
1325
+ const challenge = b64url((0, import_node_crypto4.createHash)("sha256").update(verifier).digest());
1326
+ const state = b64url((0, import_node_crypto4.randomBytes)(16));
999
1327
  const authorize = new URL(`${server}/oauth/authorize`);
1000
1328
  authorize.search = new URLSearchParams({
1001
1329
  response_type: "code",
@@ -1006,7 +1334,7 @@ async function loginGoogle(options) {
1006
1334
  code_challenge: challenge,
1007
1335
  code_challenge_method: "S256"
1008
1336
  }).toString();
1009
- const code = await new Promise((resolve2, reject) => {
1337
+ const code = await new Promise((resolve3, reject) => {
1010
1338
  const timer = setTimeout(() => reject(new Error("sign-in timed out \u2014 run login again.")), timeoutMs);
1011
1339
  listener.on("request", (req, res) => {
1012
1340
  const url = new URL(req.url ?? "/", redirectUri);
@@ -1024,7 +1352,7 @@ async function loginGoogle(options) {
1024
1352
  }
1025
1353
  res.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end(page("Signed in.", "You can close this tab and go back to the terminal."));
1026
1354
  clearTimeout(timer);
1027
- resolve2(received);
1355
+ resolve3(received);
1028
1356
  });
1029
1357
  const print = options.print ?? ((line) => process.stderr.write(`${line}
1030
1358
  `));
@@ -1084,10 +1412,10 @@ function page(title, body) {
1084
1412
  }
1085
1413
 
1086
1414
  // src/payload.ts
1087
- var import_node_crypto2 = require("node:crypto");
1088
- var import_node_fs7 = require("node:fs");
1089
- var import_node_os2 = require("node:os");
1090
- var import_node_path2 = require("node:path");
1415
+ var import_node_crypto5 = require("node:crypto");
1416
+ var import_node_fs10 = require("node:fs");
1417
+ var import_node_os3 = require("node:os");
1418
+ var import_node_path5 = require("node:path");
1091
1419
 
1092
1420
  // src/canonical.ts
1093
1421
  function canonicalJson(value) {
@@ -1104,7 +1432,7 @@ function encode(value) {
1104
1432
  return JSON.stringify(value);
1105
1433
  }
1106
1434
  if (Array.isArray(value)) {
1107
- return `[${value.map((entry) => encode(entry)).join(",")}]`;
1435
+ return `[${value.map((entry2) => encode(entry2)).join(",")}]`;
1108
1436
  }
1109
1437
  if (typeof value === "object") {
1110
1438
  const record = value;
@@ -1139,7 +1467,7 @@ function canonicalManifestBytes(manifest) {
1139
1467
  }
1140
1468
  function ed25519PublicKey(config) {
1141
1469
  const raw = publicKeyBytes(config);
1142
- return (0, import_node_crypto2.createPublicKey)({
1470
+ return (0, import_node_crypto5.createPublicKey)({
1143
1471
  key: { kty: "OKP", crv: "Ed25519", x: raw.toString("base64url") },
1144
1472
  format: "jwk"
1145
1473
  });
@@ -1148,14 +1476,14 @@ function verifyManifest(manifest, signature, config) {
1148
1476
  try {
1149
1477
  const signatureBytes = Buffer.from(signature, "base64");
1150
1478
  if (signatureBytes.length === 0) return false;
1151
- return (0, import_node_crypto2.verify)(null, canonicalManifestBytes(manifest), ed25519PublicKey(config), signatureBytes);
1479
+ return (0, import_node_crypto5.verify)(null, canonicalManifestBytes(manifest), ed25519PublicKey(config), signatureBytes);
1152
1480
  } catch {
1153
1481
  return false;
1154
1482
  }
1155
1483
  }
1156
- function verifyFileBytes(entry, bytes) {
1157
- if (entry.size !== bytes.length) return false;
1158
- return (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex") === entry.sha256.toLowerCase();
1484
+ function verifyFileBytes(entry2, bytes) {
1485
+ if (entry2.size !== bytes.length) return false;
1486
+ return (0, import_node_crypto5.createHash)("sha256").update(bytes).digest("hex") === entry2.sha256.toLowerCase();
1159
1487
  }
1160
1488
  async function readErrorCode(response) {
1161
1489
  try {
@@ -1186,8 +1514,8 @@ function parseManifest(json) {
1186
1514
  if (typeof record.signature !== "string" || !record.signature) {
1187
1515
  throw new Error("the release manifest is unsigned");
1188
1516
  }
1189
- const files = record.files.map((entry) => {
1190
- const file = entry;
1517
+ const files = record.files.map((entry2) => {
1518
+ const file = entry2;
1191
1519
  if (typeof file.path !== "string" || typeof file.sha256 !== "string" || typeof file.size !== "number") {
1192
1520
  throw new Error("the release manifest lists a malformed file");
1193
1521
  }
@@ -1227,101 +1555,31 @@ async function fetchFileBytes(config, accessToken, path, fetchImpl) {
1227
1555
  }
1228
1556
  async function downloadAndUnpack(config, dir, manifest, accessToken, options = {}) {
1229
1557
  const fetchImpl = options.fetchImpl ?? fetch;
1230
- const staging = (0, import_node_path2.join)((0, import_node_os2.tmpdir)(), `launcher-${config.product}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`);
1231
- (0, import_node_fs7.mkdirSync)(staging, { recursive: true });
1558
+ const staging = (0, import_node_path5.join)((0, import_node_os3.tmpdir)(), `launcher-${config.product}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`);
1559
+ (0, import_node_fs10.mkdirSync)(staging, { recursive: true });
1232
1560
  try {
1233
- for (const entry of manifest.files) {
1234
- const bytes = await fetchFileBytes(config, accessToken, entry.path, fetchImpl);
1235
- if (!verifyFileBytes(entry, bytes)) {
1236
- throw new Error(`file ${entry.path} failed its checksum \u2014 refusing to install anything.`);
1561
+ for (const entry2 of manifest.files) {
1562
+ const bytes = await fetchFileBytes(config, accessToken, entry2.path, fetchImpl);
1563
+ if (!verifyFileBytes(entry2, bytes)) {
1564
+ throw new Error(`file ${entry2.path} failed its checksum \u2014 refusing to install anything.`);
1237
1565
  }
1238
- const dest = (0, import_node_path2.join)(staging, entry.path);
1239
- (0, import_node_fs7.mkdirSync)((0, import_node_path2.dirname)(dest), { recursive: true });
1240
- (0, import_node_fs7.writeFileSync)(dest, bytes);
1566
+ const dest = (0, import_node_path5.join)(staging, entry2.path);
1567
+ (0, import_node_fs10.mkdirSync)((0, import_node_path5.dirname)(dest), { recursive: true });
1568
+ (0, import_node_fs10.writeFileSync)(dest, bytes);
1241
1569
  }
1242
- const target = (0, import_node_path2.join)(dir, "payload");
1243
- (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
1244
- (0, import_node_fs7.rmSync)(target, { force: true, recursive: true });
1245
- (0, import_node_fs7.renameSync)(staging, target);
1570
+ const target = (0, import_node_path5.join)(dir, "payload");
1571
+ (0, import_node_fs10.mkdirSync)(dir, { recursive: true });
1572
+ (0, import_node_fs10.rmSync)(target, { force: true, recursive: true });
1573
+ (0, import_node_fs10.renameSync)(staging, target);
1246
1574
  } catch (error) {
1247
- (0, import_node_fs7.rmSync)(staging, { force: true, recursive: true });
1575
+ (0, import_node_fs10.rmSync)(staging, { force: true, recursive: true });
1248
1576
  throw error;
1249
1577
  }
1250
1578
  return manifest.version;
1251
1579
  }
1252
1580
 
1253
- // src/store.ts
1254
- var import_node_fs8 = require("node:fs");
1255
- var import_node_os3 = require("node:os");
1256
- var import_node_path3 = require("node:path");
1257
- function defaultProductDir(product) {
1258
- if (process.platform === "win32") {
1259
- const base = process.env.LOCALAPPDATA ?? (0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "launcher-fallback");
1260
- return (0, import_node_path3.join)(base, product);
1261
- }
1262
- const home = process.env.HOME ?? (0, import_node_os3.tmpdir)();
1263
- return (0, import_node_path3.join)(home, `.${product}`);
1264
- }
1265
- function resolveProductDir(product, explicit) {
1266
- return explicit ?? process.env.LAUNCHER_DIR ?? defaultProductDir(product);
1267
- }
1268
- function tokensPath(dir) {
1269
- return (0, import_node_path3.join)(dir, "tokens.json");
1270
- }
1271
- function statePath(dir) {
1272
- return (0, import_node_path3.join)(dir, "state.json");
1273
- }
1274
- function payloadDir(dir) {
1275
- return (0, import_node_path3.join)(dir, "payload");
1276
- }
1277
- function readTokens(dir) {
1278
- try {
1279
- const data = JSON.parse((0, import_node_fs8.readFileSync)(tokensPath(dir), "utf8"));
1280
- if (typeof data.accessToken !== "string" || !data.accessToken) return null;
1281
- if (typeof data.expiresAt !== "number" || !Number.isFinite(data.expiresAt)) return null;
1282
- const tokens = { accessToken: data.accessToken, expiresAt: data.expiresAt };
1283
- if (typeof data.refreshToken === "string" && data.refreshToken) tokens.refreshToken = data.refreshToken;
1284
- if (typeof data.clientId === "string" && data.clientId) tokens.clientId = data.clientId;
1285
- return tokens;
1286
- } catch {
1287
- return null;
1288
- }
1289
- }
1290
- function writeTokens(dir, tokens) {
1291
- (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
1292
- try {
1293
- (0, import_node_fs8.writeFileSync)(tokensPath(dir), `${JSON.stringify(tokens)}
1294
- `, { mode: 384 });
1295
- } catch {
1296
- (0, import_node_fs8.writeFileSync)(tokensPath(dir), `${JSON.stringify(tokens)}
1297
- `);
1298
- }
1299
- }
1300
- function clearTokens(dir) {
1301
- (0, import_node_fs8.rmSync)(tokensPath(dir), { force: true });
1302
- }
1303
- function readState(dir) {
1304
- try {
1305
- const data = JSON.parse((0, import_node_fs8.readFileSync)(statePath(dir), "utf8"));
1306
- if (typeof data.version !== "string" || !data.version) return null;
1307
- return { version: data.version, updatedAt: typeof data.updatedAt === "string" ? data.updatedAt : "" };
1308
- } catch {
1309
- return null;
1310
- }
1311
- }
1312
- function writeState(dir, state) {
1313
- (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
1314
- (0, import_node_fs8.writeFileSync)(statePath(dir), `${JSON.stringify(state, null, 2)}
1315
- `);
1316
- }
1317
- function wipeProductDir(dir) {
1318
- (0, import_node_fs8.rmSync)(tokensPath(dir), { force: true });
1319
- (0, import_node_fs8.rmSync)(statePath(dir), { force: true });
1320
- (0, import_node_fs8.rmSync)(payloadDir(dir), { force: true, recursive: true });
1321
- }
1322
-
1323
1581
  // src/index.ts
1324
- var LAUNCHER_VERSION = true ? "0.1.12" : readVersionFromPackage();
1582
+ var LAUNCHER_VERSION = true ? "0.1.13" : readVersionFromPackage();
1325
1583
  function defaultPrint(message) {
1326
1584
  process.stdout.write(`${message}
1327
1585
  `);
@@ -1333,30 +1591,37 @@ function defaultPrintErr(message) {
1333
1591
  async function run(rawOptions = {}) {
1334
1592
  const print = rawOptions.print ?? defaultPrint;
1335
1593
  const printErr = rawOptions.printErr ?? defaultPrintErr;
1336
- const argv = rawOptions.argv ?? process.argv.slice(2);
1337
- const runAt = argv.indexOf("--run");
1338
- if (runAt >= 0) return runFile(argv[runAt + 1], argv.slice(runAt + 2), printErr);
1594
+ const argv2 = rawOptions.argv ?? process.argv.slice(2);
1595
+ const runAt = argv2.indexOf("--run");
1596
+ if (runAt >= 0) return runFile(argv2[runAt + 1], argv2.slice(runAt + 2), printErr);
1339
1597
  let config;
1340
1598
  try {
1341
- config = loadProductConfig({ configPath: rawOptions.configPath ?? flagValue(argv, "--config") });
1599
+ config = loadProductConfig({ configPath: rawOptions.configPath ?? flagValue(argv2, "--config") });
1342
1600
  } catch (error) {
1343
1601
  print(`cannot start: ${error.message}`);
1344
1602
  return 2;
1345
1603
  }
1346
- const dir = resolveProductDir(config.product, rawOptions.dir ?? flagValue(argv, "--dir"));
1347
- const positional = argv.filter((arg) => !arg.startsWith("-"));
1348
- if (argv.includes("--version") || argv.includes("-v")) {
1604
+ const dir = resolveProductDir(config.product, rawOptions.dir ?? flagValue(argv2, "--dir"));
1605
+ const positional = argv2.filter((arg) => !arg.startsWith("-"));
1606
+ if (argv2.includes("--version") || argv2.includes("-v")) {
1349
1607
  print(`${config.binName} launcher ${LAUNCHER_VERSION}`);
1350
1608
  return 0;
1351
1609
  }
1352
- if (argv.includes("--help") || argv.includes("-h") || positional.length === 0) {
1610
+ if (argv2.includes("--help") || argv2.includes("-h") || positional.length === 0) {
1353
1611
  printUsage(config, print);
1354
1612
  return positional.length === 0 ? 2 : 0;
1355
1613
  }
1356
- const command = positional[0];
1614
+ const command2 = positional[0];
1615
+ let unlock;
1357
1616
  try {
1358
- switch (command) {
1617
+ unlock = lockInstallation(dir);
1618
+ await recoverAcquisition(dir, async (command3, cwd, env) => {
1619
+ const result = (rawOptions.runEntry ?? defaultRunEntry)(command3, cwd, env);
1620
+ return result.ok && (result.code === void 0 || result.code === 0);
1621
+ }, payloadEnv(dir) ?? process.env);
1622
+ switch (command2) {
1359
1623
  case "login":
1624
+ unlock();
1360
1625
  await doLogin(config, dir, rawOptions, print);
1361
1626
  return 0;
1362
1627
  case "logout":
@@ -1368,11 +1633,11 @@ async function run(rawOptions = {}) {
1368
1633
  case "update":
1369
1634
  return await doUpdate(config, dir, rawOptions, print);
1370
1635
  case "doctor":
1371
- return doDoctor(config, dir, rawOptions, print);
1636
+ return doDoctor(config, dir, rawOptions, print, unlock);
1372
1637
  case "autoupdate":
1373
1638
  return doAutoupdate(config, rawOptions, positional.slice(1), print);
1374
1639
  default:
1375
- return doForward(config, dir, rawOptions, command, argv, print);
1640
+ return doForward(config, dir, rawOptions, command2, argv2, print, unlock);
1376
1641
  }
1377
1642
  } catch (error) {
1378
1643
  if (error instanceof NeedsLoginError) {
@@ -1381,12 +1646,14 @@ async function run(rawOptions = {}) {
1381
1646
  }
1382
1647
  print(`failed: ${error.message}`);
1383
1648
  return 1;
1649
+ } finally {
1650
+ unlock?.();
1384
1651
  }
1385
1652
  }
1386
- function flagValue(argv, flag) {
1387
- const index = argv.indexOf(flag);
1653
+ function flagValue(argv2, flag) {
1654
+ const index = argv2.indexOf(flag);
1388
1655
  if (index < 0) return void 0;
1389
- const value = argv[index + 1];
1656
+ const value = argv2[index + 1];
1390
1657
  return value && !value.startsWith("-") ? value : void 0;
1391
1658
  }
1392
1659
  function printUsage(config, print) {
@@ -1400,8 +1667,8 @@ async function runFile(file, args, printErr) {
1400
1667
  printErr("launcher --run needs a file to run.");
1401
1668
  return 1;
1402
1669
  }
1403
- const abs = (0, import_node_path4.resolve)(process.cwd(), file);
1404
- if (!(0, import_node_fs9.existsSync)(abs)) {
1670
+ const abs = (0, import_node_path6.resolve)(process.cwd(), file);
1671
+ if (!(0, import_node_fs11.existsSync)(abs)) {
1405
1672
  printErr(`cannot run ${file}: no such file.`);
1406
1673
  return 1;
1407
1674
  }
@@ -1494,14 +1761,14 @@ async function fetchAccessTokenOrLogin(config, dir, options, print, installer) {
1494
1761
  }
1495
1762
  function readPayloadArgv(dir, key) {
1496
1763
  try {
1497
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path4.join)(payloadDir(dir), "payload.json"), "utf8"));
1498
- const entry = parsed[key];
1499
- if (typeof entry === "string") {
1500
- const parts = entry.trim().split(/\s+/).filter(Boolean);
1764
+ const parsed = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path6.join)(payloadDir(dir), "payload.json"), "utf8"));
1765
+ const entry2 = parsed[key];
1766
+ if (typeof entry2 === "string") {
1767
+ const parts = entry2.trim().split(/\s+/).filter(Boolean);
1501
1768
  return parts.length > 0 ? parts : null;
1502
1769
  }
1503
- if (Array.isArray(entry) && entry.every((part) => typeof part === "string" && part.length > 0)) {
1504
- return entry;
1770
+ if (Array.isArray(entry2) && entry2.every((part) => typeof part === "string" && part.length > 0)) {
1771
+ return entry2;
1505
1772
  }
1506
1773
  return null;
1507
1774
  } catch {
@@ -1516,7 +1783,7 @@ function readPayloadRun(dir) {
1516
1783
  }
1517
1784
  function readPayloadVerbs(dir) {
1518
1785
  try {
1519
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path4.join)(payloadDir(dir), "payload.json"), "utf8"));
1786
+ const parsed = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path6.join)(payloadDir(dir), "payload.json"), "utf8"));
1520
1787
  const verbs = parsed.verbs;
1521
1788
  if (verbs === "*") return "*";
1522
1789
  if (Array.isArray(verbs) && verbs.every((v) => typeof v === "string" && v.trim().length > 0)) {
@@ -1527,13 +1794,13 @@ function readPayloadVerbs(dir) {
1527
1794
  return null;
1528
1795
  }
1529
1796
  }
1530
- function resolveEntry(entry) {
1531
- return entry[0] === "$self" ? [process.execPath, ...entry.slice(1)] : entry;
1797
+ function resolveEntry(entry2) {
1798
+ return entry2[0] === "$self" ? [process.execPath, ...entry2.slice(1)] : entry2;
1532
1799
  }
1533
- function needsShell(command) {
1800
+ function needsShell(command2) {
1534
1801
  if (process.platform !== "win32") return false;
1535
- if (/\.(cmd|bat)$/i.test(command)) return true;
1536
- return !(0, import_node_fs9.existsSync)(command);
1802
+ if (/\.(cmd|bat)$/i.test(command2)) return true;
1803
+ return !(0, import_node_fs11.existsSync)(command2);
1537
1804
  }
1538
1805
  function quoteForShell(arg) {
1539
1806
  return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
@@ -1560,11 +1827,11 @@ function parseProgress(raw) {
1560
1827
  }
1561
1828
  return progress;
1562
1829
  }
1563
- function defaultRunEntry(entry, cwd, env) {
1564
- const [command, ...args] = entry;
1565
- const shell = needsShell(command);
1566
- const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
1567
- const spawnEntry = (progress2) => (0, import_node_child_process3.spawnSync)(commandLine, shell ? [] : args, {
1830
+ function defaultRunEntry(entry2, cwd, env) {
1831
+ const [command2, ...args] = entry2;
1832
+ const shell = needsShell(command2);
1833
+ const commandLine = shell ? [command2, ...args].map(quoteForShell).join(" ") : command2;
1834
+ const spawnEntry = (progress2) => (0, import_node_child_process4.spawnSync)(commandLine, shell ? [] : args, {
1568
1835
  cwd,
1569
1836
  stdio: progress2 ? ["inherit", "inherit", "inherit", "pipe"] : "inherit",
1570
1837
  shell,
@@ -1622,11 +1889,41 @@ async function installOrUpdate(config, dir, options, print, update) {
1622
1889
  version = manifest.version;
1623
1890
  installer.phase("resolve", { seconds: (Date.now() - started) / 1e3 });
1624
1891
  const current = readState(dir);
1625
- const unchanged = update && current?.version === version && (0, import_node_fs9.existsSync)(payloadDir(dir));
1892
+ const unchanged = update && current?.version === version && (0, import_node_fs11.existsSync)(payloadDir(dir));
1893
+ if (unchanged && current.acquired) {
1894
+ installer.surface({ id: config.product, from: version, to: version, state: "current" });
1895
+ return await finishLastMile(config, dir, options, installer, version, true, acquisitionRepairCommand(dir, version));
1896
+ }
1626
1897
  if (!unchanged) {
1627
1898
  installer.phase("download", { state: "running" });
1628
1899
  started = Date.now();
1629
- await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
1900
+ const candidate = (0, import_node_crypto6.randomUUID)();
1901
+ const candidateDir = (0, import_node_path6.join)(dir, "candidates", candidate);
1902
+ await downloadAndUnpack(config, candidateDir, manifest, accessToken, { fetchImpl });
1903
+ const acquisition = (0, import_node_fs11.existsSync)((0, import_node_path6.join)(candidateDir, "payload", "payload.json")) ? readAcquisition((0, import_node_path6.join)(candidateDir, "payload")) : null;
1904
+ if (acquisition) {
1905
+ installer.phase("download", { seconds: (Date.now() - started) / 1e3 });
1906
+ installer.phase("activate", { state: "running" });
1907
+ let outcome;
1908
+ const env = { ...payloadEnv(dir) ?? process.env, MM_OUTER_CONSOLE: "1" };
1909
+ await installAcquisition(dir, candidate, version, acquisition, {
1910
+ fetchImpl,
1911
+ env,
1912
+ run: async (command2, cwd, childEnv) => {
1913
+ const result = options.runEntry ? options.runEntry(command2, cwd, childEnv) : await runInstallEntry(command2, cwd, childEnv, installer, readTokens(dir));
1914
+ if (result.outcome) outcome = validateInstallerOutcome(result.outcome);
1915
+ return result.ok && (result.code === void 0 || result.code === 0) && !result.outcome?.operationFailed && !result.outcome?.failed;
1916
+ }
1917
+ });
1918
+ installer.phase("activate");
1919
+ installer.surface({ id: config.product, from: current?.version, to: version, state: "updated" });
1920
+ installer.finish(outcome ?? { version, total: 1, updated: 1, failed: 0, installed: true });
1921
+ return 0;
1922
+ }
1923
+ const target = (0, import_node_path6.join)(dir, "payload");
1924
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
1925
+ (0, import_node_fs11.rmSync)(target, { recursive: true, force: true });
1926
+ (0, import_node_fs11.renameSync)((0, import_node_path6.join)(candidateDir, "payload"), target);
1630
1927
  writeState(dir, { version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
1631
1928
  installer.phase("download", { seconds: (Date.now() - started) / 1e3 });
1632
1929
  }
@@ -1664,36 +1961,36 @@ function payloadEnv(dir) {
1664
1961
  return { ...process.env, MM_INSTALLER_TOKEN: stored.accessToken };
1665
1962
  }
1666
1963
  var NEVER_A_PROGRAM = /\.(tgz|tar|gz|bz2|xz|zip|json|txt|md|lock)$/i;
1667
- function payloadFileAsCommand(entry, payload) {
1668
- const first = entry[0];
1964
+ function payloadFileAsCommand(entry2, payload) {
1965
+ const first = entry2[0];
1669
1966
  if (!first || first === "$self") return null;
1670
1967
  if (first.includes("/") || first.includes("\\")) return null;
1671
- const candidate = (0, import_node_path4.join)(payload, first);
1672
- if (!(0, import_node_fs9.existsSync)(candidate)) return null;
1968
+ const candidate = (0, import_node_path6.join)(payload, first);
1969
+ if (!(0, import_node_fs11.existsSync)(candidate)) return null;
1673
1970
  if (NEVER_A_PROGRAM.test(first)) return first;
1674
1971
  if (process.platform === "win32") return null;
1675
1972
  try {
1676
- return ((0, import_node_fs9.statSync)(candidate).mode & 73) === 0 ? first : null;
1973
+ return ((0, import_node_fs11.statSync)(candidate).mode & 73) === 0 ? first : null;
1677
1974
  } catch {
1678
1975
  return null;
1679
1976
  }
1680
1977
  }
1681
- async function finishLastMile(config, dir, options, installer, version, unchanged) {
1682
- const entry = readPayloadEntry(dir);
1978
+ async function finishLastMile(config, dir, options, installer, version, unchanged, repairCommand) {
1979
+ const entry2 = repairCommand ?? readPayloadEntry(dir);
1683
1980
  const payload = payloadDir(dir);
1684
- if (!entry) {
1981
+ if (!entry2) {
1685
1982
  installer.finish({
1686
1983
  version,
1687
1984
  total: 1,
1688
1985
  updated: unchanged ? 0 : 1,
1689
1986
  failed: 0,
1690
1987
  installed: true,
1691
- detail: `next step: run ${(0, import_node_path4.join)(payload, config.binName)} to start ${config.product}.`
1988
+ detail: `next step: run ${(0, import_node_path6.join)(payload, config.binName)} to start ${config.product}.`
1692
1989
  });
1693
1990
  return 0;
1694
1991
  }
1695
- const command = resolveEntry(entry);
1696
- const dataFile = payloadFileAsCommand(entry, payload);
1992
+ const command2 = resolveEntry(entry2);
1993
+ const dataFile = payloadFileAsCommand(entry2, payload);
1697
1994
  if (dataFile) {
1698
1995
  installer.finish({
1699
1996
  version,
@@ -1706,8 +2003,9 @@ async function finishLastMile(config, dir, options, installer, version, unchange
1706
2003
  }
1707
2004
  installer.phase("activate", { state: "running" });
1708
2005
  const started = Date.now();
1709
- const env = { ...payloadEnv(dir) ?? process.env, MM_OUTER_CONSOLE: "1" };
1710
- const result = options.runEntry ? options.runEntry(command, payload, env) : await runInstallEntry(command, payload, env, installer, readTokens(dir));
2006
+ const inherited = { ...payloadEnv(dir) ?? process.env, MM_OUTER_CONSOLE: "1" };
2007
+ const env = repairCommand ? runtimeEnvironment(repairCommand[0], inherited) : inherited;
2008
+ const result = options.runEntry ? options.runEntry(command2, payload, env) : await runInstallEntry(command2, payload, env, installer, readTokens(dir));
1711
2009
  for (const progress of result.progress ?? []) installer.milestone(progress);
1712
2010
  const succeeded = result.ok && (result.code === void 0 || result.code === 0);
1713
2011
  const outcome = result.outcome ? validateInstallerOutcome(result.outcome) : void 0;
@@ -1722,18 +2020,18 @@ async function finishLastMile(config, dir, options, installer, version, unchange
1722
2020
  },
1723
2021
  ...!succeeded ? {
1724
2022
  operationFailed: true,
1725
- detail: result.code === void 0 ? `Arming this machine could not start: ${result.error ?? command[0]}` : `Arming this machine did not finish (exit code ${result.code})${result.error?.startsWith("installer outcome:") ? `: ${result.error}` : ""}`,
1726
- retry: `(cd ${payload} && ${command.join(" ")})`
2023
+ detail: result.code === void 0 ? `Arming this machine could not start: ${result.error ?? command2[0]}` : `Arming this machine did not finish (exit code ${result.code})${result.error?.startsWith("installer outcome:") ? `: ${result.error}` : ""}`,
2024
+ retry: `(cd ${payload} && ${command2.join(" ")})`
1727
2025
  } : {}
1728
2026
  });
1729
2027
  return succeeded ? outcome?.operationFailed || outcome?.failed ? 1 : 0 : result.code || 1;
1730
2028
  }
1731
- async function runInstallEntry(entry, cwd, env, installer, tokens = null) {
1732
- const [command, ...args] = entry;
1733
- const shell = needsShell(command);
2029
+ async function runInstallEntry(entry2, cwd, env, installer, tokens = null) {
2030
+ const [command2, ...args] = entry2;
2031
+ const shell = needsShell(command2);
1734
2032
  const progress = !(process.platform === "win32" && shell);
1735
- const outcomeDir = (0, import_node_fs9.mkdtempSync)((0, import_node_path4.join)((0, import_node_os4.tmpdir)(), "mm-installer-outcome-"));
1736
- const outcomeFile = (0, import_node_path4.join)(outcomeDir, "outcome.json");
2033
+ const outcomeDir = (0, import_node_fs11.mkdtempSync)((0, import_node_path6.join)((0, import_node_os4.tmpdir)(), "mm-installer-outcome-"));
2034
+ const outcomeFile = (0, import_node_path6.join)(outcomeDir, "outcome.json");
1737
2035
  const childEnv = { ...env, MM_INSTALLER_OUTCOME_FILE: outcomeFile };
1738
2036
  delete childEnv.MM_FACE_TRANSCRIPT;
1739
2037
  delete childEnv.MM_PROGRESS_FD;
@@ -1742,8 +2040,8 @@ async function runInstallEntry(entry, cwd, env, installer, tokens = null) {
1742
2040
  const secrets = [tokens?.accessToken, tokens?.refreshToken].filter((value) => Boolean(value));
1743
2041
  const redact = (text) => secrets.reduce((safe, value) => safe.replaceAll(value, "[redacted]"), text);
1744
2042
  try {
1745
- const result = await new Promise((resolve2) => {
1746
- const child = (0, import_node_child_process3.spawn)(shell ? [command, ...args].map(quoteForShell).join(" ") : command, shell ? [] : args, {
2043
+ const result = await new Promise((resolve3) => {
2044
+ const child = (0, import_node_child_process4.spawn)(shell ? [command2, ...args].map(quoteForShell).join(" ") : command2, shell ? [] : args, {
1747
2045
  cwd,
1748
2046
  shell,
1749
2047
  windowsHide: true,
@@ -1779,8 +2077,8 @@ async function runInstallEntry(entry, cwd, env, installer, tokens = null) {
1779
2077
  if (pending.length > 65536) pending = "";
1780
2078
  });
1781
2079
  }
1782
- child.on("error", (error) => resolve2({ ok: false, error: error.message }));
1783
- child.on("close", (code) => resolve2({
2080
+ child.on("error", (error) => resolve3({ ok: false, error: error.message }));
2081
+ child.on("close", (code) => resolve3({
1784
2082
  ok: code === 0,
1785
2083
  ...code !== null ? { code } : {},
1786
2084
  ...code !== 0 ? { error: `exit code ${code}` } : {}
@@ -1793,25 +2091,30 @@ async function runInstallEntry(entry, cwd, env, installer, tokens = null) {
1793
2091
  return { ...result, ok: false, code: result.code || 1, error: "installer outcome: invalid child result" };
1794
2092
  }
1795
2093
  } finally {
1796
- if ((0, import_node_fs9.existsSync)(outcomeFile)) (0, import_node_fs9.unlinkSync)(outcomeFile);
1797
- (0, import_node_fs9.rmdirSync)(outcomeDir);
2094
+ if ((0, import_node_fs11.existsSync)(outcomeFile)) (0, import_node_fs11.unlinkSync)(outcomeFile);
2095
+ (0, import_node_fs11.rmdirSync)(outcomeDir);
1798
2096
  }
1799
2097
  }
1800
- function doForward(config, dir, options, command, argv, print) {
1801
- if (command && (0, import_node_fs9.existsSync)(command)) {
1802
- print(`${command} is a file, not a command \u2014 did you mean \`--run ${command}\`?`);
2098
+ function doForward(config, dir, options, command2, argv2, print, unlock) {
2099
+ if (command2 && (0, import_node_fs11.existsSync)(command2)) {
2100
+ print(`${command2} is a file, not a command \u2014 did you mean \`--run ${command2}\`?`);
1803
2101
  return 2;
1804
2102
  }
1805
- const target = readPayloadRun(dir);
2103
+ const acquired = readState(dir)?.acquired;
2104
+ const target = acquired ? [acquired.node, acquired.entry] : readPayloadRun(dir);
1806
2105
  const verbs = readPayloadVerbs(dir);
1807
- const declared = verbs === "*" || Array.isArray(verbs) && command !== void 0 && verbs.includes(command);
2106
+ const declared = verbs === "*" || Array.isArray(verbs) && command2 !== void 0 && verbs.includes(command2);
1808
2107
  if (!target || !declared) {
1809
- print(`unknown command: ${command}`);
2108
+ print(`unknown command: ${command2}`);
1810
2109
  printUsage(config, print);
1811
2110
  return 2;
1812
2111
  }
1813
- const forwarded = [...resolveEntry(target), ...argv];
1814
- const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir), payloadEnv(dir));
2112
+ const forwarded = [...resolveEntry(target), ...argv2];
2113
+ const cwd = payloadDir(dir);
2114
+ const inherited = payloadEnv(dir);
2115
+ const env = acquired ? runtimeEnvironment(acquired.node, inherited ?? process.env) : inherited;
2116
+ unlock();
2117
+ const result = (options.runEntry ?? defaultRunEntry)(forwarded, cwd, env);
1815
2118
  if (!result.ok && result.code === void 0) {
1816
2119
  print(`failed: ${result.error ?? `could not run ${forwarded[0]}`}`);
1817
2120
  return 1;
@@ -1821,10 +2124,10 @@ function doForward(config, dir, options, command, argv, print) {
1821
2124
  function doAutoupdate(config, options, args, print) {
1822
2125
  const mode = args[0] ?? "status";
1823
2126
  const scheduleOptions = options.autoupdate ?? {};
1824
- const command = options.autoupdate?.command ?? [process.execPath, "update"];
2127
+ const command2 = options.autoupdate?.command ?? [process.execPath, "update"];
1825
2128
  if (mode === "on") {
1826
2129
  try {
1827
- enableSchedule(config, command, scheduleOptions);
2130
+ enableSchedule(config, command2, scheduleOptions);
1828
2131
  } catch (error) {
1829
2132
  print(error.message);
1830
2133
  return 1;
@@ -1856,19 +2159,24 @@ function doAutoupdate(config, options, args, print) {
1856
2159
  if (state.lastRun) print(`last run: ${state.lastRun}`);
1857
2160
  return 0;
1858
2161
  }
1859
- function doDoctor(config, dir, options, print) {
2162
+ function doDoctor(config, dir, options, print, unlock) {
1860
2163
  doLauncherDoctor(config, dir, options, print);
1861
- const target = readPayloadRun(dir);
2164
+ const acquired = readState(dir)?.acquired;
2165
+ const target = acquired ? [acquired.node, acquired.entry] : readPayloadRun(dir);
1862
2166
  const verbs = readPayloadVerbs(dir);
1863
2167
  const chains = target !== null && (verbs === "*" || Array.isArray(verbs) && verbs.includes("doctor"));
1864
2168
  if (!chains) return 0;
1865
- const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir), payloadEnv(dir));
2169
+ const cwd = payloadDir(dir);
2170
+ const inherited = payloadEnv(dir);
2171
+ const env = acquired ? runtimeEnvironment(acquired.node, inherited ?? process.env) : inherited;
2172
+ unlock();
2173
+ const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], cwd, env);
1866
2174
  return result.code ?? (result.ok ? 0 : 1);
1867
2175
  }
1868
2176
  function doLauncherDoctor(config, dir, options, print) {
1869
2177
  const tokens = readTokens(dir);
1870
2178
  const state = readState(dir);
1871
- const payloadPresent = (0, import_node_fs9.existsSync)(payloadDir(dir));
2179
+ const payloadPresent = (0, import_node_fs11.existsSync)(payloadDir(dir));
1872
2180
  print(`product: ${config.product}`);
1873
2181
  print(`host: ${config.host}`);
1874
2182
  print(`login: ${config.loginKind}`);
@@ -1880,7 +2188,7 @@ function doLauncherDoctor(config, dir, options, print) {
1880
2188
  print("token: none \u2014 run login first.");
1881
2189
  }
1882
2190
  print(`payload: ${state ? state.version : "none"}${payloadPresent ? "" : " (not downloaded)"}`);
1883
- print(`paths: tokens ${(0, import_node_path4.join)(dir, "tokens.json")}, state ${(0, import_node_path4.join)(dir, "state.json")}, payload ${payloadDir(dir)}`);
2191
+ print(`paths: tokens ${(0, import_node_path6.join)(dir, "tokens.json")}, state ${(0, import_node_path6.join)(dir, "state.json")}, payload ${payloadDir(dir)}`);
1884
2192
  const schedule = querySchedule(config, options.autoupdate ?? {});
1885
2193
  print(
1886
2194
  `auto-update: ${!schedule.supported ? "unsupported on this platform" : schedule.enabled ? `on (${schedule.cadence ?? "hourly"})` : "off"}`