@mutmutco/installer-launcher 1.2.3 → 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 +194 -193
- package/dist/launcher.sea.cjs +197 -197
- package/package.json +1 -1
package/dist/launcher.js
CHANGED
|
@@ -1729,162 +1729,6 @@ function firstLine(text) {
|
|
|
1729
1729
|
return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
|
|
1730
1730
|
}
|
|
1731
1731
|
|
|
1732
|
-
// src/refresh.ts
|
|
1733
|
-
import { execFile } from "node:child_process";
|
|
1734
|
-
import { chmodSync, existsSync as existsSync7, lstatSync, readdirSync, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1735
|
-
import { homedir } from "node:os";
|
|
1736
|
-
import { basename, dirname as dirname3, join as join6 } from "node:path";
|
|
1737
|
-
var LAUNCHER_RECORD_FILE = "launcher.json";
|
|
1738
|
-
var RECORD_TIMEOUT_MS = 1e4;
|
|
1739
|
-
function platformKey(platform, arch) {
|
|
1740
|
-
if (platform === "win32") return arch === "x64" || arch === "arm64" ? `win-${arch}` : null;
|
|
1741
|
-
return platform === "darwin" && arch === "arm64" ? "darwin-arm64" : null;
|
|
1742
|
-
}
|
|
1743
|
-
function verifierFor(target) {
|
|
1744
|
-
return { product: target.product, host: target.host, loginKind: "github", publicKey: target.key, binName: target.bin };
|
|
1745
|
-
}
|
|
1746
|
-
async function fetchRecord(target, options) {
|
|
1747
|
-
let json;
|
|
1748
|
-
try {
|
|
1749
|
-
const response = await (options.fetch ?? fetch)(
|
|
1750
|
-
`${target.host.replace(/\/+$/u, "")}/release/launcher`,
|
|
1751
|
-
{ signal: AbortSignal.timeout(options.timeoutMs ?? RECORD_TIMEOUT_MS) }
|
|
1752
|
-
);
|
|
1753
|
-
if (!response.ok) return null;
|
|
1754
|
-
json = await response.json();
|
|
1755
|
-
} catch {
|
|
1756
|
-
return null;
|
|
1757
|
-
}
|
|
1758
|
-
let record;
|
|
1759
|
-
try {
|
|
1760
|
-
record = parseManifest(json);
|
|
1761
|
-
} catch {
|
|
1762
|
-
return null;
|
|
1763
|
-
}
|
|
1764
|
-
if (!verifyManifest(record, record.signature, verifierFor(target))) return "bad-signature";
|
|
1765
|
-
return { version: record.version, files: record.files };
|
|
1766
|
-
}
|
|
1767
|
-
async function readLauncherRecord(target, options = {}) {
|
|
1768
|
-
const record = await fetchRecord(target, options);
|
|
1769
|
-
return record === "bad-signature" ? null : record;
|
|
1770
|
-
}
|
|
1771
|
-
function compareVersions(a, b) {
|
|
1772
|
-
const split = (v) => {
|
|
1773
|
-
const [core, pre = ""] = v.split("-", 2);
|
|
1774
|
-
return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
|
|
1775
|
-
};
|
|
1776
|
-
const x = split(a);
|
|
1777
|
-
const y = split(b);
|
|
1778
|
-
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;
|
|
1779
|
-
if (x.pre === y.pre) return 0;
|
|
1780
|
-
if (!x.pre) return 1;
|
|
1781
|
-
if (!y.pre) return -1;
|
|
1782
|
-
return x.pre > y.pre ? 1 : -1;
|
|
1783
|
-
}
|
|
1784
|
-
function recordedPath(productDir) {
|
|
1785
|
-
try {
|
|
1786
|
-
const saved = JSON.parse(readFileSync7(join6(productDir, LAUNCHER_RECORD_FILE), "utf8"));
|
|
1787
|
-
return typeof saved.path === "string" && saved.path ? saved.path : null;
|
|
1788
|
-
} catch {
|
|
1789
|
-
return null;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
function installedPath(target, productDir, platform) {
|
|
1793
|
-
return platform === "win32" ? join6(productDir, "bin", `${target.bin}.exe`) : join6(homedir(), ".local", "bin", target.bin);
|
|
1794
|
-
}
|
|
1795
|
-
function parseLauncherVersion(stdout, bin) {
|
|
1796
|
-
const text = String(stdout).trim();
|
|
1797
|
-
if (!text.startsWith(`${bin} `)) return null;
|
|
1798
|
-
return text.match(/launcher (\d+\.\d+\.\d+[^\s)]*)/u)?.[1] ?? null;
|
|
1799
|
-
}
|
|
1800
|
-
function runVersion(path, bin) {
|
|
1801
|
-
return new Promise((resolve3) => {
|
|
1802
|
-
execFile(path, ["--version"], { timeout: 3e4, windowsHide: true, encoding: "utf8" }, (error, stdout) => {
|
|
1803
|
-
resolve3(error ? null : parseLauncherVersion(stdout, bin));
|
|
1804
|
-
});
|
|
1805
|
-
});
|
|
1806
|
-
}
|
|
1807
|
-
function removeStale(file) {
|
|
1808
|
-
const prefix = `${basename(file)}.old-`;
|
|
1809
|
-
let names;
|
|
1810
|
-
try {
|
|
1811
|
-
names = readdirSync(dirname3(file));
|
|
1812
|
-
} catch {
|
|
1813
|
-
return;
|
|
1814
|
-
}
|
|
1815
|
-
for (const name of names) {
|
|
1816
|
-
if (!name.startsWith(prefix)) continue;
|
|
1817
|
-
try {
|
|
1818
|
-
rmSync5(join6(dirname3(file), name), { force: true });
|
|
1819
|
-
} catch {
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
async function refreshLauncher(target, options = {}) {
|
|
1824
|
-
const started = Date.now();
|
|
1825
|
-
const platform = options.platform ?? process.platform;
|
|
1826
|
-
const key = platformKey(platform, options.arch ?? process.arch);
|
|
1827
|
-
if (!key) return null;
|
|
1828
|
-
const productDir = options.productDir ?? defaultProductDir(target.product);
|
|
1829
|
-
const recorded = recordedPath(productDir);
|
|
1830
|
-
const file = options.path ?? (recorded && existsSync7(recorded) ? recorded : installedPath(target, productDir, platform));
|
|
1831
|
-
if (!existsSync7(file) || lstatSync(file).isSymbolicLink()) return null;
|
|
1832
|
-
if (!options.dryRun) removeStale(file);
|
|
1833
|
-
const from = await (options.readVersion ?? ((path) => runVersion(path, target.bin)))(file);
|
|
1834
|
-
if (!from) return null;
|
|
1835
|
-
const arm = (verdict, to, detail) => ({ surface: "launcher", from, to, verdict, detail, durationMs: Date.now() - started });
|
|
1836
|
-
const record = await fetchRecord(target, options);
|
|
1837
|
-
if (record === "bad-signature") return arm("fail", from, "The launcher record failed its signature check.");
|
|
1838
|
-
const entry2 = record?.files.find((candidate) => candidate.path === key);
|
|
1839
|
-
if (!record || !entry2) return arm("skip", from, "No launcher update is offered yet.");
|
|
1840
|
-
if (compareVersions(record.version, from) <= 0) return arm("ok", from, "");
|
|
1841
|
-
if (options.dryRun) return arm("ok", record.version, "");
|
|
1842
|
-
let unlock;
|
|
1843
|
-
const lockHeld = options.lockHeld ?? process.env.MM_OUTER_CONSOLE === "1";
|
|
1844
|
-
if (!lockHeld) {
|
|
1845
|
-
try {
|
|
1846
|
-
unlock = lockInstallation(productDir);
|
|
1847
|
-
} catch {
|
|
1848
|
-
return arm("defer", from, "Another install is running; the launcher updates next time.");
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
const next = `${file}.new-${process.pid}`;
|
|
1852
|
-
let moved;
|
|
1853
|
-
try {
|
|
1854
|
-
let bytes;
|
|
1855
|
-
try {
|
|
1856
|
-
const response = await (options.fetch ?? fetch)(
|
|
1857
|
-
`${(target.dl ?? target.host).replace(/\/+$/u, "")}/dl/${target.product}/${key}`,
|
|
1858
|
-
{ signal: AbortSignal.timeout(3e5) }
|
|
1859
|
-
);
|
|
1860
|
-
if (!response.ok) return arm("defer", from, "The launcher download did not complete; it retries next time.");
|
|
1861
|
-
bytes = await readDownload(response, void 0, entry2.size);
|
|
1862
|
-
} catch {
|
|
1863
|
-
return arm("defer", from, "The launcher download did not complete; it retries next time.");
|
|
1864
|
-
}
|
|
1865
|
-
if (!verifyFileBytes(entry2, bytes)) return arm("fail", from, "The downloaded launcher failed its checksum.");
|
|
1866
|
-
try {
|
|
1867
|
-
writeFileSync7(next, bytes, { mode: 493 });
|
|
1868
|
-
if (platform !== "win32") chmodSync(next, 493);
|
|
1869
|
-
if (platform === "win32") {
|
|
1870
|
-
moved = `${file}.old-${process.pid}-${Date.now()}`;
|
|
1871
|
-
renameSync4(file, moved);
|
|
1872
|
-
}
|
|
1873
|
-
renameSync4(next, file);
|
|
1874
|
-
} catch {
|
|
1875
|
-
try {
|
|
1876
|
-
if (moved && !existsSync7(file)) renameSync4(moved, file);
|
|
1877
|
-
} catch {
|
|
1878
|
-
}
|
|
1879
|
-
rmSync5(next, { force: true });
|
|
1880
|
-
return arm("fail", from, "The launcher could not be replaced.");
|
|
1881
|
-
}
|
|
1882
|
-
return arm("ok", record.version, "");
|
|
1883
|
-
} finally {
|
|
1884
|
-
unlock?.();
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
1732
|
// src/login-github.ts
|
|
1889
1733
|
import { spawn, spawnSync as spawnSync3 } from "node:child_process";
|
|
1890
1734
|
var realSleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
@@ -2145,8 +1989,201 @@ function page(title, body) {
|
|
|
2145
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>`;
|
|
2146
1990
|
}
|
|
2147
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
|
+
|
|
2148
2185
|
// src/index.ts
|
|
2149
|
-
var LAUNCHER_VERSION = true ? "1.2.
|
|
2186
|
+
var LAUNCHER_VERSION = true ? "1.2.4" : readVersionFromPackage();
|
|
2150
2187
|
function defaultPrint(message) {
|
|
2151
2188
|
process.stdout.write(`${message}
|
|
2152
2189
|
`);
|
|
@@ -2369,41 +2406,6 @@ async function runFile(file, args, printErr) {
|
|
|
2369
2406
|
}
|
|
2370
2407
|
return code;
|
|
2371
2408
|
}
|
|
2372
|
-
async function ensureFreshToken(config, dir, fetchImpl = fetch) {
|
|
2373
|
-
const stored = readTokens(dir);
|
|
2374
|
-
if (!stored) return null;
|
|
2375
|
-
if (stored.expiresAt - Date.now() > 6e4) return stored.accessToken;
|
|
2376
|
-
if (!stored.refreshToken) return null;
|
|
2377
|
-
if (config.loginKind === "github") {
|
|
2378
|
-
const refreshed2 = await refreshAccessToken(config, stored.refreshToken, fetchImpl);
|
|
2379
|
-
if (!refreshed2) {
|
|
2380
|
-
clearTokens(dir);
|
|
2381
|
-
return null;
|
|
2382
|
-
}
|
|
2383
|
-
writeTokens(dir, {
|
|
2384
|
-
accessToken: refreshed2.accessToken,
|
|
2385
|
-
refreshToken: refreshed2.refreshToken ?? stored.refreshToken,
|
|
2386
|
-
expiresAt: Date.now() + refreshed2.expiresIn * 1e3
|
|
2387
|
-
});
|
|
2388
|
-
return refreshed2.accessToken;
|
|
2389
|
-
}
|
|
2390
|
-
if (!stored.clientId) {
|
|
2391
|
-
clearTokens(dir);
|
|
2392
|
-
return null;
|
|
2393
|
-
}
|
|
2394
|
-
const refreshed = await refreshGoogleToken(config.host, stored.clientId, stored.refreshToken, fetchImpl);
|
|
2395
|
-
if (!refreshed) {
|
|
2396
|
-
clearTokens(dir);
|
|
2397
|
-
return null;
|
|
2398
|
-
}
|
|
2399
|
-
writeTokens(dir, {
|
|
2400
|
-
accessToken: refreshed.accessToken,
|
|
2401
|
-
refreshToken: refreshed.refreshToken,
|
|
2402
|
-
expiresAt: Date.now() + refreshed.expiresIn * 1e3,
|
|
2403
|
-
clientId: stored.clientId
|
|
2404
|
-
});
|
|
2405
|
-
return refreshed.accessToken;
|
|
2406
|
-
}
|
|
2407
2409
|
async function doLogin(config, dir, options, prompt, installer) {
|
|
2408
2410
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
2409
2411
|
const open = options.open ?? openBrowser;
|
|
@@ -3051,7 +3053,6 @@ if (invokedAsMain) {
|
|
|
3051
3053
|
export {
|
|
3052
3054
|
LAUNCHER_VERSION,
|
|
3053
3055
|
defaultRunEntry,
|
|
3054
|
-
ensureFreshToken,
|
|
3055
3056
|
formatMb,
|
|
3056
3057
|
parseArgv,
|
|
3057
3058
|
readPayloadEntry,
|
package/dist/launcher.sea.cjs
CHANGED
|
@@ -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,
|
|
@@ -1763,171 +1762,15 @@ function firstLine(text) {
|
|
|
1763
1762
|
return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
|
|
1764
1763
|
}
|
|
1765
1764
|
|
|
1766
|
-
// src/refresh.ts
|
|
1767
|
-
var import_node_child_process3 = require("node:child_process");
|
|
1768
|
-
var import_node_fs11 = require("node:fs");
|
|
1769
|
-
var import_node_os4 = require("node:os");
|
|
1770
|
-
var import_node_path6 = require("node:path");
|
|
1771
|
-
var LAUNCHER_RECORD_FILE = "launcher.json";
|
|
1772
|
-
var RECORD_TIMEOUT_MS = 1e4;
|
|
1773
|
-
function platformKey(platform, arch) {
|
|
1774
|
-
if (platform === "win32") return arch === "x64" || arch === "arm64" ? `win-${arch}` : null;
|
|
1775
|
-
return platform === "darwin" && arch === "arm64" ? "darwin-arm64" : null;
|
|
1776
|
-
}
|
|
1777
|
-
function verifierFor(target) {
|
|
1778
|
-
return { product: target.product, host: target.host, loginKind: "github", publicKey: target.key, binName: target.bin };
|
|
1779
|
-
}
|
|
1780
|
-
async function fetchRecord(target, options) {
|
|
1781
|
-
let json;
|
|
1782
|
-
try {
|
|
1783
|
-
const response = await (options.fetch ?? fetch)(
|
|
1784
|
-
`${target.host.replace(/\/+$/u, "")}/release/launcher`,
|
|
1785
|
-
{ signal: AbortSignal.timeout(options.timeoutMs ?? RECORD_TIMEOUT_MS) }
|
|
1786
|
-
);
|
|
1787
|
-
if (!response.ok) return null;
|
|
1788
|
-
json = await response.json();
|
|
1789
|
-
} catch {
|
|
1790
|
-
return null;
|
|
1791
|
-
}
|
|
1792
|
-
let record;
|
|
1793
|
-
try {
|
|
1794
|
-
record = parseManifest(json);
|
|
1795
|
-
} catch {
|
|
1796
|
-
return null;
|
|
1797
|
-
}
|
|
1798
|
-
if (!verifyManifest(record, record.signature, verifierFor(target))) return "bad-signature";
|
|
1799
|
-
return { version: record.version, files: record.files };
|
|
1800
|
-
}
|
|
1801
|
-
async function readLauncherRecord(target, options = {}) {
|
|
1802
|
-
const record = await fetchRecord(target, options);
|
|
1803
|
-
return record === "bad-signature" ? null : record;
|
|
1804
|
-
}
|
|
1805
|
-
function compareVersions(a, b) {
|
|
1806
|
-
const split = (v) => {
|
|
1807
|
-
const [core, pre = ""] = v.split("-", 2);
|
|
1808
|
-
return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
|
|
1809
|
-
};
|
|
1810
|
-
const x = split(a);
|
|
1811
|
-
const y = split(b);
|
|
1812
|
-
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;
|
|
1813
|
-
if (x.pre === y.pre) return 0;
|
|
1814
|
-
if (!x.pre) return 1;
|
|
1815
|
-
if (!y.pre) return -1;
|
|
1816
|
-
return x.pre > y.pre ? 1 : -1;
|
|
1817
|
-
}
|
|
1818
|
-
function recordedPath(productDir) {
|
|
1819
|
-
try {
|
|
1820
|
-
const saved = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path6.join)(productDir, LAUNCHER_RECORD_FILE), "utf8"));
|
|
1821
|
-
return typeof saved.path === "string" && saved.path ? saved.path : null;
|
|
1822
|
-
} catch {
|
|
1823
|
-
return null;
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
function installedPath(target, productDir, platform) {
|
|
1827
|
-
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);
|
|
1828
|
-
}
|
|
1829
|
-
function parseLauncherVersion(stdout, bin) {
|
|
1830
|
-
const text = String(stdout).trim();
|
|
1831
|
-
if (!text.startsWith(`${bin} `)) return null;
|
|
1832
|
-
return text.match(/launcher (\d+\.\d+\.\d+[^\s)]*)/u)?.[1] ?? null;
|
|
1833
|
-
}
|
|
1834
|
-
function runVersion(path, bin) {
|
|
1835
|
-
return new Promise((resolve3) => {
|
|
1836
|
-
(0, import_node_child_process3.execFile)(path, ["--version"], { timeout: 3e4, windowsHide: true, encoding: "utf8" }, (error, stdout) => {
|
|
1837
|
-
resolve3(error ? null : parseLauncherVersion(stdout, bin));
|
|
1838
|
-
});
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
function removeStale(file) {
|
|
1842
|
-
const prefix = `${(0, import_node_path6.basename)(file)}.old-`;
|
|
1843
|
-
let names;
|
|
1844
|
-
try {
|
|
1845
|
-
names = (0, import_node_fs11.readdirSync)((0, import_node_path6.dirname)(file));
|
|
1846
|
-
} catch {
|
|
1847
|
-
return;
|
|
1848
|
-
}
|
|
1849
|
-
for (const name of names) {
|
|
1850
|
-
if (!name.startsWith(prefix)) continue;
|
|
1851
|
-
try {
|
|
1852
|
-
(0, import_node_fs11.rmSync)((0, import_node_path6.join)((0, import_node_path6.dirname)(file), name), { force: true });
|
|
1853
|
-
} catch {
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
async function refreshLauncher(target, options = {}) {
|
|
1858
|
-
const started = Date.now();
|
|
1859
|
-
const platform = options.platform ?? process.platform;
|
|
1860
|
-
const key = platformKey(platform, options.arch ?? process.arch);
|
|
1861
|
-
if (!key) return null;
|
|
1862
|
-
const productDir = options.productDir ?? defaultProductDir(target.product);
|
|
1863
|
-
const recorded = recordedPath(productDir);
|
|
1864
|
-
const file = options.path ?? (recorded && (0, import_node_fs11.existsSync)(recorded) ? recorded : installedPath(target, productDir, platform));
|
|
1865
|
-
if (!(0, import_node_fs11.existsSync)(file) || (0, import_node_fs11.lstatSync)(file).isSymbolicLink()) return null;
|
|
1866
|
-
if (!options.dryRun) removeStale(file);
|
|
1867
|
-
const from = await (options.readVersion ?? ((path) => runVersion(path, target.bin)))(file);
|
|
1868
|
-
if (!from) return null;
|
|
1869
|
-
const arm = (verdict, to, detail) => ({ surface: "launcher", from, to, verdict, detail, durationMs: Date.now() - started });
|
|
1870
|
-
const record = await fetchRecord(target, options);
|
|
1871
|
-
if (record === "bad-signature") return arm("fail", from, "The launcher record failed its signature check.");
|
|
1872
|
-
const entry2 = record?.files.find((candidate) => candidate.path === key);
|
|
1873
|
-
if (!record || !entry2) return arm("skip", from, "No launcher update is offered yet.");
|
|
1874
|
-
if (compareVersions(record.version, from) <= 0) return arm("ok", from, "");
|
|
1875
|
-
if (options.dryRun) return arm("ok", record.version, "");
|
|
1876
|
-
let unlock;
|
|
1877
|
-
const lockHeld = options.lockHeld ?? process.env.MM_OUTER_CONSOLE === "1";
|
|
1878
|
-
if (!lockHeld) {
|
|
1879
|
-
try {
|
|
1880
|
-
unlock = lockInstallation(productDir);
|
|
1881
|
-
} catch {
|
|
1882
|
-
return arm("defer", from, "Another install is running; the launcher updates next time.");
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
const next = `${file}.new-${process.pid}`;
|
|
1886
|
-
let moved;
|
|
1887
|
-
try {
|
|
1888
|
-
let bytes;
|
|
1889
|
-
try {
|
|
1890
|
-
const response = await (options.fetch ?? fetch)(
|
|
1891
|
-
`${(target.dl ?? target.host).replace(/\/+$/u, "")}/dl/${target.product}/${key}`,
|
|
1892
|
-
{ signal: AbortSignal.timeout(3e5) }
|
|
1893
|
-
);
|
|
1894
|
-
if (!response.ok) return arm("defer", from, "The launcher download did not complete; it retries next time.");
|
|
1895
|
-
bytes = await readDownload(response, void 0, entry2.size);
|
|
1896
|
-
} catch {
|
|
1897
|
-
return arm("defer", from, "The launcher download did not complete; it retries next time.");
|
|
1898
|
-
}
|
|
1899
|
-
if (!verifyFileBytes(entry2, bytes)) return arm("fail", from, "The downloaded launcher failed its checksum.");
|
|
1900
|
-
try {
|
|
1901
|
-
(0, import_node_fs11.writeFileSync)(next, bytes, { mode: 493 });
|
|
1902
|
-
if (platform !== "win32") (0, import_node_fs11.chmodSync)(next, 493);
|
|
1903
|
-
if (platform === "win32") {
|
|
1904
|
-
moved = `${file}.old-${process.pid}-${Date.now()}`;
|
|
1905
|
-
(0, import_node_fs11.renameSync)(file, moved);
|
|
1906
|
-
}
|
|
1907
|
-
(0, import_node_fs11.renameSync)(next, file);
|
|
1908
|
-
} catch {
|
|
1909
|
-
try {
|
|
1910
|
-
if (moved && !(0, import_node_fs11.existsSync)(file)) (0, import_node_fs11.renameSync)(moved, file);
|
|
1911
|
-
} catch {
|
|
1912
|
-
}
|
|
1913
|
-
(0, import_node_fs11.rmSync)(next, { force: true });
|
|
1914
|
-
return arm("fail", from, "The launcher could not be replaced.");
|
|
1915
|
-
}
|
|
1916
|
-
return arm("ok", record.version, "");
|
|
1917
|
-
} finally {
|
|
1918
|
-
unlock?.();
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
1765
|
// src/login-github.ts
|
|
1923
|
-
var
|
|
1766
|
+
var import_node_child_process3 = require("node:child_process");
|
|
1924
1767
|
var realSleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1925
1768
|
function openBrowser(url) {
|
|
1926
1769
|
if (process.env.LAUNCHER_NO_OPEN === "1") return;
|
|
1927
1770
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
1928
1771
|
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
1929
1772
|
try {
|
|
1930
|
-
const child = (0,
|
|
1773
|
+
const child = (0, import_node_child_process3.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
1931
1774
|
child.on("error", () => {
|
|
1932
1775
|
});
|
|
1933
1776
|
child.unref();
|
|
@@ -2045,7 +1888,7 @@ async function refreshAccessToken(config, refreshToken, fetchImpl = fetch) {
|
|
|
2045
1888
|
}
|
|
2046
1889
|
function readGhToken() {
|
|
2047
1890
|
try {
|
|
2048
|
-
const result = (0,
|
|
1891
|
+
const result = (0, import_node_child_process3.spawnSync)("gh", ["auth", "token", "--hostname", "github.com"], { encoding: "utf8", timeout: 1e4, windowsHide: true });
|
|
2049
1892
|
const token = result.status === 0 ? String(result.stdout).trim() : "";
|
|
2050
1893
|
return token && !/\s/u.test(token) ? token : null;
|
|
2051
1894
|
} catch {
|
|
@@ -2179,8 +2022,201 @@ function page(title, body) {
|
|
|
2179
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>`;
|
|
2180
2023
|
}
|
|
2181
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
|
+
|
|
2182
2218
|
// src/index.ts
|
|
2183
|
-
var LAUNCHER_VERSION = true ? "1.2.
|
|
2219
|
+
var LAUNCHER_VERSION = true ? "1.2.4" : readVersionFromPackage();
|
|
2184
2220
|
function defaultPrint(message) {
|
|
2185
2221
|
process.stdout.write(`${message}
|
|
2186
2222
|
`);
|
|
@@ -2403,41 +2439,6 @@ async function runFile(file, args, printErr) {
|
|
|
2403
2439
|
}
|
|
2404
2440
|
return code;
|
|
2405
2441
|
}
|
|
2406
|
-
async function ensureFreshToken(config, dir, fetchImpl = fetch) {
|
|
2407
|
-
const stored = readTokens(dir);
|
|
2408
|
-
if (!stored) return null;
|
|
2409
|
-
if (stored.expiresAt - Date.now() > 6e4) return stored.accessToken;
|
|
2410
|
-
if (!stored.refreshToken) return null;
|
|
2411
|
-
if (config.loginKind === "github") {
|
|
2412
|
-
const refreshed2 = await refreshAccessToken(config, stored.refreshToken, fetchImpl);
|
|
2413
|
-
if (!refreshed2) {
|
|
2414
|
-
clearTokens(dir);
|
|
2415
|
-
return null;
|
|
2416
|
-
}
|
|
2417
|
-
writeTokens(dir, {
|
|
2418
|
-
accessToken: refreshed2.accessToken,
|
|
2419
|
-
refreshToken: refreshed2.refreshToken ?? stored.refreshToken,
|
|
2420
|
-
expiresAt: Date.now() + refreshed2.expiresIn * 1e3
|
|
2421
|
-
});
|
|
2422
|
-
return refreshed2.accessToken;
|
|
2423
|
-
}
|
|
2424
|
-
if (!stored.clientId) {
|
|
2425
|
-
clearTokens(dir);
|
|
2426
|
-
return null;
|
|
2427
|
-
}
|
|
2428
|
-
const refreshed = await refreshGoogleToken(config.host, stored.clientId, stored.refreshToken, fetchImpl);
|
|
2429
|
-
if (!refreshed) {
|
|
2430
|
-
clearTokens(dir);
|
|
2431
|
-
return null;
|
|
2432
|
-
}
|
|
2433
|
-
writeTokens(dir, {
|
|
2434
|
-
accessToken: refreshed.accessToken,
|
|
2435
|
-
refreshToken: refreshed.refreshToken,
|
|
2436
|
-
expiresAt: Date.now() + refreshed.expiresIn * 1e3,
|
|
2437
|
-
clientId: stored.clientId
|
|
2438
|
-
});
|
|
2439
|
-
return refreshed.accessToken;
|
|
2440
|
-
}
|
|
2441
2442
|
async function doLogin(config, dir, options, prompt, installer) {
|
|
2442
2443
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
2443
2444
|
const open = options.open ?? openBrowser;
|
|
@@ -3086,7 +3087,6 @@ if (invokedAsMain) {
|
|
|
3086
3087
|
0 && (module.exports = {
|
|
3087
3088
|
LAUNCHER_VERSION,
|
|
3088
3089
|
defaultRunEntry,
|
|
3089
|
-
ensureFreshToken,
|
|
3090
3090
|
formatMb,
|
|
3091
3091
|
parseArgv,
|
|
3092
3092
|
readPayloadEntry,
|
package/package.json
CHANGED