@mutmutco/installer-launcher 0.1.2 → 0.1.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 +573 -77
- package/dist/launcher.sea.cjs +579 -81
- package/package.json +4 -1
package/dist/launcher.sea.cjs
CHANGED
|
@@ -24,18 +24,326 @@ __export(index_exports, {
|
|
|
24
24
|
defaultRunEntry: () => defaultRunEntry,
|
|
25
25
|
ensureFreshToken: () => ensureFreshToken,
|
|
26
26
|
readPayloadEntry: () => readPayloadEntry,
|
|
27
|
+
readPayloadRun: () => readPayloadRun,
|
|
28
|
+
readPayloadVerbs: () => readPayloadVerbs,
|
|
27
29
|
resolveEntry: () => resolveEntry,
|
|
28
30
|
run: () => run,
|
|
29
31
|
runFile: () => runFile
|
|
30
32
|
});
|
|
31
33
|
module.exports = __toCommonJS(index_exports);
|
|
32
|
-
var
|
|
33
|
-
var
|
|
34
|
-
var
|
|
34
|
+
var import_node_child_process3 = require("node:child_process");
|
|
35
|
+
var import_node_fs5 = require("node:fs");
|
|
36
|
+
var import_node_path4 = require("node:path");
|
|
35
37
|
var import_node_url2 = require("node:url");
|
|
36
38
|
|
|
37
|
-
//
|
|
39
|
+
// node_modules/@mutmutco/installer-face/dist/index.js
|
|
40
|
+
var PRODUCTS = Object.freeze({
|
|
41
|
+
"mm-strategy": Object.freeze({
|
|
42
|
+
name: "MM Strategy",
|
|
43
|
+
accent: "38;2;249;115;22",
|
|
44
|
+
warm: "Welcome. Let's set up MM Strategy \u2014 about a minute."
|
|
45
|
+
}),
|
|
46
|
+
"mmi-hub": Object.freeze({
|
|
47
|
+
name: "mmi-hub",
|
|
48
|
+
accent: "38;2;125;211;252",
|
|
49
|
+
warm: "Welcome back. Checking your surfaces\u2026"
|
|
50
|
+
}),
|
|
51
|
+
"jerv-hub": Object.freeze({
|
|
52
|
+
name: "jerv-hub",
|
|
53
|
+
accent: "38;2;248;113;113",
|
|
54
|
+
warm: "Welcome back. Checking your surfaces\u2026"
|
|
55
|
+
}),
|
|
56
|
+
jervcode: Object.freeze({
|
|
57
|
+
name: "JervCode",
|
|
58
|
+
accent: "38;2;192;132;252",
|
|
59
|
+
warm: "Welcome back. Keeping JervCode current\u2026"
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
function identityFor(product) {
|
|
63
|
+
const identity = PRODUCTS[product];
|
|
64
|
+
if (!identity) {
|
|
65
|
+
throw new Error(`unknown product ${JSON.stringify(product)} \u2014 add it to installer/face/src/products.ts rather than composing a greeting locally (known: ${Object.keys(PRODUCTS).join(", ")})`);
|
|
66
|
+
}
|
|
67
|
+
return identity;
|
|
68
|
+
}
|
|
69
|
+
var GLYPH = Object.freeze({
|
|
70
|
+
diamond: "\u25C6",
|
|
71
|
+
hollow: "\u25C7",
|
|
72
|
+
bar: "\u2502",
|
|
73
|
+
check: "\u2714",
|
|
74
|
+
cross: "\u2716",
|
|
75
|
+
dot: "\u25CF",
|
|
76
|
+
boxTop: "\u256D",
|
|
77
|
+
boxTopEnd: "\u256E",
|
|
78
|
+
boxBottom: "\u2570",
|
|
79
|
+
boxBottomEnd: "\u256F",
|
|
80
|
+
rule: "\u2500"
|
|
81
|
+
});
|
|
82
|
+
var PALETTE = Object.freeze({
|
|
83
|
+
green: "38;2;31;209;138",
|
|
84
|
+
ink: "38;2;237;237;237",
|
|
85
|
+
muted: "38;2;150;150;150",
|
|
86
|
+
line: "38;2;42;42;42"
|
|
87
|
+
});
|
|
88
|
+
var TITLE_COLUMN = 6;
|
|
89
|
+
var ANSI = /\u001b\[[0-9;]*m/g;
|
|
90
|
+
function visibleWidth(text) {
|
|
91
|
+
return [...String(text).replace(ANSI, "")].length;
|
|
92
|
+
}
|
|
93
|
+
function faceWidth(columns) {
|
|
94
|
+
const raw = Number(columns);
|
|
95
|
+
return Math.max(40, Math.min(100, Number.isFinite(raw) && raw > 0 ? raw : 100));
|
|
96
|
+
}
|
|
97
|
+
function wrapWords(text, width) {
|
|
98
|
+
const limit = Math.max(8, Math.floor(width));
|
|
99
|
+
const lines = [];
|
|
100
|
+
let line = "";
|
|
101
|
+
for (const word of String(text).split(/\s+/u).filter(Boolean)) {
|
|
102
|
+
let token = word;
|
|
103
|
+
while (visibleWidth(token) > limit) {
|
|
104
|
+
if (line) {
|
|
105
|
+
lines.push(line);
|
|
106
|
+
line = "";
|
|
107
|
+
}
|
|
108
|
+
lines.push([...token].slice(0, limit).join(""));
|
|
109
|
+
token = [...token].slice(limit).join("");
|
|
110
|
+
}
|
|
111
|
+
if (!line) line = token;
|
|
112
|
+
else if (visibleWidth(line) + 1 + visibleWidth(token) <= limit) line += ` ${token}`;
|
|
113
|
+
else {
|
|
114
|
+
lines.push(line);
|
|
115
|
+
line = token;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (line) lines.push(line);
|
|
119
|
+
return lines.length ? lines : [""];
|
|
120
|
+
}
|
|
121
|
+
function createFace({ product, color = false, columns }) {
|
|
122
|
+
const identity = identityFor(product);
|
|
123
|
+
const width = faceWidth(columns);
|
|
124
|
+
const paint = (sgr, text) => color ? `\x1B[${sgr}m${text}\x1B[0m` : String(text);
|
|
125
|
+
const bar = () => paint(PALETTE.muted, GLYPH.bar);
|
|
126
|
+
const indent = " ".repeat(TITLE_COLUMN - 1);
|
|
127
|
+
const welcome = () => [
|
|
128
|
+
`${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, identity.name)} \u2014 Mutatis Mutandis`,
|
|
129
|
+
bar(),
|
|
130
|
+
`${bar()} ${identity.warm}`,
|
|
131
|
+
bar()
|
|
132
|
+
];
|
|
133
|
+
const step = (title, seconds = null, kind = "ok") => {
|
|
134
|
+
const glyph = kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.hollow) : paint(PALETTE.green, GLYPH.check);
|
|
135
|
+
const time = seconds === null || seconds === void 0 ? "" : paint(PALETTE.muted, `${Math.max(0, Math.round(seconds))}s`);
|
|
136
|
+
const column = Math.min(44, Math.max(0, width - 8));
|
|
137
|
+
const reserved = time ? 6 : 0;
|
|
138
|
+
const [first, ...rest] = wrapWords(title, width - TITLE_COLUMN - reserved);
|
|
139
|
+
const head = `${bar()} ${glyph} ${first}`;
|
|
140
|
+
const pad = Math.max(1, column - visibleWidth(head));
|
|
141
|
+
return [time ? `${head}${" ".repeat(pad)}${time}` : head, ...rest.map((line) => `${bar()}${indent}${line}`)].join("\n");
|
|
142
|
+
};
|
|
143
|
+
const relay = (text) => String(text).split("\n").map((line) => line.trim() === "" ? bar() : `${bar()}${indent}${line}`).join("\n");
|
|
144
|
+
const receipt = (lines) => {
|
|
145
|
+
const body = (Array.isArray(lines) ? lines : String(lines).split("\n")).flatMap((raw) => {
|
|
146
|
+
const line = String(raw);
|
|
147
|
+
if (visibleWidth(line) <= width - 6) return [line];
|
|
148
|
+
const lead = /^\s*/u.exec(line)?.[0] ?? "";
|
|
149
|
+
return wrapWords(line, width - 6 - lead.length).map((part) => `${lead}${part}`);
|
|
150
|
+
});
|
|
151
|
+
const content = Math.min(width - 6, Math.max(...body.map(visibleWidth)));
|
|
152
|
+
const rule = paint(identity.accent, GLYPH.rule.repeat(content + 4));
|
|
153
|
+
const frame = (left, right) => `${paint(identity.accent, left)}${rule}${paint(identity.accent, right)}`;
|
|
154
|
+
const bodyRow = (line) => line.startsWith(GLYPH.check) ? `${paint(PALETTE.green, GLYPH.check)}${line.slice(1)}` : line;
|
|
155
|
+
return [
|
|
156
|
+
frame(GLYPH.boxTop, GLYPH.boxTopEnd),
|
|
157
|
+
...body.map((line) => `${paint(identity.accent, GLYPH.bar)} ${bodyRow(line)}${" ".repeat(Math.max(0, content - visibleWidth(line)))} ${paint(identity.accent, GLYPH.bar)}`),
|
|
158
|
+
frame(GLYPH.boxBottom, GLYPH.boxBottomEnd)
|
|
159
|
+
];
|
|
160
|
+
};
|
|
161
|
+
const signOff = () => `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, `${identity.name} \xB7 Mutatis Mutandis`)}`;
|
|
162
|
+
const refusal = (message) => `${bar()} ${paint(identity.accent, GLYPH.cross)} ${message}`;
|
|
163
|
+
return { identity, width, welcome, step, relay, receipt, signOff, refusal, paint };
|
|
164
|
+
}
|
|
165
|
+
var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
|
|
166
|
+
var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
167
|
+
var SPINNER_FRAMES = Object.freeze([...FRAMES]);
|
|
168
|
+
var ALLOWED = new Set(Object.values(GLYPH));
|
|
169
|
+
|
|
170
|
+
// src/autoupdate.ts
|
|
171
|
+
var import_node_child_process = require("node:child_process");
|
|
38
172
|
var import_node_fs = require("node:fs");
|
|
173
|
+
var import_node_os = require("node:os");
|
|
174
|
+
var import_node_path = require("node:path");
|
|
175
|
+
function schedulePlatform(override) {
|
|
176
|
+
const platform = override ?? process.platform;
|
|
177
|
+
if (platform === "win32" || platform === "darwin" || platform === "linux") return platform;
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
function defaultExec(command, args) {
|
|
181
|
+
const result = (0, import_node_child_process.spawnSync)(command, args, { encoding: "utf8", windowsHide: true });
|
|
182
|
+
if (result.error) return { code: 1, stdout: "", stderr: result.error.message };
|
|
183
|
+
return {
|
|
184
|
+
code: typeof result.status === "number" ? result.status : 1,
|
|
185
|
+
stdout: String(result.stdout ?? ""),
|
|
186
|
+
stderr: String(result.stderr ?? "")
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function homeOf(options) {
|
|
190
|
+
if (options.homeDir) return options.homeDir;
|
|
191
|
+
if (process.platform === "win32") return process.env.USERPROFILE ?? (0, import_node_os.tmpdir)();
|
|
192
|
+
return process.env.HOME ?? (0, import_node_os.tmpdir)();
|
|
193
|
+
}
|
|
194
|
+
function scheduleName(config) {
|
|
195
|
+
return `${config.binName} autoupdate`;
|
|
196
|
+
}
|
|
197
|
+
function scheduleLabel(config) {
|
|
198
|
+
return `${config.binName}-autoupdate`.replace(/[^A-Za-z0-9_-]+/g, "-");
|
|
199
|
+
}
|
|
200
|
+
function quoteWindows(arg) {
|
|
201
|
+
return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
202
|
+
}
|
|
203
|
+
function enableSchedule(config, command, options = {}) {
|
|
204
|
+
const platform = schedulePlatform(options.platform);
|
|
205
|
+
if (!platform) throw new Error(`autoupdate is not supported on ${options.platform ?? process.platform}.`);
|
|
206
|
+
const exec = options.exec ?? defaultExec;
|
|
207
|
+
const home = homeOf(options);
|
|
208
|
+
if (platform === "win32") {
|
|
209
|
+
const taskLine = command.map(quoteWindows).join(" ");
|
|
210
|
+
const result2 = exec("schtasks", ["/Create", "/TN", scheduleName(config), "/TR", taskLine, "/SC", "HOURLY", "/IT", "/F"]);
|
|
211
|
+
if (result2.code !== 0) {
|
|
212
|
+
throw new Error(`could not turn autoupdate on: ${firstLine(result2.stdout || result2.stderr) || `exit ${result2.code}`}`);
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (platform === "darwin") {
|
|
217
|
+
const label2 = scheduleLabel(config);
|
|
218
|
+
const dir2 = (0, import_node_path.join)(home, "Library", "LaunchAgents");
|
|
219
|
+
(0, import_node_fs.mkdirSync)(dir2, { recursive: true });
|
|
220
|
+
const plist = (0, import_node_path.join)(dir2, `${label2}.plist`);
|
|
221
|
+
(0, import_node_fs.writeFileSync)(plist, darwinPlist(label2, command));
|
|
222
|
+
exec("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${label2}`]);
|
|
223
|
+
const result2 = exec("launchctl", ["bootstrap", `gui/${process.getuid?.() ?? 501}`, plist]);
|
|
224
|
+
if (result2.code !== 0) {
|
|
225
|
+
throw new Error(`could not turn autoupdate on: ${firstLine(result2.stderr || result2.stdout) || `exit ${result2.code}`}`);
|
|
226
|
+
}
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const label = scheduleLabel(config);
|
|
230
|
+
const dir = (0, import_node_path.join)(home, ".config", "systemd", "user");
|
|
231
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
232
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(dir, `${label}.service`), linuxService(command));
|
|
233
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(dir, `${label}.timer`), linuxTimer(label));
|
|
234
|
+
const reload = exec("systemctl", ["--user", "daemon-reload"]);
|
|
235
|
+
if (reload.code !== 0) {
|
|
236
|
+
throw new Error(`could not turn autoupdate on: ${firstLine(reload.stderr || reload.stdout) || `exit ${reload.code}`}`);
|
|
237
|
+
}
|
|
238
|
+
const result = exec("systemctl", ["--user", "enable", "--now", `${label}.timer`]);
|
|
239
|
+
if (result.code !== 0) {
|
|
240
|
+
throw new Error(`could not turn autoupdate on: ${firstLine(result.stderr || result.stdout) || `exit ${result.code}`}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function disableSchedule(config, options = {}) {
|
|
244
|
+
const platform = schedulePlatform(options.platform);
|
|
245
|
+
if (!platform) throw new Error(`autoupdate is not supported on ${options.platform ?? process.platform}.`);
|
|
246
|
+
const exec = options.exec ?? defaultExec;
|
|
247
|
+
const home = homeOf(options);
|
|
248
|
+
if (platform === "win32") {
|
|
249
|
+
const result = exec("schtasks", ["/Delete", "/TN", scheduleName(config), "/F"]);
|
|
250
|
+
if (result.code !== 0 && !/cannot find|does not exist/i.test(`${result.stdout} ${result.stderr}`)) {
|
|
251
|
+
throw new Error(`could not turn autoupdate off: ${firstLine(result.stdout || result.stderr) || `exit ${result.code}`}`);
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (platform === "darwin") {
|
|
256
|
+
const label2 = scheduleLabel(config);
|
|
257
|
+
exec("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${label2}`]);
|
|
258
|
+
(0, import_node_fs.rmSync)((0, import_node_path.join)(home, "Library", "LaunchAgents", `${label2}.plist`), { force: true });
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const label = scheduleLabel(config);
|
|
262
|
+
const dir = (0, import_node_path.join)(home, ".config", "systemd", "user");
|
|
263
|
+
exec("systemctl", ["--user", "disable", "--now", `${label}.timer`]);
|
|
264
|
+
(0, import_node_fs.rmSync)((0, import_node_path.join)(dir, `${label}.service`), { force: true });
|
|
265
|
+
(0, import_node_fs.rmSync)((0, import_node_path.join)(dir, `${label}.timer`), { force: true });
|
|
266
|
+
}
|
|
267
|
+
function querySchedule(config, options = {}) {
|
|
268
|
+
const platform = schedulePlatform(options.platform);
|
|
269
|
+
if (!platform) return { supported: false, enabled: false };
|
|
270
|
+
const exec = options.exec ?? defaultExec;
|
|
271
|
+
const home = homeOf(options);
|
|
272
|
+
if (platform === "win32") {
|
|
273
|
+
const result = exec("schtasks", ["/Query", "/TN", scheduleName(config), "/FO", "LIST", "/V"]);
|
|
274
|
+
if (result.code !== 0) return { supported: true, enabled: false };
|
|
275
|
+
const lastRun = valueOf(result.stdout, "Last Run Time:");
|
|
276
|
+
const nextRun = valueOf(result.stdout, "Next Run Time:");
|
|
277
|
+
const state2 = { supported: true, enabled: true, cadence: "hourly" };
|
|
278
|
+
if (lastRun && !/disabled|never|not run/i.test(lastRun)) state2.lastRun = lastRun;
|
|
279
|
+
else if (nextRun && !/disabled|never/i.test(nextRun)) state2.lastRun = `next run ${nextRun}`;
|
|
280
|
+
return state2;
|
|
281
|
+
}
|
|
282
|
+
if (platform === "darwin") {
|
|
283
|
+
const plist = (0, import_node_path.join)(home, "Library", "LaunchAgents", `${scheduleLabel(config)}.plist`);
|
|
284
|
+
if (!(0, import_node_fs.existsSync)(plist)) return { supported: true, enabled: false };
|
|
285
|
+
return { supported: true, enabled: true, cadence: "hourly" };
|
|
286
|
+
}
|
|
287
|
+
const timer = (0, import_node_path.join)(home, ".config", "systemd", "user", `${scheduleLabel(config)}.timer`);
|
|
288
|
+
if (!(0, import_node_fs.existsSync)(timer)) return { supported: true, enabled: false };
|
|
289
|
+
const state = { supported: true, enabled: true, cadence: "hourly" };
|
|
290
|
+
const shown = exec("systemctl", ["--user", "show", `${scheduleLabel(config)}.service`, "-p", "ExecMainStartTimestamp", "--value"]);
|
|
291
|
+
const stamp = (shown.stdout ?? "").trim();
|
|
292
|
+
if (shown.code === 0 && stamp && stamp !== "n/a") state.lastRun = stamp;
|
|
293
|
+
return state;
|
|
294
|
+
}
|
|
295
|
+
function darwinPlist(label, command) {
|
|
296
|
+
const args = command.map((arg) => ` <string>${xmlEscape(arg)}</string>`).join("\n");
|
|
297
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
298
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
299
|
+
<plist version="1.0">
|
|
300
|
+
<dict>
|
|
301
|
+
<key>Label</key>
|
|
302
|
+
<string>${xmlEscape(label)}</string>
|
|
303
|
+
<key>ProgramArguments</key>
|
|
304
|
+
<array>
|
|
305
|
+
${args}
|
|
306
|
+
</array>
|
|
307
|
+
<key>StartInterval</key>
|
|
308
|
+
<integer>3600</integer>
|
|
309
|
+
</dict>
|
|
310
|
+
</plist>
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
function linuxService(command) {
|
|
314
|
+
const line = command.map((arg) => /[\s"\\]/.test(arg) ? `"${arg.replace(/(["\\])/g, "\\$1")}"` : arg).join(" ");
|
|
315
|
+
return `[Unit]
|
|
316
|
+
Description=${"Hourly update check"}
|
|
317
|
+
[Service]
|
|
318
|
+
Type=oneshot
|
|
319
|
+
ExecStart=${line}
|
|
320
|
+
`;
|
|
321
|
+
}
|
|
322
|
+
function linuxTimer(label) {
|
|
323
|
+
return `[Unit]
|
|
324
|
+
Description=Hourly update check for ${label}
|
|
325
|
+
[Timer]
|
|
326
|
+
OnCalendar=hourly
|
|
327
|
+
Persistent=true
|
|
328
|
+
[Install]
|
|
329
|
+
WantedBy=timers.target
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
function xmlEscape(text) {
|
|
333
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
334
|
+
}
|
|
335
|
+
function valueOf(output, key) {
|
|
336
|
+
const line = output.split(/\r?\n/).find((candidate) => candidate.trimStart().startsWith(key));
|
|
337
|
+
if (!line) return null;
|
|
338
|
+
const value = line.slice(line.indexOf(key) + key.length).trim();
|
|
339
|
+
return value ? value : null;
|
|
340
|
+
}
|
|
341
|
+
function firstLine(text) {
|
|
342
|
+
return String(text).split(/\r?\n/).map((line) => line.trim()).filter(Boolean)[0] ?? "";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/config.ts
|
|
346
|
+
var import_node_fs2 = require("node:fs");
|
|
39
347
|
var import_node_sea = require("node:sea");
|
|
40
348
|
|
|
41
349
|
// src/module-url.ts
|
|
@@ -58,10 +366,10 @@ function loadProductConfig(options = {}) {
|
|
|
58
366
|
const explicit = options.configPath ?? process.env.LAUNCHER_CONFIG;
|
|
59
367
|
const devFallback = new URL("../config/product.template.json", moduleUrl());
|
|
60
368
|
if (explicit) {
|
|
61
|
-
return parseProductConfig((0,
|
|
369
|
+
return parseProductConfig((0, import_node_fs2.readFileSync)(explicit, "utf8"));
|
|
62
370
|
}
|
|
63
371
|
try {
|
|
64
|
-
return parseProductConfig((0,
|
|
372
|
+
return parseProductConfig((0, import_node_fs2.readFileSync)(devFallback, "utf8"));
|
|
65
373
|
} catch {
|
|
66
374
|
}
|
|
67
375
|
try {
|
|
@@ -116,14 +424,14 @@ function field(record, key) {
|
|
|
116
424
|
}
|
|
117
425
|
|
|
118
426
|
// src/login-github.ts
|
|
119
|
-
var
|
|
427
|
+
var import_node_child_process2 = require("node:child_process");
|
|
120
428
|
var realSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
121
429
|
function openBrowser(url) {
|
|
122
430
|
if (process.env.LAUNCHER_NO_OPEN === "1") return;
|
|
123
431
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
|
|
124
432
|
const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
125
433
|
try {
|
|
126
|
-
const child = (0,
|
|
434
|
+
const child = (0, import_node_child_process2.spawn)(opener, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
127
435
|
child.on("error", () => {
|
|
128
436
|
});
|
|
129
437
|
child.unref();
|
|
@@ -350,9 +658,9 @@ function page(title, body) {
|
|
|
350
658
|
|
|
351
659
|
// src/payload.ts
|
|
352
660
|
var import_node_crypto2 = require("node:crypto");
|
|
353
|
-
var
|
|
354
|
-
var
|
|
355
|
-
var
|
|
661
|
+
var import_node_fs3 = require("node:fs");
|
|
662
|
+
var import_node_os2 = require("node:os");
|
|
663
|
+
var import_node_path2 = require("node:path");
|
|
356
664
|
|
|
357
665
|
// src/canonical.ts
|
|
358
666
|
function canonicalJson(value) {
|
|
@@ -492,56 +800,56 @@ async function fetchFileBytes(config, accessToken, path, fetchImpl) {
|
|
|
492
800
|
}
|
|
493
801
|
async function downloadAndUnpack(config, dir, manifest, accessToken, options = {}) {
|
|
494
802
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
495
|
-
const staging = (0,
|
|
496
|
-
(0,
|
|
803
|
+
const staging = (0, import_node_path2.join)((0, import_node_os2.tmpdir)(), `launcher-${config.product}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`);
|
|
804
|
+
(0, import_node_fs3.mkdirSync)(staging, { recursive: true });
|
|
497
805
|
try {
|
|
498
806
|
for (const entry of manifest.files) {
|
|
499
807
|
const bytes = await fetchFileBytes(config, accessToken, entry.path, fetchImpl);
|
|
500
808
|
if (!verifyFileBytes(entry, bytes)) {
|
|
501
809
|
throw new Error(`file ${entry.path} failed its checksum \u2014 refusing to install anything.`);
|
|
502
810
|
}
|
|
503
|
-
const dest = (0,
|
|
504
|
-
(0,
|
|
505
|
-
(0,
|
|
811
|
+
const dest = (0, import_node_path2.join)(staging, entry.path);
|
|
812
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(dest), { recursive: true });
|
|
813
|
+
(0, import_node_fs3.writeFileSync)(dest, bytes);
|
|
506
814
|
}
|
|
507
|
-
const target = (0,
|
|
508
|
-
(0,
|
|
509
|
-
(0,
|
|
510
|
-
(0,
|
|
815
|
+
const target = (0, import_node_path2.join)(dir, "payload");
|
|
816
|
+
(0, import_node_fs3.mkdirSync)(dir, { recursive: true });
|
|
817
|
+
(0, import_node_fs3.rmSync)(target, { force: true, recursive: true });
|
|
818
|
+
(0, import_node_fs3.renameSync)(staging, target);
|
|
511
819
|
} catch (error) {
|
|
512
|
-
(0,
|
|
820
|
+
(0, import_node_fs3.rmSync)(staging, { force: true, recursive: true });
|
|
513
821
|
throw error;
|
|
514
822
|
}
|
|
515
823
|
return manifest.version;
|
|
516
824
|
}
|
|
517
825
|
|
|
518
826
|
// src/store.ts
|
|
519
|
-
var
|
|
520
|
-
var
|
|
521
|
-
var
|
|
827
|
+
var import_node_fs4 = require("node:fs");
|
|
828
|
+
var import_node_os3 = require("node:os");
|
|
829
|
+
var import_node_path3 = require("node:path");
|
|
522
830
|
function defaultProductDir(product) {
|
|
523
831
|
if (process.platform === "win32") {
|
|
524
|
-
const base = process.env.LOCALAPPDATA ?? (0,
|
|
525
|
-
return (0,
|
|
832
|
+
const base = process.env.LOCALAPPDATA ?? (0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "launcher-fallback");
|
|
833
|
+
return (0, import_node_path3.join)(base, product);
|
|
526
834
|
}
|
|
527
|
-
const home = process.env.HOME ?? (0,
|
|
528
|
-
return (0,
|
|
835
|
+
const home = process.env.HOME ?? (0, import_node_os3.tmpdir)();
|
|
836
|
+
return (0, import_node_path3.join)(home, `.${product}`);
|
|
529
837
|
}
|
|
530
838
|
function resolveProductDir(product, explicit) {
|
|
531
839
|
return explicit ?? process.env.LAUNCHER_DIR ?? defaultProductDir(product);
|
|
532
840
|
}
|
|
533
841
|
function tokensPath(dir) {
|
|
534
|
-
return (0,
|
|
842
|
+
return (0, import_node_path3.join)(dir, "tokens.json");
|
|
535
843
|
}
|
|
536
844
|
function statePath(dir) {
|
|
537
|
-
return (0,
|
|
845
|
+
return (0, import_node_path3.join)(dir, "state.json");
|
|
538
846
|
}
|
|
539
847
|
function payloadDir(dir) {
|
|
540
|
-
return (0,
|
|
848
|
+
return (0, import_node_path3.join)(dir, "payload");
|
|
541
849
|
}
|
|
542
850
|
function readTokens(dir) {
|
|
543
851
|
try {
|
|
544
|
-
const data = JSON.parse((0,
|
|
852
|
+
const data = JSON.parse((0, import_node_fs4.readFileSync)(tokensPath(dir), "utf8"));
|
|
545
853
|
if (typeof data.accessToken !== "string" || !data.accessToken) return null;
|
|
546
854
|
if (typeof data.expiresAt !== "number" || !Number.isFinite(data.expiresAt)) return null;
|
|
547
855
|
const tokens = { accessToken: data.accessToken, expiresAt: data.expiresAt };
|
|
@@ -553,21 +861,21 @@ function readTokens(dir) {
|
|
|
553
861
|
}
|
|
554
862
|
}
|
|
555
863
|
function writeTokens(dir, tokens) {
|
|
556
|
-
(0,
|
|
864
|
+
(0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
557
865
|
try {
|
|
558
|
-
(0,
|
|
866
|
+
(0, import_node_fs4.writeFileSync)(tokensPath(dir), `${JSON.stringify(tokens)}
|
|
559
867
|
`, { mode: 384 });
|
|
560
868
|
} catch {
|
|
561
|
-
(0,
|
|
869
|
+
(0, import_node_fs4.writeFileSync)(tokensPath(dir), `${JSON.stringify(tokens)}
|
|
562
870
|
`);
|
|
563
871
|
}
|
|
564
872
|
}
|
|
565
873
|
function clearTokens(dir) {
|
|
566
|
-
(0,
|
|
874
|
+
(0, import_node_fs4.rmSync)(tokensPath(dir), { force: true });
|
|
567
875
|
}
|
|
568
876
|
function readState(dir) {
|
|
569
877
|
try {
|
|
570
|
-
const data = JSON.parse((0,
|
|
878
|
+
const data = JSON.parse((0, import_node_fs4.readFileSync)(statePath(dir), "utf8"));
|
|
571
879
|
if (typeof data.version !== "string" || !data.version) return null;
|
|
572
880
|
return { version: data.version, updatedAt: typeof data.updatedAt === "string" ? data.updatedAt : "" };
|
|
573
881
|
} catch {
|
|
@@ -575,18 +883,18 @@ function readState(dir) {
|
|
|
575
883
|
}
|
|
576
884
|
}
|
|
577
885
|
function writeState(dir, state) {
|
|
578
|
-
(0,
|
|
579
|
-
(0,
|
|
886
|
+
(0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
887
|
+
(0, import_node_fs4.writeFileSync)(statePath(dir), `${JSON.stringify(state, null, 2)}
|
|
580
888
|
`);
|
|
581
889
|
}
|
|
582
890
|
function wipeProductDir(dir) {
|
|
583
|
-
(0,
|
|
584
|
-
(0,
|
|
585
|
-
(0,
|
|
891
|
+
(0, import_node_fs4.rmSync)(tokensPath(dir), { force: true });
|
|
892
|
+
(0, import_node_fs4.rmSync)(statePath(dir), { force: true });
|
|
893
|
+
(0, import_node_fs4.rmSync)(payloadDir(dir), { force: true, recursive: true });
|
|
586
894
|
}
|
|
587
895
|
|
|
588
896
|
// src/index.ts
|
|
589
|
-
var LAUNCHER_VERSION = true ? "0.1.
|
|
897
|
+
var LAUNCHER_VERSION = true ? "0.1.4" : readVersionFromPackage();
|
|
590
898
|
function defaultPrint(message) {
|
|
591
899
|
process.stdout.write(`${message}
|
|
592
900
|
`);
|
|
@@ -633,12 +941,11 @@ async function run(rawOptions = {}) {
|
|
|
633
941
|
case "update":
|
|
634
942
|
return await doUpdate(config, dir, rawOptions, print);
|
|
635
943
|
case "doctor":
|
|
636
|
-
doDoctor(config, dir, print);
|
|
637
|
-
|
|
944
|
+
return doDoctor(config, dir, rawOptions, print);
|
|
945
|
+
case "autoupdate":
|
|
946
|
+
return doAutoupdate(config, rawOptions, positional.slice(1), print);
|
|
638
947
|
default:
|
|
639
|
-
|
|
640
|
-
printUsage(config, print);
|
|
641
|
-
return 2;
|
|
948
|
+
return doForward(config, dir, rawOptions, command, argv, print);
|
|
642
949
|
}
|
|
643
950
|
} catch (error) {
|
|
644
951
|
if (error instanceof NeedsLoginError) {
|
|
@@ -657,16 +964,17 @@ function flagValue(argv, flag) {
|
|
|
657
964
|
}
|
|
658
965
|
function printUsage(config, print) {
|
|
659
966
|
print(`${config.binName} launcher ${LAUNCHER_VERSION} \u2014 sign in and install ${config.product}.`);
|
|
660
|
-
print("usage: launcher <login|logout|install|update|doctor> [--config <path>] [--dir <path>]");
|
|
967
|
+
print("usage: launcher <login|logout|install|update|doctor|autoupdate on|off|status> [--config <path>] [--dir <path>]");
|
|
661
968
|
print(" launcher --run <file> [args\u2026] (run a payload file with the embedded runtime)");
|
|
969
|
+
print(" launcher <verb> [args\u2026] (forwarded to the payload when it declares run + verbs)");
|
|
662
970
|
}
|
|
663
971
|
async function runFile(file, args, printErr) {
|
|
664
972
|
if (!file) {
|
|
665
973
|
printErr("launcher --run needs a file to run.");
|
|
666
974
|
return 1;
|
|
667
975
|
}
|
|
668
|
-
const abs = (0,
|
|
669
|
-
if (!(0,
|
|
976
|
+
const abs = (0, import_node_path4.resolve)(process.cwd(), file);
|
|
977
|
+
if (!(0, import_node_fs5.existsSync)(abs)) {
|
|
670
978
|
printErr(`cannot run ${file}: no such file.`);
|
|
671
979
|
return 1;
|
|
672
980
|
}
|
|
@@ -752,10 +1060,10 @@ async function fetchAccessTokenOrLogin(config, dir, options, print) {
|
|
|
752
1060
|
if (!after) throw new Error("sign-in did not produce a token");
|
|
753
1061
|
return after.accessToken;
|
|
754
1062
|
}
|
|
755
|
-
function
|
|
1063
|
+
function readPayloadArgv(dir, key) {
|
|
756
1064
|
try {
|
|
757
|
-
const parsed = JSON.parse((0,
|
|
758
|
-
const entry = parsed
|
|
1065
|
+
const parsed = JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path4.join)(payloadDir(dir), "payload.json"), "utf8"));
|
|
1066
|
+
const entry = parsed[key];
|
|
759
1067
|
if (typeof entry === "string") {
|
|
760
1068
|
const parts = entry.trim().split(/\s+/).filter(Boolean);
|
|
761
1069
|
return parts.length > 0 ? parts : null;
|
|
@@ -768,67 +1076,251 @@ function readPayloadEntry(dir) {
|
|
|
768
1076
|
return null;
|
|
769
1077
|
}
|
|
770
1078
|
}
|
|
1079
|
+
function readPayloadEntry(dir) {
|
|
1080
|
+
return readPayloadArgv(dir, "entry");
|
|
1081
|
+
}
|
|
1082
|
+
function readPayloadRun(dir) {
|
|
1083
|
+
return readPayloadArgv(dir, "run");
|
|
1084
|
+
}
|
|
1085
|
+
function readPayloadVerbs(dir) {
|
|
1086
|
+
try {
|
|
1087
|
+
const parsed = JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path4.join)(payloadDir(dir), "payload.json"), "utf8"));
|
|
1088
|
+
const verbs = parsed.verbs;
|
|
1089
|
+
if (verbs === "*") return "*";
|
|
1090
|
+
if (Array.isArray(verbs) && verbs.every((v) => typeof v === "string" && v.trim().length > 0)) {
|
|
1091
|
+
return verbs;
|
|
1092
|
+
}
|
|
1093
|
+
return null;
|
|
1094
|
+
} catch {
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
771
1098
|
function resolveEntry(entry) {
|
|
772
1099
|
return entry[0] === "$self" ? [process.execPath, ...entry.slice(1)] : entry;
|
|
773
1100
|
}
|
|
774
1101
|
function needsShell(command) {
|
|
775
1102
|
if (process.platform !== "win32") return false;
|
|
776
1103
|
if (/\.(cmd|bat)$/i.test(command)) return true;
|
|
777
|
-
return !(0,
|
|
1104
|
+
return !(0, import_node_fs5.existsSync)(command);
|
|
1105
|
+
}
|
|
1106
|
+
function quoteForShell(arg) {
|
|
1107
|
+
return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
778
1108
|
}
|
|
779
|
-
function defaultRunEntry(entry, cwd) {
|
|
1109
|
+
function defaultRunEntry(entry, cwd, env) {
|
|
780
1110
|
const [command, ...args] = entry;
|
|
781
|
-
const
|
|
1111
|
+
const shell = needsShell(command);
|
|
1112
|
+
const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
|
|
1113
|
+
const result = (0, import_node_child_process3.spawnSync)(commandLine, shell ? [] : args, {
|
|
782
1114
|
cwd,
|
|
783
1115
|
stdio: "inherit",
|
|
784
|
-
shell
|
|
1116
|
+
shell,
|
|
1117
|
+
env: env ?? process.env,
|
|
785
1118
|
windowsHide: true
|
|
786
1119
|
});
|
|
787
1120
|
if (result.error) return { ok: false, error: result.error.message };
|
|
788
|
-
|
|
1121
|
+
const code = typeof result.status === "number" ? result.status : void 0;
|
|
1122
|
+
return { ok: result.status === 0, error: result.status === 0 ? void 0 : `exit code ${result.status}`, code };
|
|
1123
|
+
}
|
|
1124
|
+
function faceProduct(config) {
|
|
1125
|
+
const known = { mmi: "mmi-hub", jerv: "jerv-hub" };
|
|
1126
|
+
return known[config.product] ?? config.product;
|
|
1127
|
+
}
|
|
1128
|
+
function faceFor(config, options) {
|
|
1129
|
+
const tty = options.tty ?? Boolean(process.stdout.isTTY);
|
|
1130
|
+
if (!tty) return null;
|
|
1131
|
+
try {
|
|
1132
|
+
return createFace({
|
|
1133
|
+
product: faceProduct(config),
|
|
1134
|
+
color: !process.env.NO_COLOR && process.env.TERM !== "dumb",
|
|
1135
|
+
columns: process.stdout.columns
|
|
1136
|
+
});
|
|
1137
|
+
} catch {
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
function since(start) {
|
|
1142
|
+
return (Date.now() - start) / 1e3;
|
|
1143
|
+
}
|
|
1144
|
+
function printReceipt(face, config, print, ready, lines) {
|
|
1145
|
+
const name = face ? face.identity.name : config.product;
|
|
1146
|
+
const headline = ready ? `${name} is ready.` : `${name} is installed, but not ready yet.`;
|
|
1147
|
+
if (!face) {
|
|
1148
|
+
print(headline);
|
|
1149
|
+
for (const line of lines) print(line.trim());
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const glyph = ready ? "\u2714" : "\u2716";
|
|
1153
|
+
for (const line of face.receipt([`${glyph} ${headline}`, ...lines])) print(line);
|
|
1154
|
+
print(face.signOff());
|
|
1155
|
+
}
|
|
1156
|
+
function printStep(face, print, title, seconds, kind = "ok") {
|
|
1157
|
+
print(face ? face.step(title, seconds, kind) : title);
|
|
789
1158
|
}
|
|
790
1159
|
async function doInstall(config, dir, options, print) {
|
|
791
1160
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1161
|
+
const face = faceFor(config, options);
|
|
1162
|
+
const accessToken = await fetchAccessTokenOrLogin(config, dir, options, print);
|
|
1163
|
+
const started = Date.now();
|
|
1164
|
+
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
1165
|
+
await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
|
|
1166
|
+
writeState(dir, { version: manifest.version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1167
|
+
printStep(face, print, `installed ${config.product} ${manifest.version}`, since(started));
|
|
1168
|
+
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1169
|
+
}
|
|
1170
|
+
async function doUpdate(config, dir, options, print) {
|
|
1171
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1172
|
+
const face = faceFor(config, options);
|
|
1173
|
+
if (face) for (const line of face.welcome()) print(line);
|
|
792
1174
|
const accessToken = await fetchAccessTokenOrLogin(config, dir, options, print);
|
|
1175
|
+
const started = Date.now();
|
|
793
1176
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
1177
|
+
const current = readState(dir);
|
|
1178
|
+
if (current && current.version === manifest.version && (0, import_node_fs5.existsSync)(payloadDir(dir))) {
|
|
1179
|
+
printStep(face, print, `${config.product} is already up to date at ${manifest.version}`, since(started));
|
|
1180
|
+
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1181
|
+
}
|
|
794
1182
|
await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
|
|
795
1183
|
writeState(dir, { version: manifest.version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
796
|
-
print
|
|
1184
|
+
printStep(face, print, `updated ${config.product} to ${manifest.version}`, since(started));
|
|
1185
|
+
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1186
|
+
}
|
|
1187
|
+
function payloadEnv(dir) {
|
|
1188
|
+
const stored = readTokens(dir);
|
|
1189
|
+
if (!stored?.accessToken) return void 0;
|
|
1190
|
+
return { ...process.env, MM_INSTALLER_TOKEN: stored.accessToken };
|
|
1191
|
+
}
|
|
1192
|
+
var NEVER_A_PROGRAM = /\.(tgz|tar|gz|bz2|xz|zip|json|txt|md|lock)$/i;
|
|
1193
|
+
function payloadFileAsCommand(entry, payload) {
|
|
1194
|
+
const first = entry[0];
|
|
1195
|
+
if (!first || first === "$self") return null;
|
|
1196
|
+
if (first.includes("/") || first.includes("\\")) return null;
|
|
1197
|
+
const candidate = (0, import_node_path4.join)(payload, first);
|
|
1198
|
+
if (!(0, import_node_fs5.existsSync)(candidate)) return null;
|
|
1199
|
+
if (NEVER_A_PROGRAM.test(first)) return first;
|
|
1200
|
+
if (process.platform === "win32") return null;
|
|
1201
|
+
try {
|
|
1202
|
+
return ((0, import_node_fs5.statSync)(candidate).mode & 73) === 0 ? first : null;
|
|
1203
|
+
} catch {
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
function finishLastMile(config, dir, options, print, face, version) {
|
|
797
1208
|
const entry = readPayloadEntry(dir);
|
|
1209
|
+
const payload = payloadDir(dir);
|
|
798
1210
|
if (!entry) {
|
|
799
|
-
|
|
1211
|
+
printReceipt(face, config, print, true, [
|
|
1212
|
+
` Installed ${version} into ${dir}`,
|
|
1213
|
+
` next step: run ${(0, import_node_path4.join)(payload, config.binName)} to start ${config.product}.`
|
|
1214
|
+
]);
|
|
800
1215
|
return 0;
|
|
801
1216
|
}
|
|
802
|
-
const payload = payloadDir(dir);
|
|
803
1217
|
const command = resolveEntry(entry);
|
|
804
|
-
const
|
|
805
|
-
|
|
1218
|
+
const dataFile = payloadFileAsCommand(entry, payload);
|
|
1219
|
+
if (dataFile) {
|
|
1220
|
+
printStep(face, print, `The payload names ${dataFile} as its command, but that is a file, not a program`, null, "fail");
|
|
1221
|
+
printReceipt(face, config, print, false, [
|
|
1222
|
+
` Downloaded ${version} into ${dir}`,
|
|
1223
|
+
` This payload was built wrong: its entry must be a command, not one of its own files.`,
|
|
1224
|
+
` Nothing on this machine can finish it \u2014 report it to the ${config.product} maintainers.`
|
|
1225
|
+
]);
|
|
1226
|
+
return 2;
|
|
1227
|
+
}
|
|
1228
|
+
const started = Date.now();
|
|
1229
|
+
const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
|
|
806
1230
|
if (result.ok) {
|
|
807
|
-
|
|
1231
|
+
printStep(face, print, "Armed this machine", since(started));
|
|
1232
|
+
printReceipt(face, config, print, true, [
|
|
1233
|
+
` Installed ${version} into ${dir}`,
|
|
1234
|
+
` Check health any time: ${config.binName} doctor`
|
|
1235
|
+
]);
|
|
808
1236
|
return 0;
|
|
809
1237
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1238
|
+
const code = result.code ?? 1;
|
|
1239
|
+
printStep(
|
|
1240
|
+
face,
|
|
1241
|
+
print,
|
|
1242
|
+
result.code === void 0 ? `Arming this machine could not start: ${result.error ?? `could not run ${command[0]}`}` : `Arming this machine did not finish (exit code ${code})`,
|
|
1243
|
+
null,
|
|
1244
|
+
"fail"
|
|
1245
|
+
);
|
|
1246
|
+
printReceipt(face, config, print, false, [
|
|
1247
|
+
` Downloaded ${version} into ${dir}`,
|
|
1248
|
+
` Finish it with: (cd ${payload} && ${command.join(" ")})`
|
|
1249
|
+
]);
|
|
1250
|
+
return code;
|
|
813
1251
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1252
|
+
function doForward(config, dir, options, command, argv, print) {
|
|
1253
|
+
if (command && (0, import_node_fs5.existsSync)(command)) {
|
|
1254
|
+
print(`${command} is a file, not a command \u2014 did you mean \`--run ${command}\`?`);
|
|
1255
|
+
return 2;
|
|
1256
|
+
}
|
|
1257
|
+
const target = readPayloadRun(dir);
|
|
1258
|
+
const verbs = readPayloadVerbs(dir);
|
|
1259
|
+
const declared = verbs === "*" || Array.isArray(verbs) && command !== void 0 && verbs.includes(command);
|
|
1260
|
+
if (!target || !declared) {
|
|
1261
|
+
print(`unknown command: ${command}`);
|
|
1262
|
+
printUsage(config, print);
|
|
1263
|
+
return 2;
|
|
1264
|
+
}
|
|
1265
|
+
const forwarded = [...resolveEntry(target), ...argv];
|
|
1266
|
+
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir), payloadEnv(dir));
|
|
1267
|
+
if (!result.ok && result.code === void 0) {
|
|
1268
|
+
print(`failed: ${result.error ?? `could not run ${forwarded[0]}`}`);
|
|
1269
|
+
return 1;
|
|
1270
|
+
}
|
|
1271
|
+
return result.code ?? 0;
|
|
1272
|
+
}
|
|
1273
|
+
function doAutoupdate(config, options, args, print) {
|
|
1274
|
+
const mode = args[0] ?? "status";
|
|
1275
|
+
const scheduleOptions = options.autoupdate ?? {};
|
|
1276
|
+
const command = options.autoupdate?.command ?? [process.execPath, "update"];
|
|
1277
|
+
if (mode === "on") {
|
|
1278
|
+
try {
|
|
1279
|
+
enableSchedule(config, command, scheduleOptions);
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
print(error.message);
|
|
1282
|
+
return 1;
|
|
1283
|
+
}
|
|
1284
|
+
const state2 = querySchedule(config, scheduleOptions);
|
|
1285
|
+
print(`auto-update: on (${state2.cadence ?? "hourly"})`);
|
|
821
1286
|
return 0;
|
|
822
1287
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1288
|
+
if (mode === "off") {
|
|
1289
|
+
try {
|
|
1290
|
+
disableSchedule(config, scheduleOptions);
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
print(error.message);
|
|
1293
|
+
return 1;
|
|
1294
|
+
}
|
|
1295
|
+
print("auto-update: off");
|
|
1296
|
+
return 0;
|
|
1297
|
+
}
|
|
1298
|
+
if (mode !== "status") {
|
|
1299
|
+
print(`unknown autoupdate mode: ${mode} \u2014 use on, off or status.`);
|
|
1300
|
+
return 1;
|
|
1301
|
+
}
|
|
1302
|
+
const state = querySchedule(config, scheduleOptions);
|
|
1303
|
+
if (!state.supported) {
|
|
1304
|
+
print("auto-update: unsupported on this platform");
|
|
1305
|
+
return 1;
|
|
1306
|
+
}
|
|
1307
|
+
print(`auto-update: ${state.enabled ? `on (${state.cadence ?? "hourly"})` : "off"}`);
|
|
1308
|
+
if (state.lastRun) print(`last run: ${state.lastRun}`);
|
|
826
1309
|
return 0;
|
|
827
1310
|
}
|
|
828
|
-
function doDoctor(config, dir, print) {
|
|
1311
|
+
function doDoctor(config, dir, options, print) {
|
|
1312
|
+
doLauncherDoctor(config, dir, options, print);
|
|
1313
|
+
const target = readPayloadRun(dir);
|
|
1314
|
+
const verbs = readPayloadVerbs(dir);
|
|
1315
|
+
const chains = target !== null && (verbs === "*" || Array.isArray(verbs) && verbs.includes("doctor"));
|
|
1316
|
+
if (!chains) return 0;
|
|
1317
|
+
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir), payloadEnv(dir));
|
|
1318
|
+
return result.code ?? (result.ok ? 0 : 1);
|
|
1319
|
+
}
|
|
1320
|
+
function doLauncherDoctor(config, dir, options, print) {
|
|
829
1321
|
const tokens = readTokens(dir);
|
|
830
1322
|
const state = readState(dir);
|
|
831
|
-
const payloadPresent = (0,
|
|
1323
|
+
const payloadPresent = (0, import_node_fs5.existsSync)(payloadDir(dir));
|
|
832
1324
|
print(`product: ${config.product}`);
|
|
833
1325
|
print(`host: ${config.host}`);
|
|
834
1326
|
print(`login: ${config.loginKind}`);
|
|
@@ -840,7 +1332,11 @@ function doDoctor(config, dir, print) {
|
|
|
840
1332
|
print("token: none \u2014 run login first.");
|
|
841
1333
|
}
|
|
842
1334
|
print(`payload: ${state ? state.version : "none"}${payloadPresent ? "" : " (not downloaded)"}`);
|
|
843
|
-
print(`paths: tokens ${(0,
|
|
1335
|
+
print(`paths: tokens ${(0, import_node_path4.join)(dir, "tokens.json")}, state ${(0, import_node_path4.join)(dir, "state.json")}, payload ${payloadDir(dir)}`);
|
|
1336
|
+
const schedule = querySchedule(config, options.autoupdate ?? {});
|
|
1337
|
+
print(
|
|
1338
|
+
`auto-update: ${!schedule.supported ? "unsupported on this platform" : schedule.enabled ? `on (${schedule.cadence ?? "hourly"})` : "off"}`
|
|
1339
|
+
);
|
|
844
1340
|
}
|
|
845
1341
|
var invokedAsMain = typeof process.argv[1] === "string" && (() => {
|
|
846
1342
|
try {
|
|
@@ -871,6 +1367,8 @@ if (invokedAsMain) {
|
|
|
871
1367
|
defaultRunEntry,
|
|
872
1368
|
ensureFreshToken,
|
|
873
1369
|
readPayloadEntry,
|
|
1370
|
+
readPayloadRun,
|
|
1371
|
+
readPayloadVerbs,
|
|
874
1372
|
resolveEntry,
|
|
875
1373
|
run,
|
|
876
1374
|
runFile
|