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