@mutmutco/installer-launcher 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/launcher.js CHANGED
@@ -896,35 +896,17 @@ var plainText = (text) => String(text).replaceAll("\u2192", "->").replaceAll("\x
896
896
 
897
897
  // ../face/src/spinner.ts
898
898
  import { appendFileSync as appendFileSync2, writeSync as writeSync2 } from "node:fs";
899
+ import { isatty } from "node:tty";
899
900
  import { Worker } from "node:worker_threads";
900
901
 
901
902
  // ../face/src/encoding.ts
902
- import { execFileSync } from "node:child_process";
903
903
  var UNICODE_SPINNER_FRAMES = Object.freeze(["\u25D2", "\u25D0", "\u25D3", "\u25D1"]);
904
904
  var ASCII_SPINNER_FRAMES = Object.freeze(["-", "\\", "|", "/"]);
905
- var cachedCodePage;
906
- function consoleOutputCodePage() {
907
- if (cachedCodePage !== void 0) return cachedCodePage;
908
- cachedCodePage = null;
909
- try {
910
- const out = execFileSync("chcp.com", { windowsHide: true, encoding: "utf8", timeout: 5e3 });
911
- const pages = [...String(out).matchAll(/(\d{3,5})/gu)].map((m) => Number(m[1]));
912
- cachedCodePage = pages.length ? pages[pages.length - 1] : null;
913
- } catch {
914
- cachedCodePage = null;
915
- }
916
- return cachedCodePage;
917
- }
918
- function consoleRendersUnicode(env = process.env, platform = process.platform, codePage = platform === "win32" ? consoleOutputCodePage() : null) {
905
+ function consoleRendersUnicode(env = process.env, _platform = process.platform) {
919
906
  if (env.MM_FACE_ASCII === "1" || env.NO_UNICODE === "1") return false;
920
907
  const locale = env.LC_ALL || env.LC_CTYPE || env.LANG || "";
921
908
  if (locale === "C" || locale === "POSIX" || /^C\./u.test(locale) || /^POSIX\./u.test(locale)) return false;
922
909
  if (/cp437|ibm437|\b437\b/iu.test(locale)) return false;
923
- if (/utf-?8/iu.test(locale)) return true;
924
- if (platform === "win32") {
925
- if (env.WT_SESSION || env.ConEmuANSI === "ON") return true;
926
- return codePage === 65001;
927
- }
928
910
  return true;
929
911
  }
930
912
  function spinnerFramesFor(env = process.env, platform = process.platform) {
@@ -935,6 +917,8 @@ function spinnerFramesFor(env = process.env, platform = process.platform) {
935
917
  var WORKER_SOURCE = `
936
918
  const { parentPort, workerData } = require('node:worker_threads');
937
919
  const { writeSync, appendFileSync } = require('node:fs');
920
+ const wide = workerData.wide ? new (require('node:tty').WriteStream)(2) : null;
921
+ const put = (text) => wide ? wide.write(text) : writeSync(2, text);
938
922
  const control = new Int32Array(workerData.control);
939
923
  let frames = workerData.frames, frame = workerData.frame;
940
924
  function draw() {
@@ -942,7 +926,7 @@ function draw() {
942
926
  try {
943
927
  if (Atomics.load(control, 0) || Atomics.load(control, 2) || !frames.length) return;
944
928
  const text = frames[frame++ % frames.length].replace('__elapsed__', Math.floor((Date.now() - workerData.started) / 1000) + 's');
945
- writeSync(2, text);
929
+ put(text);
946
930
  if (workerData.transcriptPath) appendFileSync(workerData.transcriptPath, JSON.stringify({ channel: 'spinner', text }) + '\\n');
947
931
  } finally {
948
932
  Atomics.store(control, 1, 0);
@@ -967,10 +951,12 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath,
967
951
  let frame = 0;
968
952
  let started = 0;
969
953
  let suspended = false;
954
+ const wide = process.platform === "win32" && isatty(2);
970
955
  const write = (text) => {
971
956
  if (stream) stream.write(text);
972
957
  else {
973
- writeSync2(2, text);
958
+ if (wide) process.stderr.write(`\x1B[1G${text}`);
959
+ else writeSync2(2, text);
974
960
  if (transcriptPath) appendFileSync2(transcriptPath, `${JSON.stringify({ channel: "spinner", text })}
975
961
  `);
976
962
  }
@@ -1017,7 +1003,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath,
1017
1003
  frame,
1018
1004
  intervalMs,
1019
1005
  transcriptPath,
1020
- started
1006
+ started,
1007
+ wide
1021
1008
  } });
1022
1009
  const active = worker;
1023
1010
  worker.on("error", () => {
@@ -1742,162 +1729,6 @@ function firstLine(text) {
1742
1729
  return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
1743
1730
  }
1744
1731
 
1745
- // src/refresh.ts
1746
- import { execFile } from "node:child_process";
1747
- import { chmodSync, existsSync as existsSync7, lstatSync, readdirSync, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
1748
- import { homedir } from "node:os";
1749
- import { basename, dirname as dirname3, join as join6 } from "node:path";
1750
- var LAUNCHER_RECORD_FILE = "launcher.json";
1751
- var RECORD_TIMEOUT_MS = 1e4;
1752
- function platformKey(platform, arch) {
1753
- if (platform === "win32") return arch === "x64" || arch === "arm64" ? `win-${arch}` : null;
1754
- return platform === "darwin" && arch === "arm64" ? "darwin-arm64" : null;
1755
- }
1756
- function verifierFor(target) {
1757
- return { product: target.product, host: target.host, loginKind: "github", publicKey: target.key, binName: target.bin };
1758
- }
1759
- async function fetchRecord(target, options) {
1760
- let json;
1761
- try {
1762
- const response = await (options.fetch ?? fetch)(
1763
- `${target.host.replace(/\/+$/u, "")}/release/launcher`,
1764
- { signal: AbortSignal.timeout(options.timeoutMs ?? RECORD_TIMEOUT_MS) }
1765
- );
1766
- if (!response.ok) return null;
1767
- json = await response.json();
1768
- } catch {
1769
- return null;
1770
- }
1771
- let record;
1772
- try {
1773
- record = parseManifest(json);
1774
- } catch {
1775
- return null;
1776
- }
1777
- if (!verifyManifest(record, record.signature, verifierFor(target))) return "bad-signature";
1778
- return { version: record.version, files: record.files };
1779
- }
1780
- async function readLauncherRecord(target, options = {}) {
1781
- const record = await fetchRecord(target, options);
1782
- return record === "bad-signature" ? null : record;
1783
- }
1784
- function compareVersions(a, b) {
1785
- const split = (v) => {
1786
- const [core, pre = ""] = v.split("-", 2);
1787
- return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
1788
- };
1789
- const x = split(a);
1790
- const y = split(b);
1791
- for (let i = 0; i < 3; i++) if ((x.nums[i] ?? 0) !== (y.nums[i] ?? 0)) return (x.nums[i] ?? 0) > (y.nums[i] ?? 0) ? 1 : -1;
1792
- if (x.pre === y.pre) return 0;
1793
- if (!x.pre) return 1;
1794
- if (!y.pre) return -1;
1795
- return x.pre > y.pre ? 1 : -1;
1796
- }
1797
- function recordedPath(productDir) {
1798
- try {
1799
- const saved = JSON.parse(readFileSync7(join6(productDir, LAUNCHER_RECORD_FILE), "utf8"));
1800
- return typeof saved.path === "string" && saved.path ? saved.path : null;
1801
- } catch {
1802
- return null;
1803
- }
1804
- }
1805
- function installedPath(target, productDir, platform) {
1806
- return platform === "win32" ? join6(productDir, "bin", `${target.bin}.exe`) : join6(homedir(), ".local", "bin", target.bin);
1807
- }
1808
- function parseLauncherVersion(stdout, bin) {
1809
- const text = String(stdout).trim();
1810
- if (!text.startsWith(`${bin} `)) return null;
1811
- return text.match(/launcher (\d+\.\d+\.\d+[^\s)]*)/u)?.[1] ?? null;
1812
- }
1813
- function runVersion(path, bin) {
1814
- return new Promise((resolve3) => {
1815
- execFile(path, ["--version"], { timeout: 3e4, windowsHide: true, encoding: "utf8" }, (error, stdout) => {
1816
- resolve3(error ? null : parseLauncherVersion(stdout, bin));
1817
- });
1818
- });
1819
- }
1820
- function removeStale(file) {
1821
- const prefix = `${basename(file)}.old-`;
1822
- let names;
1823
- try {
1824
- names = readdirSync(dirname3(file));
1825
- } catch {
1826
- return;
1827
- }
1828
- for (const name of names) {
1829
- if (!name.startsWith(prefix)) continue;
1830
- try {
1831
- rmSync5(join6(dirname3(file), name), { force: true });
1832
- } catch {
1833
- }
1834
- }
1835
- }
1836
- async function refreshLauncher(target, options = {}) {
1837
- const started = Date.now();
1838
- const platform = options.platform ?? process.platform;
1839
- const key = platformKey(platform, options.arch ?? process.arch);
1840
- if (!key) return null;
1841
- const productDir = options.productDir ?? defaultProductDir(target.product);
1842
- const recorded = recordedPath(productDir);
1843
- const file = options.path ?? (recorded && existsSync7(recorded) ? recorded : installedPath(target, productDir, platform));
1844
- if (!existsSync7(file) || lstatSync(file).isSymbolicLink()) return null;
1845
- if (!options.dryRun) removeStale(file);
1846
- const from = await (options.readVersion ?? ((path) => runVersion(path, target.bin)))(file);
1847
- if (!from) return null;
1848
- const arm = (verdict, to, detail) => ({ surface: "launcher", from, to, verdict, detail, durationMs: Date.now() - started });
1849
- const record = await fetchRecord(target, options);
1850
- if (record === "bad-signature") return arm("fail", from, "The launcher record failed its signature check.");
1851
- const entry2 = record?.files.find((candidate) => candidate.path === key);
1852
- if (!record || !entry2) return arm("skip", from, "No launcher update is offered yet.");
1853
- if (compareVersions(record.version, from) <= 0) return arm("ok", from, "");
1854
- if (options.dryRun) return arm("ok", record.version, "");
1855
- let unlock;
1856
- const lockHeld = options.lockHeld ?? process.env.MM_OUTER_CONSOLE === "1";
1857
- if (!lockHeld) {
1858
- try {
1859
- unlock = lockInstallation(productDir);
1860
- } catch {
1861
- return arm("defer", from, "Another install is running; the launcher updates next time.");
1862
- }
1863
- }
1864
- const next = `${file}.new-${process.pid}`;
1865
- let moved;
1866
- try {
1867
- let bytes;
1868
- try {
1869
- const response = await (options.fetch ?? fetch)(
1870
- `${(target.dl ?? target.host).replace(/\/+$/u, "")}/dl/${target.product}/${key}`,
1871
- { signal: AbortSignal.timeout(3e5) }
1872
- );
1873
- if (!response.ok) return arm("defer", from, "The launcher download did not complete; it retries next time.");
1874
- bytes = await readDownload(response, void 0, entry2.size);
1875
- } catch {
1876
- return arm("defer", from, "The launcher download did not complete; it retries next time.");
1877
- }
1878
- if (!verifyFileBytes(entry2, bytes)) return arm("fail", from, "The downloaded launcher failed its checksum.");
1879
- try {
1880
- writeFileSync7(next, bytes, { mode: 493 });
1881
- if (platform !== "win32") chmodSync(next, 493);
1882
- if (platform === "win32") {
1883
- moved = `${file}.old-${process.pid}-${Date.now()}`;
1884
- renameSync4(file, moved);
1885
- }
1886
- renameSync4(next, file);
1887
- } catch {
1888
- try {
1889
- if (moved && !existsSync7(file)) renameSync4(moved, file);
1890
- } catch {
1891
- }
1892
- rmSync5(next, { force: true });
1893
- return arm("fail", from, "The launcher could not be replaced.");
1894
- }
1895
- return arm("ok", record.version, "");
1896
- } finally {
1897
- unlock?.();
1898
- }
1899
- }
1900
-
1901
1732
  // src/login-github.ts
1902
1733
  import { spawn, spawnSync as spawnSync3 } from "node:child_process";
1903
1734
  var realSleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
@@ -2158,8 +1989,201 @@ function page(title, body) {
2158
1989
  return `<!doctype html><meta charset="utf-8"><meta name="color-scheme" content="dark"><title>${title}</title><style>${style}</style><main><h1>${title}</h1><p>${body}</p></main>`;
2159
1990
  }
2160
1991
 
1992
+ // src/tokens.ts
1993
+ async function ensureFreshToken(config, dir, fetchImpl = fetch) {
1994
+ const stored = readTokens(dir);
1995
+ if (!stored) return null;
1996
+ if (stored.expiresAt - Date.now() > 6e4) return stored.accessToken;
1997
+ if (!stored.refreshToken) return null;
1998
+ if (config.loginKind === "github") {
1999
+ const refreshed2 = await refreshAccessToken(config, stored.refreshToken, fetchImpl);
2000
+ if (!refreshed2) {
2001
+ clearTokens(dir);
2002
+ return null;
2003
+ }
2004
+ writeTokens(dir, {
2005
+ accessToken: refreshed2.accessToken,
2006
+ refreshToken: refreshed2.refreshToken ?? stored.refreshToken,
2007
+ expiresAt: Date.now() + refreshed2.expiresIn * 1e3
2008
+ });
2009
+ return refreshed2.accessToken;
2010
+ }
2011
+ if (!stored.clientId) {
2012
+ clearTokens(dir);
2013
+ return null;
2014
+ }
2015
+ const refreshed = await refreshGoogleToken(config.host, stored.clientId, stored.refreshToken, fetchImpl);
2016
+ if (!refreshed) {
2017
+ clearTokens(dir);
2018
+ return null;
2019
+ }
2020
+ writeTokens(dir, {
2021
+ accessToken: refreshed.accessToken,
2022
+ refreshToken: refreshed.refreshToken,
2023
+ expiresAt: Date.now() + refreshed.expiresIn * 1e3,
2024
+ clientId: stored.clientId
2025
+ });
2026
+ return refreshed.accessToken;
2027
+ }
2028
+
2029
+ // src/refresh.ts
2030
+ import { execFile } from "node:child_process";
2031
+ import { chmodSync, existsSync as existsSync7, lstatSync, readdirSync, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
2032
+ import { homedir } from "node:os";
2033
+ import { basename, dirname as dirname3, join as join6 } from "node:path";
2034
+ var LAUNCHER_RECORD_FILE = "launcher.json";
2035
+ var RECORD_TIMEOUT_MS = 1e4;
2036
+ function platformKey(platform, arch) {
2037
+ if (platform === "win32") return arch === "x64" || arch === "arm64" ? `win-${arch}` : null;
2038
+ return platform === "darwin" && arch === "arm64" ? "darwin-arm64" : null;
2039
+ }
2040
+ function verifierFor(target) {
2041
+ return { product: target.product, host: target.host, loginKind: "github", publicKey: target.key, binName: target.bin };
2042
+ }
2043
+ async function fetchRecord(target, options) {
2044
+ let json;
2045
+ try {
2046
+ const response = await (options.fetch ?? fetch)(
2047
+ `${target.host.replace(/\/+$/u, "")}/release/launcher`,
2048
+ { signal: AbortSignal.timeout(options.timeoutMs ?? RECORD_TIMEOUT_MS) }
2049
+ );
2050
+ if (!response.ok) return null;
2051
+ json = await response.json();
2052
+ } catch {
2053
+ return null;
2054
+ }
2055
+ let record;
2056
+ try {
2057
+ record = parseManifest(json);
2058
+ } catch {
2059
+ return null;
2060
+ }
2061
+ if (!verifyManifest(record, record.signature, verifierFor(target))) return "bad-signature";
2062
+ return { version: record.version, files: record.files };
2063
+ }
2064
+ async function readLauncherRecord(target, options = {}) {
2065
+ const record = await fetchRecord(target, options);
2066
+ return record === "bad-signature" ? null : record;
2067
+ }
2068
+ function compareVersions(a, b) {
2069
+ const split = (v) => {
2070
+ const [core, pre = ""] = v.split("-", 2);
2071
+ return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
2072
+ };
2073
+ const x = split(a);
2074
+ const y = split(b);
2075
+ for (let i = 0; i < 3; i++) if ((x.nums[i] ?? 0) !== (y.nums[i] ?? 0)) return (x.nums[i] ?? 0) > (y.nums[i] ?? 0) ? 1 : -1;
2076
+ if (x.pre === y.pre) return 0;
2077
+ if (!x.pre) return 1;
2078
+ if (!y.pre) return -1;
2079
+ return x.pre > y.pre ? 1 : -1;
2080
+ }
2081
+ function recordedPath(productDir) {
2082
+ try {
2083
+ const saved = JSON.parse(readFileSync7(join6(productDir, LAUNCHER_RECORD_FILE), "utf8"));
2084
+ return typeof saved.path === "string" && saved.path ? saved.path : null;
2085
+ } catch {
2086
+ return null;
2087
+ }
2088
+ }
2089
+ function installedPath(target, productDir, platform) {
2090
+ return platform === "win32" ? join6(productDir, "bin", `${target.bin}.exe`) : join6(homedir(), ".local", "bin", target.bin);
2091
+ }
2092
+ function parseLauncherVersion(stdout, bin) {
2093
+ const text = String(stdout).trim();
2094
+ if (!text.startsWith(`${bin} `)) return null;
2095
+ return text.match(/launcher (\d+\.\d+\.\d+[^\s)]*)/u)?.[1] ?? null;
2096
+ }
2097
+ function runVersion(path, bin) {
2098
+ return new Promise((resolve3) => {
2099
+ execFile(path, ["--version"], { timeout: 3e4, windowsHide: true, encoding: "utf8" }, (error, stdout) => {
2100
+ resolve3(error ? null : parseLauncherVersion(stdout, bin));
2101
+ });
2102
+ });
2103
+ }
2104
+ function removeStale(file) {
2105
+ const prefix = `${basename(file)}.old-`;
2106
+ let names;
2107
+ try {
2108
+ names = readdirSync(dirname3(file));
2109
+ } catch {
2110
+ return;
2111
+ }
2112
+ for (const name of names) {
2113
+ if (!name.startsWith(prefix)) continue;
2114
+ try {
2115
+ rmSync5(join6(dirname3(file), name), { force: true });
2116
+ } catch {
2117
+ }
2118
+ }
2119
+ }
2120
+ async function refreshLauncher(target, options = {}) {
2121
+ const started = Date.now();
2122
+ const platform = options.platform ?? process.platform;
2123
+ const key = platformKey(platform, options.arch ?? process.arch);
2124
+ if (!key) return null;
2125
+ const productDir = options.productDir ?? defaultProductDir(target.product);
2126
+ const recorded = recordedPath(productDir);
2127
+ const file = options.path ?? (recorded && existsSync7(recorded) ? recorded : installedPath(target, productDir, platform));
2128
+ if (!existsSync7(file) || lstatSync(file).isSymbolicLink()) return null;
2129
+ if (!options.dryRun) removeStale(file);
2130
+ const from = await (options.readVersion ?? ((path) => runVersion(path, target.bin)))(file);
2131
+ if (!from) return null;
2132
+ const arm = (verdict, to, detail) => ({ surface: "launcher", from, to, verdict, detail, durationMs: Date.now() - started });
2133
+ const record = await fetchRecord(target, options);
2134
+ if (record === "bad-signature") return arm("fail", from, "The launcher record failed its signature check.");
2135
+ const entry2 = record?.files.find((candidate) => candidate.path === key);
2136
+ if (!record || !entry2) return arm("skip", from, "No launcher update is offered yet.");
2137
+ if (compareVersions(record.version, from) <= 0) return arm("ok", from, "");
2138
+ if (options.dryRun) return arm("ok", record.version, "");
2139
+ let unlock;
2140
+ const lockHeld = options.lockHeld ?? process.env.MM_OUTER_CONSOLE === "1";
2141
+ if (!lockHeld) {
2142
+ try {
2143
+ unlock = lockInstallation(productDir);
2144
+ } catch {
2145
+ return arm("defer", from, "Another install is running; the launcher updates next time.");
2146
+ }
2147
+ }
2148
+ const next = `${file}.new-${process.pid}`;
2149
+ let moved;
2150
+ try {
2151
+ let bytes;
2152
+ try {
2153
+ const response = await (options.fetch ?? fetch)(
2154
+ `${(target.dl ?? target.host).replace(/\/+$/u, "")}/dl/${target.product}/${key}`,
2155
+ { signal: AbortSignal.timeout(3e5) }
2156
+ );
2157
+ if (!response.ok) return arm("defer", from, "The launcher download did not complete; it retries next time.");
2158
+ bytes = await readDownload(response, void 0, entry2.size);
2159
+ } catch {
2160
+ return arm("defer", from, "The launcher download did not complete; it retries next time.");
2161
+ }
2162
+ if (!verifyFileBytes(entry2, bytes)) return arm("fail", from, "The downloaded launcher failed its checksum.");
2163
+ try {
2164
+ writeFileSync7(next, bytes, { mode: 493 });
2165
+ if (platform !== "win32") chmodSync(next, 493);
2166
+ if (platform === "win32") {
2167
+ moved = `${file}.old-${process.pid}-${Date.now()}`;
2168
+ renameSync4(file, moved);
2169
+ }
2170
+ renameSync4(next, file);
2171
+ } catch {
2172
+ try {
2173
+ if (moved && !existsSync7(file)) renameSync4(moved, file);
2174
+ } catch {
2175
+ }
2176
+ rmSync5(next, { force: true });
2177
+ return arm("fail", from, "The launcher could not be replaced.");
2178
+ }
2179
+ return arm("ok", record.version, "");
2180
+ } finally {
2181
+ unlock?.();
2182
+ }
2183
+ }
2184
+
2161
2185
  // src/index.ts
2162
- var LAUNCHER_VERSION = true ? "1.2.2" : readVersionFromPackage();
2186
+ var LAUNCHER_VERSION = true ? "1.2.4" : readVersionFromPackage();
2163
2187
  function defaultPrint(message) {
2164
2188
  process.stdout.write(`${message}
2165
2189
  `);
@@ -2382,41 +2406,6 @@ async function runFile(file, args, printErr) {
2382
2406
  }
2383
2407
  return code;
2384
2408
  }
2385
- async function ensureFreshToken(config, dir, fetchImpl = fetch) {
2386
- const stored = readTokens(dir);
2387
- if (!stored) return null;
2388
- if (stored.expiresAt - Date.now() > 6e4) return stored.accessToken;
2389
- if (!stored.refreshToken) return null;
2390
- if (config.loginKind === "github") {
2391
- const refreshed2 = await refreshAccessToken(config, stored.refreshToken, fetchImpl);
2392
- if (!refreshed2) {
2393
- clearTokens(dir);
2394
- return null;
2395
- }
2396
- writeTokens(dir, {
2397
- accessToken: refreshed2.accessToken,
2398
- refreshToken: refreshed2.refreshToken ?? stored.refreshToken,
2399
- expiresAt: Date.now() + refreshed2.expiresIn * 1e3
2400
- });
2401
- return refreshed2.accessToken;
2402
- }
2403
- if (!stored.clientId) {
2404
- clearTokens(dir);
2405
- return null;
2406
- }
2407
- const refreshed = await refreshGoogleToken(config.host, stored.clientId, stored.refreshToken, fetchImpl);
2408
- if (!refreshed) {
2409
- clearTokens(dir);
2410
- return null;
2411
- }
2412
- writeTokens(dir, {
2413
- accessToken: refreshed.accessToken,
2414
- refreshToken: refreshed.refreshToken,
2415
- expiresAt: Date.now() + refreshed.expiresIn * 1e3,
2416
- clientId: stored.clientId
2417
- });
2418
- return refreshed.accessToken;
2419
- }
2420
2409
  async function doLogin(config, dir, options, prompt, installer) {
2421
2410
  const fetchImpl = options.fetchImpl ?? fetch;
2422
2411
  const open = options.open ?? openBrowser;
@@ -3064,7 +3053,6 @@ if (invokedAsMain) {
3064
3053
  export {
3065
3054
  LAUNCHER_VERSION,
3066
3055
  defaultRunEntry,
3067
- ensureFreshToken,
3068
3056
  formatMb,
3069
3057
  parseArgv,
3070
3058
  readPayloadEntry,
@@ -22,7 +22,6 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  LAUNCHER_VERSION: () => LAUNCHER_VERSION,
24
24
  defaultRunEntry: () => defaultRunEntry,
25
- ensureFreshToken: () => ensureFreshToken,
26
25
  formatMb: () => formatMb,
27
26
  parseArgv: () => parseArgv,
28
27
  readPayloadEntry: () => readPayloadEntry,
@@ -710,7 +709,7 @@ async function installAcquisition(dir, candidate, version, acquisition, options)
710
709
  }
711
710
 
712
711
  // src/index.ts
713
- var import_node_child_process6 = require("node:child_process");
712
+ var import_node_child_process5 = require("node:child_process");
714
713
  var import_node_fs12 = require("node:fs");
715
714
  var import_node_os5 = require("node:os");
716
715
  var import_node_path7 = require("node:path");
@@ -930,35 +929,17 @@ var plainText = (text) => String(text).replaceAll("\u2192", "->").replaceAll("\x
930
929
 
931
930
  // ../face/src/spinner.ts
932
931
  var import_node_fs7 = require("node:fs");
932
+ var import_node_tty = require("node:tty");
933
933
  var import_node_worker_threads = require("node:worker_threads");
934
934
 
935
935
  // ../face/src/encoding.ts
936
- var import_node_child_process2 = require("node:child_process");
937
936
  var UNICODE_SPINNER_FRAMES = Object.freeze(["\u25D2", "\u25D0", "\u25D3", "\u25D1"]);
938
937
  var ASCII_SPINNER_FRAMES = Object.freeze(["-", "\\", "|", "/"]);
939
- var cachedCodePage;
940
- function consoleOutputCodePage() {
941
- if (cachedCodePage !== void 0) return cachedCodePage;
942
- cachedCodePage = null;
943
- try {
944
- const out = (0, import_node_child_process2.execFileSync)("chcp.com", { windowsHide: true, encoding: "utf8", timeout: 5e3 });
945
- const pages = [...String(out).matchAll(/(\d{3,5})/gu)].map((m) => Number(m[1]));
946
- cachedCodePage = pages.length ? pages[pages.length - 1] : null;
947
- } catch {
948
- cachedCodePage = null;
949
- }
950
- return cachedCodePage;
951
- }
952
- function consoleRendersUnicode(env = process.env, platform = process.platform, codePage = platform === "win32" ? consoleOutputCodePage() : null) {
938
+ function consoleRendersUnicode(env = process.env, _platform = process.platform) {
953
939
  if (env.MM_FACE_ASCII === "1" || env.NO_UNICODE === "1") return false;
954
940
  const locale = env.LC_ALL || env.LC_CTYPE || env.LANG || "";
955
941
  if (locale === "C" || locale === "POSIX" || /^C\./u.test(locale) || /^POSIX\./u.test(locale)) return false;
956
942
  if (/cp437|ibm437|\b437\b/iu.test(locale)) return false;
957
- if (/utf-?8/iu.test(locale)) return true;
958
- if (platform === "win32") {
959
- if (env.WT_SESSION || env.ConEmuANSI === "ON") return true;
960
- return codePage === 65001;
961
- }
962
943
  return true;
963
944
  }
964
945
  function spinnerFramesFor(env = process.env, platform = process.platform) {
@@ -969,6 +950,8 @@ function spinnerFramesFor(env = process.env, platform = process.platform) {
969
950
  var WORKER_SOURCE = `
970
951
  const { parentPort, workerData } = require('node:worker_threads');
971
952
  const { writeSync, appendFileSync } = require('node:fs');
953
+ const wide = workerData.wide ? new (require('node:tty').WriteStream)(2) : null;
954
+ const put = (text) => wide ? wide.write(text) : writeSync(2, text);
972
955
  const control = new Int32Array(workerData.control);
973
956
  let frames = workerData.frames, frame = workerData.frame;
974
957
  function draw() {
@@ -976,7 +959,7 @@ function draw() {
976
959
  try {
977
960
  if (Atomics.load(control, 0) || Atomics.load(control, 2) || !frames.length) return;
978
961
  const text = frames[frame++ % frames.length].replace('__elapsed__', Math.floor((Date.now() - workerData.started) / 1000) + 's');
979
- writeSync(2, text);
962
+ put(text);
980
963
  if (workerData.transcriptPath) appendFileSync(workerData.transcriptPath, JSON.stringify({ channel: 'spinner', text }) + '\\n');
981
964
  } finally {
982
965
  Atomics.store(control, 1, 0);
@@ -1001,10 +984,12 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath,
1001
984
  let frame = 0;
1002
985
  let started = 0;
1003
986
  let suspended = false;
987
+ const wide = process.platform === "win32" && (0, import_node_tty.isatty)(2);
1004
988
  const write = (text) => {
1005
989
  if (stream) stream.write(text);
1006
990
  else {
1007
- (0, import_node_fs7.writeSync)(2, text);
991
+ if (wide) process.stderr.write(`\x1B[1G${text}`);
992
+ else (0, import_node_fs7.writeSync)(2, text);
1008
993
  if (transcriptPath) (0, import_node_fs7.appendFileSync)(transcriptPath, `${JSON.stringify({ channel: "spinner", text })}
1009
994
  `);
1010
995
  }
@@ -1051,7 +1036,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath,
1051
1036
  frame,
1052
1037
  intervalMs,
1053
1038
  transcriptPath,
1054
- started
1039
+ started,
1040
+ wide
1055
1041
  } });
1056
1042
  const active = worker;
1057
1043
  worker.on("error", () => {
@@ -1587,7 +1573,7 @@ var plain = (text, lane) => lane.tty ? String(text) : plainText(stripColor(Strin
1587
1573
  var rowTitle = (row) => `${row.surface}${row.aspect ? ` ${row.aspect}` : ""}${row.instance ? ` \xB7 ${row.instance}` : ""} ${row.state}`;
1588
1574
 
1589
1575
  // src/autoupdate.ts
1590
- var import_node_child_process3 = require("node:child_process");
1576
+ var import_node_child_process2 = require("node:child_process");
1591
1577
  var import_node_fs10 = require("node:fs");
1592
1578
  var import_node_os3 = require("node:os");
1593
1579
  var import_node_path5 = require("node:path");
@@ -1597,7 +1583,7 @@ function schedulePlatform(override) {
1597
1583
  return null;
1598
1584
  }
1599
1585
  function defaultExec(command2, args) {
1600
- const result = (0, import_node_child_process3.spawnSync)(command2, args, { encoding: "utf8", windowsHide: true });
1586
+ const result = (0, import_node_child_process2.spawnSync)(command2, args, { encoding: "utf8", windowsHide: true });
1601
1587
  if (result.error) return { code: 1, stdout: "", stderr: result.error.message };
1602
1588
  return {
1603
1589
  code: typeof result.status === "number" ? result.status : 1,
@@ -1776,171 +1762,15 @@ function firstLine(text) {
1776
1762
  return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
1777
1763
  }
1778
1764
 
1779
- // src/refresh.ts
1780
- var import_node_child_process4 = require("node:child_process");
1781
- var import_node_fs11 = require("node:fs");
1782
- var import_node_os4 = require("node:os");
1783
- var import_node_path6 = require("node:path");
1784
- var LAUNCHER_RECORD_FILE = "launcher.json";
1785
- var RECORD_TIMEOUT_MS = 1e4;
1786
- function platformKey(platform, arch) {
1787
- if (platform === "win32") return arch === "x64" || arch === "arm64" ? `win-${arch}` : null;
1788
- return platform === "darwin" && arch === "arm64" ? "darwin-arm64" : null;
1789
- }
1790
- function verifierFor(target) {
1791
- return { product: target.product, host: target.host, loginKind: "github", publicKey: target.key, binName: target.bin };
1792
- }
1793
- async function fetchRecord(target, options) {
1794
- let json;
1795
- try {
1796
- const response = await (options.fetch ?? fetch)(
1797
- `${target.host.replace(/\/+$/u, "")}/release/launcher`,
1798
- { signal: AbortSignal.timeout(options.timeoutMs ?? RECORD_TIMEOUT_MS) }
1799
- );
1800
- if (!response.ok) return null;
1801
- json = await response.json();
1802
- } catch {
1803
- return null;
1804
- }
1805
- let record;
1806
- try {
1807
- record = parseManifest(json);
1808
- } catch {
1809
- return null;
1810
- }
1811
- if (!verifyManifest(record, record.signature, verifierFor(target))) return "bad-signature";
1812
- return { version: record.version, files: record.files };
1813
- }
1814
- async function readLauncherRecord(target, options = {}) {
1815
- const record = await fetchRecord(target, options);
1816
- return record === "bad-signature" ? null : record;
1817
- }
1818
- function compareVersions(a, b) {
1819
- const split = (v) => {
1820
- const [core, pre = ""] = v.split("-", 2);
1821
- return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
1822
- };
1823
- const x = split(a);
1824
- const y = split(b);
1825
- for (let i = 0; i < 3; i++) if ((x.nums[i] ?? 0) !== (y.nums[i] ?? 0)) return (x.nums[i] ?? 0) > (y.nums[i] ?? 0) ? 1 : -1;
1826
- if (x.pre === y.pre) return 0;
1827
- if (!x.pre) return 1;
1828
- if (!y.pre) return -1;
1829
- return x.pre > y.pre ? 1 : -1;
1830
- }
1831
- function recordedPath(productDir) {
1832
- try {
1833
- const saved = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path6.join)(productDir, LAUNCHER_RECORD_FILE), "utf8"));
1834
- return typeof saved.path === "string" && saved.path ? saved.path : null;
1835
- } catch {
1836
- return null;
1837
- }
1838
- }
1839
- function installedPath(target, productDir, platform) {
1840
- return platform === "win32" ? (0, import_node_path6.join)(productDir, "bin", `${target.bin}.exe`) : (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".local", "bin", target.bin);
1841
- }
1842
- function parseLauncherVersion(stdout, bin) {
1843
- const text = String(stdout).trim();
1844
- if (!text.startsWith(`${bin} `)) return null;
1845
- return text.match(/launcher (\d+\.\d+\.\d+[^\s)]*)/u)?.[1] ?? null;
1846
- }
1847
- function runVersion(path, bin) {
1848
- return new Promise((resolve3) => {
1849
- (0, import_node_child_process4.execFile)(path, ["--version"], { timeout: 3e4, windowsHide: true, encoding: "utf8" }, (error, stdout) => {
1850
- resolve3(error ? null : parseLauncherVersion(stdout, bin));
1851
- });
1852
- });
1853
- }
1854
- function removeStale(file) {
1855
- const prefix = `${(0, import_node_path6.basename)(file)}.old-`;
1856
- let names;
1857
- try {
1858
- names = (0, import_node_fs11.readdirSync)((0, import_node_path6.dirname)(file));
1859
- } catch {
1860
- return;
1861
- }
1862
- for (const name of names) {
1863
- if (!name.startsWith(prefix)) continue;
1864
- try {
1865
- (0, import_node_fs11.rmSync)((0, import_node_path6.join)((0, import_node_path6.dirname)(file), name), { force: true });
1866
- } catch {
1867
- }
1868
- }
1869
- }
1870
- async function refreshLauncher(target, options = {}) {
1871
- const started = Date.now();
1872
- const platform = options.platform ?? process.platform;
1873
- const key = platformKey(platform, options.arch ?? process.arch);
1874
- if (!key) return null;
1875
- const productDir = options.productDir ?? defaultProductDir(target.product);
1876
- const recorded = recordedPath(productDir);
1877
- const file = options.path ?? (recorded && (0, import_node_fs11.existsSync)(recorded) ? recorded : installedPath(target, productDir, platform));
1878
- if (!(0, import_node_fs11.existsSync)(file) || (0, import_node_fs11.lstatSync)(file).isSymbolicLink()) return null;
1879
- if (!options.dryRun) removeStale(file);
1880
- const from = await (options.readVersion ?? ((path) => runVersion(path, target.bin)))(file);
1881
- if (!from) return null;
1882
- const arm = (verdict, to, detail) => ({ surface: "launcher", from, to, verdict, detail, durationMs: Date.now() - started });
1883
- const record = await fetchRecord(target, options);
1884
- if (record === "bad-signature") return arm("fail", from, "The launcher record failed its signature check.");
1885
- const entry2 = record?.files.find((candidate) => candidate.path === key);
1886
- if (!record || !entry2) return arm("skip", from, "No launcher update is offered yet.");
1887
- if (compareVersions(record.version, from) <= 0) return arm("ok", from, "");
1888
- if (options.dryRun) return arm("ok", record.version, "");
1889
- let unlock;
1890
- const lockHeld = options.lockHeld ?? process.env.MM_OUTER_CONSOLE === "1";
1891
- if (!lockHeld) {
1892
- try {
1893
- unlock = lockInstallation(productDir);
1894
- } catch {
1895
- return arm("defer", from, "Another install is running; the launcher updates next time.");
1896
- }
1897
- }
1898
- const next = `${file}.new-${process.pid}`;
1899
- let moved;
1900
- try {
1901
- let bytes;
1902
- try {
1903
- const response = await (options.fetch ?? fetch)(
1904
- `${(target.dl ?? target.host).replace(/\/+$/u, "")}/dl/${target.product}/${key}`,
1905
- { signal: AbortSignal.timeout(3e5) }
1906
- );
1907
- if (!response.ok) return arm("defer", from, "The launcher download did not complete; it retries next time.");
1908
- bytes = await readDownload(response, void 0, entry2.size);
1909
- } catch {
1910
- return arm("defer", from, "The launcher download did not complete; it retries next time.");
1911
- }
1912
- if (!verifyFileBytes(entry2, bytes)) return arm("fail", from, "The downloaded launcher failed its checksum.");
1913
- try {
1914
- (0, import_node_fs11.writeFileSync)(next, bytes, { mode: 493 });
1915
- if (platform !== "win32") (0, import_node_fs11.chmodSync)(next, 493);
1916
- if (platform === "win32") {
1917
- moved = `${file}.old-${process.pid}-${Date.now()}`;
1918
- (0, import_node_fs11.renameSync)(file, moved);
1919
- }
1920
- (0, import_node_fs11.renameSync)(next, file);
1921
- } catch {
1922
- try {
1923
- if (moved && !(0, import_node_fs11.existsSync)(file)) (0, import_node_fs11.renameSync)(moved, file);
1924
- } catch {
1925
- }
1926
- (0, import_node_fs11.rmSync)(next, { force: true });
1927
- return arm("fail", from, "The launcher could not be replaced.");
1928
- }
1929
- return arm("ok", record.version, "");
1930
- } finally {
1931
- unlock?.();
1932
- }
1933
- }
1934
-
1935
1765
  // src/login-github.ts
1936
- var import_node_child_process5 = require("node:child_process");
1766
+ var import_node_child_process3 = require("node:child_process");
1937
1767
  var realSleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
1938
1768
  function openBrowser(url) {
1939
1769
  if (process.env.LAUNCHER_NO_OPEN === "1") return;
1940
1770
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
1941
1771
  const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
1942
1772
  try {
1943
- const child = (0, import_node_child_process5.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
1773
+ const child = (0, import_node_child_process3.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
1944
1774
  child.on("error", () => {
1945
1775
  });
1946
1776
  child.unref();
@@ -2058,7 +1888,7 @@ async function refreshAccessToken(config, refreshToken, fetchImpl = fetch) {
2058
1888
  }
2059
1889
  function readGhToken() {
2060
1890
  try {
2061
- const result = (0, import_node_child_process5.spawnSync)("gh", ["auth", "token", "--hostname", "github.com"], { encoding: "utf8", timeout: 1e4, windowsHide: true });
1891
+ const result = (0, import_node_child_process3.spawnSync)("gh", ["auth", "token", "--hostname", "github.com"], { encoding: "utf8", timeout: 1e4, windowsHide: true });
2062
1892
  const token = result.status === 0 ? String(result.stdout).trim() : "";
2063
1893
  return token && !/\s/u.test(token) ? token : null;
2064
1894
  } catch {
@@ -2192,8 +2022,201 @@ function page(title, body) {
2192
2022
  return `<!doctype html><meta charset="utf-8"><meta name="color-scheme" content="dark"><title>${title}</title><style>${style}</style><main><h1>${title}</h1><p>${body}</p></main>`;
2193
2023
  }
2194
2024
 
2025
+ // src/tokens.ts
2026
+ async function ensureFreshToken(config, dir, fetchImpl = fetch) {
2027
+ const stored = readTokens(dir);
2028
+ if (!stored) return null;
2029
+ if (stored.expiresAt - Date.now() > 6e4) return stored.accessToken;
2030
+ if (!stored.refreshToken) return null;
2031
+ if (config.loginKind === "github") {
2032
+ const refreshed2 = await refreshAccessToken(config, stored.refreshToken, fetchImpl);
2033
+ if (!refreshed2) {
2034
+ clearTokens(dir);
2035
+ return null;
2036
+ }
2037
+ writeTokens(dir, {
2038
+ accessToken: refreshed2.accessToken,
2039
+ refreshToken: refreshed2.refreshToken ?? stored.refreshToken,
2040
+ expiresAt: Date.now() + refreshed2.expiresIn * 1e3
2041
+ });
2042
+ return refreshed2.accessToken;
2043
+ }
2044
+ if (!stored.clientId) {
2045
+ clearTokens(dir);
2046
+ return null;
2047
+ }
2048
+ const refreshed = await refreshGoogleToken(config.host, stored.clientId, stored.refreshToken, fetchImpl);
2049
+ if (!refreshed) {
2050
+ clearTokens(dir);
2051
+ return null;
2052
+ }
2053
+ writeTokens(dir, {
2054
+ accessToken: refreshed.accessToken,
2055
+ refreshToken: refreshed.refreshToken,
2056
+ expiresAt: Date.now() + refreshed.expiresIn * 1e3,
2057
+ clientId: stored.clientId
2058
+ });
2059
+ return refreshed.accessToken;
2060
+ }
2061
+
2062
+ // src/refresh.ts
2063
+ var import_node_child_process4 = require("node:child_process");
2064
+ var import_node_fs11 = require("node:fs");
2065
+ var import_node_os4 = require("node:os");
2066
+ var import_node_path6 = require("node:path");
2067
+ var LAUNCHER_RECORD_FILE = "launcher.json";
2068
+ var RECORD_TIMEOUT_MS = 1e4;
2069
+ function platformKey(platform, arch) {
2070
+ if (platform === "win32") return arch === "x64" || arch === "arm64" ? `win-${arch}` : null;
2071
+ return platform === "darwin" && arch === "arm64" ? "darwin-arm64" : null;
2072
+ }
2073
+ function verifierFor(target) {
2074
+ return { product: target.product, host: target.host, loginKind: "github", publicKey: target.key, binName: target.bin };
2075
+ }
2076
+ async function fetchRecord(target, options) {
2077
+ let json;
2078
+ try {
2079
+ const response = await (options.fetch ?? fetch)(
2080
+ `${target.host.replace(/\/+$/u, "")}/release/launcher`,
2081
+ { signal: AbortSignal.timeout(options.timeoutMs ?? RECORD_TIMEOUT_MS) }
2082
+ );
2083
+ if (!response.ok) return null;
2084
+ json = await response.json();
2085
+ } catch {
2086
+ return null;
2087
+ }
2088
+ let record;
2089
+ try {
2090
+ record = parseManifest(json);
2091
+ } catch {
2092
+ return null;
2093
+ }
2094
+ if (!verifyManifest(record, record.signature, verifierFor(target))) return "bad-signature";
2095
+ return { version: record.version, files: record.files };
2096
+ }
2097
+ async function readLauncherRecord(target, options = {}) {
2098
+ const record = await fetchRecord(target, options);
2099
+ return record === "bad-signature" ? null : record;
2100
+ }
2101
+ function compareVersions(a, b) {
2102
+ const split = (v) => {
2103
+ const [core, pre = ""] = v.split("-", 2);
2104
+ return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
2105
+ };
2106
+ const x = split(a);
2107
+ const y = split(b);
2108
+ for (let i = 0; i < 3; i++) if ((x.nums[i] ?? 0) !== (y.nums[i] ?? 0)) return (x.nums[i] ?? 0) > (y.nums[i] ?? 0) ? 1 : -1;
2109
+ if (x.pre === y.pre) return 0;
2110
+ if (!x.pre) return 1;
2111
+ if (!y.pre) return -1;
2112
+ return x.pre > y.pre ? 1 : -1;
2113
+ }
2114
+ function recordedPath(productDir) {
2115
+ try {
2116
+ const saved = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path6.join)(productDir, LAUNCHER_RECORD_FILE), "utf8"));
2117
+ return typeof saved.path === "string" && saved.path ? saved.path : null;
2118
+ } catch {
2119
+ return null;
2120
+ }
2121
+ }
2122
+ function installedPath(target, productDir, platform) {
2123
+ return platform === "win32" ? (0, import_node_path6.join)(productDir, "bin", `${target.bin}.exe`) : (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".local", "bin", target.bin);
2124
+ }
2125
+ function parseLauncherVersion(stdout, bin) {
2126
+ const text = String(stdout).trim();
2127
+ if (!text.startsWith(`${bin} `)) return null;
2128
+ return text.match(/launcher (\d+\.\d+\.\d+[^\s)]*)/u)?.[1] ?? null;
2129
+ }
2130
+ function runVersion(path, bin) {
2131
+ return new Promise((resolve3) => {
2132
+ (0, import_node_child_process4.execFile)(path, ["--version"], { timeout: 3e4, windowsHide: true, encoding: "utf8" }, (error, stdout) => {
2133
+ resolve3(error ? null : parseLauncherVersion(stdout, bin));
2134
+ });
2135
+ });
2136
+ }
2137
+ function removeStale(file) {
2138
+ const prefix = `${(0, import_node_path6.basename)(file)}.old-`;
2139
+ let names;
2140
+ try {
2141
+ names = (0, import_node_fs11.readdirSync)((0, import_node_path6.dirname)(file));
2142
+ } catch {
2143
+ return;
2144
+ }
2145
+ for (const name of names) {
2146
+ if (!name.startsWith(prefix)) continue;
2147
+ try {
2148
+ (0, import_node_fs11.rmSync)((0, import_node_path6.join)((0, import_node_path6.dirname)(file), name), { force: true });
2149
+ } catch {
2150
+ }
2151
+ }
2152
+ }
2153
+ async function refreshLauncher(target, options = {}) {
2154
+ const started = Date.now();
2155
+ const platform = options.platform ?? process.platform;
2156
+ const key = platformKey(platform, options.arch ?? process.arch);
2157
+ if (!key) return null;
2158
+ const productDir = options.productDir ?? defaultProductDir(target.product);
2159
+ const recorded = recordedPath(productDir);
2160
+ const file = options.path ?? (recorded && (0, import_node_fs11.existsSync)(recorded) ? recorded : installedPath(target, productDir, platform));
2161
+ if (!(0, import_node_fs11.existsSync)(file) || (0, import_node_fs11.lstatSync)(file).isSymbolicLink()) return null;
2162
+ if (!options.dryRun) removeStale(file);
2163
+ const from = await (options.readVersion ?? ((path) => runVersion(path, target.bin)))(file);
2164
+ if (!from) return null;
2165
+ const arm = (verdict, to, detail) => ({ surface: "launcher", from, to, verdict, detail, durationMs: Date.now() - started });
2166
+ const record = await fetchRecord(target, options);
2167
+ if (record === "bad-signature") return arm("fail", from, "The launcher record failed its signature check.");
2168
+ const entry2 = record?.files.find((candidate) => candidate.path === key);
2169
+ if (!record || !entry2) return arm("skip", from, "No launcher update is offered yet.");
2170
+ if (compareVersions(record.version, from) <= 0) return arm("ok", from, "");
2171
+ if (options.dryRun) return arm("ok", record.version, "");
2172
+ let unlock;
2173
+ const lockHeld = options.lockHeld ?? process.env.MM_OUTER_CONSOLE === "1";
2174
+ if (!lockHeld) {
2175
+ try {
2176
+ unlock = lockInstallation(productDir);
2177
+ } catch {
2178
+ return arm("defer", from, "Another install is running; the launcher updates next time.");
2179
+ }
2180
+ }
2181
+ const next = `${file}.new-${process.pid}`;
2182
+ let moved;
2183
+ try {
2184
+ let bytes;
2185
+ try {
2186
+ const response = await (options.fetch ?? fetch)(
2187
+ `${(target.dl ?? target.host).replace(/\/+$/u, "")}/dl/${target.product}/${key}`,
2188
+ { signal: AbortSignal.timeout(3e5) }
2189
+ );
2190
+ if (!response.ok) return arm("defer", from, "The launcher download did not complete; it retries next time.");
2191
+ bytes = await readDownload(response, void 0, entry2.size);
2192
+ } catch {
2193
+ return arm("defer", from, "The launcher download did not complete; it retries next time.");
2194
+ }
2195
+ if (!verifyFileBytes(entry2, bytes)) return arm("fail", from, "The downloaded launcher failed its checksum.");
2196
+ try {
2197
+ (0, import_node_fs11.writeFileSync)(next, bytes, { mode: 493 });
2198
+ if (platform !== "win32") (0, import_node_fs11.chmodSync)(next, 493);
2199
+ if (platform === "win32") {
2200
+ moved = `${file}.old-${process.pid}-${Date.now()}`;
2201
+ (0, import_node_fs11.renameSync)(file, moved);
2202
+ }
2203
+ (0, import_node_fs11.renameSync)(next, file);
2204
+ } catch {
2205
+ try {
2206
+ if (moved && !(0, import_node_fs11.existsSync)(file)) (0, import_node_fs11.renameSync)(moved, file);
2207
+ } catch {
2208
+ }
2209
+ (0, import_node_fs11.rmSync)(next, { force: true });
2210
+ return arm("fail", from, "The launcher could not be replaced.");
2211
+ }
2212
+ return arm("ok", record.version, "");
2213
+ } finally {
2214
+ unlock?.();
2215
+ }
2216
+ }
2217
+
2195
2218
  // src/index.ts
2196
- var LAUNCHER_VERSION = true ? "1.2.2" : readVersionFromPackage();
2219
+ var LAUNCHER_VERSION = true ? "1.2.4" : readVersionFromPackage();
2197
2220
  function defaultPrint(message) {
2198
2221
  process.stdout.write(`${message}
2199
2222
  `);
@@ -2416,41 +2439,6 @@ async function runFile(file, args, printErr) {
2416
2439
  }
2417
2440
  return code;
2418
2441
  }
2419
- async function ensureFreshToken(config, dir, fetchImpl = fetch) {
2420
- const stored = readTokens(dir);
2421
- if (!stored) return null;
2422
- if (stored.expiresAt - Date.now() > 6e4) return stored.accessToken;
2423
- if (!stored.refreshToken) return null;
2424
- if (config.loginKind === "github") {
2425
- const refreshed2 = await refreshAccessToken(config, stored.refreshToken, fetchImpl);
2426
- if (!refreshed2) {
2427
- clearTokens(dir);
2428
- return null;
2429
- }
2430
- writeTokens(dir, {
2431
- accessToken: refreshed2.accessToken,
2432
- refreshToken: refreshed2.refreshToken ?? stored.refreshToken,
2433
- expiresAt: Date.now() + refreshed2.expiresIn * 1e3
2434
- });
2435
- return refreshed2.accessToken;
2436
- }
2437
- if (!stored.clientId) {
2438
- clearTokens(dir);
2439
- return null;
2440
- }
2441
- const refreshed = await refreshGoogleToken(config.host, stored.clientId, stored.refreshToken, fetchImpl);
2442
- if (!refreshed) {
2443
- clearTokens(dir);
2444
- return null;
2445
- }
2446
- writeTokens(dir, {
2447
- accessToken: refreshed.accessToken,
2448
- refreshToken: refreshed.refreshToken,
2449
- expiresAt: Date.now() + refreshed.expiresIn * 1e3,
2450
- clientId: stored.clientId
2451
- });
2452
- return refreshed.accessToken;
2453
- }
2454
2442
  async function doLogin(config, dir, options, prompt, installer) {
2455
2443
  const fetchImpl = options.fetchImpl ?? fetch;
2456
2444
  const open = options.open ?? openBrowser;
@@ -2569,7 +2557,7 @@ function defaultRunEntry(entry2, cwd, env) {
2569
2557
  const [command2, ...args] = entry2;
2570
2558
  const shell = needsShell(command2);
2571
2559
  const commandLine = shell ? [command2, ...args].map(quoteForShell).join(" ") : command2;
2572
- const spawnEntry = (progress2) => (0, import_node_child_process6.spawnSync)(commandLine, shell ? [] : args, {
2560
+ const spawnEntry = (progress2) => (0, import_node_child_process5.spawnSync)(commandLine, shell ? [] : args, {
2573
2561
  cwd,
2574
2562
  stdio: progress2 ? ["inherit", "inherit", "inherit", "pipe"] : "inherit",
2575
2563
  shell,
@@ -2861,7 +2849,7 @@ async function runInstallEntry(entry2, cwd, env, installer, tokens = null) {
2861
2849
  const redact = (text) => secrets.reduce((safe, value) => safe.replaceAll(value, "[redacted]"), text);
2862
2850
  try {
2863
2851
  const result = await new Promise((resolve3) => {
2864
- const child = (0, import_node_child_process6.spawn)(shell ? [command2, ...args].map(quoteForShell).join(" ") : command2, shell ? [] : args, {
2852
+ const child = (0, import_node_child_process5.spawn)(shell ? [command2, ...args].map(quoteForShell).join(" ") : command2, shell ? [] : args, {
2865
2853
  cwd,
2866
2854
  shell,
2867
2855
  windowsHide: true,
@@ -3099,7 +3087,6 @@ if (invokedAsMain) {
3099
3087
  0 && (module.exports = {
3100
3088
  LAUNCHER_VERSION,
3101
3089
  defaultRunEntry,
3102
- ensureFreshToken,
3103
3090
  formatMb,
3104
3091
  parseArgv,
3105
3092
  readPayloadEntry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/installer-launcher",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Single-executable (SEA) launcher: sign-in, gated payload fetch, self-update. One copy ships per product.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",