@mutmutco/installer-launcher 0.1.3 → 0.1.7
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 +109 -20
- package/dist/launcher.sea.cjs +108 -19
- package/package.json +2 -2
package/dist/launcher.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
5
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
5
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
6
6
|
import { dirname as dirname2, join as join4, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
8
8
|
|
|
@@ -121,9 +121,10 @@ function createFace({ product, color = false, columns }) {
|
|
|
121
121
|
const content = Math.min(width - 6, Math.max(...body.map(visibleWidth)));
|
|
122
122
|
const rule = paint(identity.accent, GLYPH.rule.repeat(content + 4));
|
|
123
123
|
const frame = (left, right) => `${paint(identity.accent, left)}${rule}${paint(identity.accent, right)}`;
|
|
124
|
+
const bodyRow = (line) => line.startsWith(GLYPH.check) ? `${paint(PALETTE.green, GLYPH.check)}${line.slice(1)}` : line;
|
|
124
125
|
return [
|
|
125
126
|
frame(GLYPH.boxTop, GLYPH.boxTopEnd),
|
|
126
|
-
...body.map((line) => `${paint(identity.accent, GLYPH.bar)} ${line}${" ".repeat(Math.max(0, content - visibleWidth(line)))} ${paint(identity.accent, GLYPH.bar)}`),
|
|
127
|
+
...body.map((line) => `${paint(identity.accent, GLYPH.bar)} ${bodyRow(line)}${" ".repeat(Math.max(0, content - visibleWidth(line)))} ${paint(identity.accent, GLYPH.bar)}`),
|
|
127
128
|
frame(GLYPH.boxBottom, GLYPH.boxBottomEnd)
|
|
128
129
|
];
|
|
129
130
|
};
|
|
@@ -132,6 +133,8 @@ function createFace({ product, color = false, columns }) {
|
|
|
132
133
|
return { identity, width, welcome, step, relay, receipt, signOff, refusal, paint };
|
|
133
134
|
}
|
|
134
135
|
var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
|
|
136
|
+
var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
137
|
+
var SPINNER_FRAMES = Object.freeze([...FRAMES]);
|
|
135
138
|
var ALLOWED = new Set(Object.values(GLYPH));
|
|
136
139
|
|
|
137
140
|
// src/autoupdate.ts
|
|
@@ -860,7 +863,7 @@ function wipeProductDir(dir) {
|
|
|
860
863
|
}
|
|
861
864
|
|
|
862
865
|
// src/index.ts
|
|
863
|
-
var LAUNCHER_VERSION = true ? "0.1.
|
|
866
|
+
var LAUNCHER_VERSION = true ? "0.1.7" : readVersionFromPackage();
|
|
864
867
|
function defaultPrint(message) {
|
|
865
868
|
process.stdout.write(`${message}
|
|
866
869
|
`);
|
|
@@ -1072,19 +1075,58 @@ function needsShell(command) {
|
|
|
1072
1075
|
function quoteForShell(arg) {
|
|
1073
1076
|
return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
1074
1077
|
}
|
|
1075
|
-
|
|
1078
|
+
var SUPPORTED_PROGRESS_PROTOCOLS = /* @__PURE__ */ new Set([1]);
|
|
1079
|
+
function parseProgress(raw) {
|
|
1080
|
+
if (raw === null || raw === void 0) return [];
|
|
1081
|
+
const progress = [];
|
|
1082
|
+
for (const line of raw.toString().split(/\r?\n/)) {
|
|
1083
|
+
if (!line) continue;
|
|
1084
|
+
try {
|
|
1085
|
+
const message = JSON.parse(line);
|
|
1086
|
+
if (!SUPPORTED_PROGRESS_PROTOCOLS.has(message.v) || typeof message.step !== "string") continue;
|
|
1087
|
+
const state = message.state ?? "ok";
|
|
1088
|
+
if (state !== "ok" && state !== "fail" && state !== "note") continue;
|
|
1089
|
+
if (message.ms !== void 0 && typeof message.ms !== "number") continue;
|
|
1090
|
+
progress.push({
|
|
1091
|
+
step: message.step,
|
|
1092
|
+
state,
|
|
1093
|
+
...typeof message.ms === "number" ? { ms: message.ms } : {}
|
|
1094
|
+
});
|
|
1095
|
+
} catch {
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
return progress;
|
|
1099
|
+
}
|
|
1100
|
+
function defaultRunEntry(entry, cwd, env) {
|
|
1076
1101
|
const [command, ...args] = entry;
|
|
1077
1102
|
const shell = needsShell(command);
|
|
1078
1103
|
const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
|
|
1079
|
-
const
|
|
1104
|
+
const spawn2 = (progress2) => spawnSync2(commandLine, shell ? [] : args, {
|
|
1080
1105
|
cwd,
|
|
1081
|
-
stdio: "inherit",
|
|
1106
|
+
stdio: progress2 ? ["inherit", "inherit", "inherit", "pipe"] : "inherit",
|
|
1082
1107
|
shell,
|
|
1108
|
+
env: progress2 ? { ...env ?? process.env, MM_PROGRESS_FD: "3", MM_PROGRESS_PROTOCOL: "1" } : env ?? process.env,
|
|
1083
1109
|
windowsHide: true
|
|
1084
1110
|
});
|
|
1111
|
+
let result;
|
|
1112
|
+
if (process.platform === "win32" && shell) {
|
|
1113
|
+
result = spawn2(false);
|
|
1114
|
+
} else {
|
|
1115
|
+
try {
|
|
1116
|
+
result = spawn2(true);
|
|
1117
|
+
} catch {
|
|
1118
|
+
result = spawn2(false);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1085
1121
|
if (result.error) return { ok: false, error: result.error.message };
|
|
1086
1122
|
const code = typeof result.status === "number" ? result.status : void 0;
|
|
1087
|
-
|
|
1123
|
+
const progress = parseProgress(result.output?.[3]);
|
|
1124
|
+
return {
|
|
1125
|
+
ok: result.status === 0,
|
|
1126
|
+
error: result.status === 0 ? void 0 : `exit code ${result.status}`,
|
|
1127
|
+
code,
|
|
1128
|
+
...progress.length > 0 ? { progress } : {}
|
|
1129
|
+
};
|
|
1088
1130
|
}
|
|
1089
1131
|
function faceProduct(config) {
|
|
1090
1132
|
const known = { mmi: "mmi-hub", jerv: "jerv-hub" };
|
|
@@ -1092,10 +1134,11 @@ function faceProduct(config) {
|
|
|
1092
1134
|
}
|
|
1093
1135
|
function faceFor(config, options) {
|
|
1094
1136
|
const tty = options.tty ?? Boolean(process.stdout.isTTY);
|
|
1137
|
+
if (!tty) return null;
|
|
1095
1138
|
try {
|
|
1096
1139
|
return createFace({
|
|
1097
1140
|
product: faceProduct(config),
|
|
1098
|
-
color:
|
|
1141
|
+
color: !process.env.NO_COLOR && process.env.TERM !== "dumb",
|
|
1099
1142
|
columns: process.stdout.columns
|
|
1100
1143
|
});
|
|
1101
1144
|
} catch {
|
|
@@ -1105,9 +1148,13 @@ function faceFor(config, options) {
|
|
|
1105
1148
|
function since(start) {
|
|
1106
1149
|
return (Date.now() - start) / 1e3;
|
|
1107
1150
|
}
|
|
1151
|
+
function outerConsoleOwnsOutcome() {
|
|
1152
|
+
return process.env.MM_OUTER_CONSOLE === "1";
|
|
1153
|
+
}
|
|
1108
1154
|
function printReceipt(face, config, print, ready, lines) {
|
|
1109
1155
|
const name = face ? face.identity.name : config.product;
|
|
1110
1156
|
const headline = ready ? `${name} is ready.` : `${name} is installed, but not ready yet.`;
|
|
1157
|
+
if (ready && outerConsoleOwnsOutcome()) return;
|
|
1111
1158
|
if (!face) {
|
|
1112
1159
|
print(headline);
|
|
1113
1160
|
for (const line of lines) print(line.trim());
|
|
@@ -1120,6 +1167,11 @@ function printReceipt(face, config, print, ready, lines) {
|
|
|
1120
1167
|
function printStep(face, print, title, seconds, kind = "ok") {
|
|
1121
1168
|
print(face ? face.step(title, seconds, kind) : title);
|
|
1122
1169
|
}
|
|
1170
|
+
function printProgress(face, print, progress) {
|
|
1171
|
+
for (const message of progress ?? []) {
|
|
1172
|
+
printStep(face, print, message.step, message.ms === void 0 ? null : message.ms / 1e3, message.state);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1123
1175
|
async function doInstall(config, dir, options, print) {
|
|
1124
1176
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1125
1177
|
const face = faceFor(config, options);
|
|
@@ -1128,13 +1180,13 @@ async function doInstall(config, dir, options, print) {
|
|
|
1128
1180
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
1129
1181
|
await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
|
|
1130
1182
|
writeState(dir, { version: manifest.version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1131
|
-
printStep(face, print, `installed ${config.product} ${manifest.version}
|
|
1183
|
+
printStep(face, print, `installed ${config.product} ${manifest.version}`, since(started));
|
|
1132
1184
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1133
1185
|
}
|
|
1134
1186
|
async function doUpdate(config, dir, options, print) {
|
|
1135
1187
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1136
1188
|
const face = faceFor(config, options);
|
|
1137
|
-
if (face) for (const line of face.welcome()) print(line);
|
|
1189
|
+
if (face && !outerConsoleOwnsOutcome()) for (const line of face.welcome()) print(line);
|
|
1138
1190
|
const accessToken = await fetchAccessTokenOrLogin(config, dir, options, print);
|
|
1139
1191
|
const started = Date.now();
|
|
1140
1192
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
@@ -1148,32 +1200,69 @@ async function doUpdate(config, dir, options, print) {
|
|
|
1148
1200
|
printStep(face, print, `updated ${config.product} to ${manifest.version}`, since(started));
|
|
1149
1201
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1150
1202
|
}
|
|
1203
|
+
function payloadEnv(dir) {
|
|
1204
|
+
const stored = readTokens(dir);
|
|
1205
|
+
if (!stored?.accessToken) return void 0;
|
|
1206
|
+
return { ...process.env, MM_INSTALLER_TOKEN: stored.accessToken };
|
|
1207
|
+
}
|
|
1208
|
+
var NEVER_A_PROGRAM = /\.(tgz|tar|gz|bz2|xz|zip|json|txt|md|lock)$/i;
|
|
1209
|
+
function payloadFileAsCommand(entry, payload) {
|
|
1210
|
+
const first = entry[0];
|
|
1211
|
+
if (!first || first === "$self") return null;
|
|
1212
|
+
if (first.includes("/") || first.includes("\\")) return null;
|
|
1213
|
+
const candidate = join4(payload, first);
|
|
1214
|
+
if (!existsSync4(candidate)) return null;
|
|
1215
|
+
if (NEVER_A_PROGRAM.test(first)) return first;
|
|
1216
|
+
if (process.platform === "win32") return null;
|
|
1217
|
+
try {
|
|
1218
|
+
return (statSync(candidate).mode & 73) === 0 ? first : null;
|
|
1219
|
+
} catch {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1151
1223
|
function finishLastMile(config, dir, options, print, face, version) {
|
|
1152
1224
|
const entry = readPayloadEntry(dir);
|
|
1153
1225
|
const payload = payloadDir(dir);
|
|
1154
1226
|
if (!entry) {
|
|
1155
1227
|
printReceipt(face, config, print, true, [
|
|
1156
|
-
`
|
|
1157
|
-
`
|
|
1228
|
+
`Installed ${version} into ${dir}`,
|
|
1229
|
+
`next step: run ${join4(payload, config.binName)} to start ${config.product}.`
|
|
1158
1230
|
]);
|
|
1159
1231
|
return 0;
|
|
1160
1232
|
}
|
|
1161
1233
|
const command = resolveEntry(entry);
|
|
1234
|
+
const dataFile = payloadFileAsCommand(entry, payload);
|
|
1235
|
+
if (dataFile) {
|
|
1236
|
+
printStep(face, print, `The payload names ${dataFile} as its command, but that is a file, not a program`, null, "fail");
|
|
1237
|
+
printReceipt(face, config, print, false, [
|
|
1238
|
+
`Downloaded ${version} into ${dir}`,
|
|
1239
|
+
`This payload was built wrong: its entry must be a command, not one of its own files.`,
|
|
1240
|
+
`Nothing on this machine can finish it \u2014 report it to the ${config.product} maintainers.`
|
|
1241
|
+
]);
|
|
1242
|
+
return 2;
|
|
1243
|
+
}
|
|
1162
1244
|
const started = Date.now();
|
|
1163
|
-
const result = (options.runEntry ?? defaultRunEntry)(command, payload);
|
|
1245
|
+
const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
|
|
1246
|
+
printProgress(face, print, result.progress);
|
|
1164
1247
|
if (result.ok) {
|
|
1165
1248
|
printStep(face, print, "Armed this machine", since(started));
|
|
1166
1249
|
printReceipt(face, config, print, true, [
|
|
1167
|
-
`
|
|
1168
|
-
`
|
|
1250
|
+
`Installed ${version} into ${dir}`,
|
|
1251
|
+
`Check health any time: ${config.binName} doctor`
|
|
1169
1252
|
]);
|
|
1170
1253
|
return 0;
|
|
1171
1254
|
}
|
|
1172
1255
|
const code = result.code ?? 1;
|
|
1173
|
-
printStep(
|
|
1256
|
+
printStep(
|
|
1257
|
+
face,
|
|
1258
|
+
print,
|
|
1259
|
+
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})`,
|
|
1260
|
+
null,
|
|
1261
|
+
"fail"
|
|
1262
|
+
);
|
|
1174
1263
|
printReceipt(face, config, print, false, [
|
|
1175
|
-
`
|
|
1176
|
-
`
|
|
1264
|
+
`Downloaded ${version} into ${dir}`,
|
|
1265
|
+
`Finish it with: (cd ${payload} && ${command.join(" ")})`
|
|
1177
1266
|
]);
|
|
1178
1267
|
return code;
|
|
1179
1268
|
}
|
|
@@ -1191,7 +1280,7 @@ function doForward(config, dir, options, command, argv, print) {
|
|
|
1191
1280
|
return 2;
|
|
1192
1281
|
}
|
|
1193
1282
|
const forwarded = [...resolveEntry(target), ...argv];
|
|
1194
|
-
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir));
|
|
1283
|
+
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir), payloadEnv(dir));
|
|
1195
1284
|
if (!result.ok && result.code === void 0) {
|
|
1196
1285
|
print(`failed: ${result.error ?? `could not run ${forwarded[0]}`}`);
|
|
1197
1286
|
return 1;
|
|
@@ -1242,7 +1331,7 @@ function doDoctor(config, dir, options, print) {
|
|
|
1242
1331
|
const verbs = readPayloadVerbs(dir);
|
|
1243
1332
|
const chains = target !== null && (verbs === "*" || Array.isArray(verbs) && verbs.includes("doctor"));
|
|
1244
1333
|
if (!chains) return 0;
|
|
1245
|
-
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir));
|
|
1334
|
+
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir), payloadEnv(dir));
|
|
1246
1335
|
return result.code ?? (result.ok ? 0 : 1);
|
|
1247
1336
|
}
|
|
1248
1337
|
function doLauncherDoctor(config, dir, options, print) {
|
package/dist/launcher.sea.cjs
CHANGED
|
@@ -151,9 +151,10 @@ function createFace({ product, color = false, columns }) {
|
|
|
151
151
|
const content = Math.min(width - 6, Math.max(...body.map(visibleWidth)));
|
|
152
152
|
const rule = paint(identity.accent, GLYPH.rule.repeat(content + 4));
|
|
153
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;
|
|
154
155
|
return [
|
|
155
156
|
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
|
+
...body.map((line) => `${paint(identity.accent, GLYPH.bar)} ${bodyRow(line)}${" ".repeat(Math.max(0, content - visibleWidth(line)))} ${paint(identity.accent, GLYPH.bar)}`),
|
|
157
158
|
frame(GLYPH.boxBottom, GLYPH.boxBottomEnd)
|
|
158
159
|
];
|
|
159
160
|
};
|
|
@@ -162,6 +163,8 @@ function createFace({ product, color = false, columns }) {
|
|
|
162
163
|
return { identity, width, welcome, step, relay, receipt, signOff, refusal, paint };
|
|
163
164
|
}
|
|
164
165
|
var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
|
|
166
|
+
var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
167
|
+
var SPINNER_FRAMES = Object.freeze([...FRAMES]);
|
|
165
168
|
var ALLOWED = new Set(Object.values(GLYPH));
|
|
166
169
|
|
|
167
170
|
// src/autoupdate.ts
|
|
@@ -891,7 +894,7 @@ function wipeProductDir(dir) {
|
|
|
891
894
|
}
|
|
892
895
|
|
|
893
896
|
// src/index.ts
|
|
894
|
-
var LAUNCHER_VERSION = true ? "0.1.
|
|
897
|
+
var LAUNCHER_VERSION = true ? "0.1.7" : readVersionFromPackage();
|
|
895
898
|
function defaultPrint(message) {
|
|
896
899
|
process.stdout.write(`${message}
|
|
897
900
|
`);
|
|
@@ -1103,19 +1106,58 @@ function needsShell(command) {
|
|
|
1103
1106
|
function quoteForShell(arg) {
|
|
1104
1107
|
return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
1105
1108
|
}
|
|
1106
|
-
|
|
1109
|
+
var SUPPORTED_PROGRESS_PROTOCOLS = /* @__PURE__ */ new Set([1]);
|
|
1110
|
+
function parseProgress(raw) {
|
|
1111
|
+
if (raw === null || raw === void 0) return [];
|
|
1112
|
+
const progress = [];
|
|
1113
|
+
for (const line of raw.toString().split(/\r?\n/)) {
|
|
1114
|
+
if (!line) continue;
|
|
1115
|
+
try {
|
|
1116
|
+
const message = JSON.parse(line);
|
|
1117
|
+
if (!SUPPORTED_PROGRESS_PROTOCOLS.has(message.v) || typeof message.step !== "string") continue;
|
|
1118
|
+
const state = message.state ?? "ok";
|
|
1119
|
+
if (state !== "ok" && state !== "fail" && state !== "note") continue;
|
|
1120
|
+
if (message.ms !== void 0 && typeof message.ms !== "number") continue;
|
|
1121
|
+
progress.push({
|
|
1122
|
+
step: message.step,
|
|
1123
|
+
state,
|
|
1124
|
+
...typeof message.ms === "number" ? { ms: message.ms } : {}
|
|
1125
|
+
});
|
|
1126
|
+
} catch {
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return progress;
|
|
1130
|
+
}
|
|
1131
|
+
function defaultRunEntry(entry, cwd, env) {
|
|
1107
1132
|
const [command, ...args] = entry;
|
|
1108
1133
|
const shell = needsShell(command);
|
|
1109
1134
|
const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
|
|
1110
|
-
const
|
|
1135
|
+
const spawn2 = (progress2) => (0, import_node_child_process3.spawnSync)(commandLine, shell ? [] : args, {
|
|
1111
1136
|
cwd,
|
|
1112
|
-
stdio: "inherit",
|
|
1137
|
+
stdio: progress2 ? ["inherit", "inherit", "inherit", "pipe"] : "inherit",
|
|
1113
1138
|
shell,
|
|
1139
|
+
env: progress2 ? { ...env ?? process.env, MM_PROGRESS_FD: "3", MM_PROGRESS_PROTOCOL: "1" } : env ?? process.env,
|
|
1114
1140
|
windowsHide: true
|
|
1115
1141
|
});
|
|
1142
|
+
let result;
|
|
1143
|
+
if (process.platform === "win32" && shell) {
|
|
1144
|
+
result = spawn2(false);
|
|
1145
|
+
} else {
|
|
1146
|
+
try {
|
|
1147
|
+
result = spawn2(true);
|
|
1148
|
+
} catch {
|
|
1149
|
+
result = spawn2(false);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1116
1152
|
if (result.error) return { ok: false, error: result.error.message };
|
|
1117
1153
|
const code = typeof result.status === "number" ? result.status : void 0;
|
|
1118
|
-
|
|
1154
|
+
const progress = parseProgress(result.output?.[3]);
|
|
1155
|
+
return {
|
|
1156
|
+
ok: result.status === 0,
|
|
1157
|
+
error: result.status === 0 ? void 0 : `exit code ${result.status}`,
|
|
1158
|
+
code,
|
|
1159
|
+
...progress.length > 0 ? { progress } : {}
|
|
1160
|
+
};
|
|
1119
1161
|
}
|
|
1120
1162
|
function faceProduct(config) {
|
|
1121
1163
|
const known = { mmi: "mmi-hub", jerv: "jerv-hub" };
|
|
@@ -1123,10 +1165,11 @@ function faceProduct(config) {
|
|
|
1123
1165
|
}
|
|
1124
1166
|
function faceFor(config, options) {
|
|
1125
1167
|
const tty = options.tty ?? Boolean(process.stdout.isTTY);
|
|
1168
|
+
if (!tty) return null;
|
|
1126
1169
|
try {
|
|
1127
1170
|
return createFace({
|
|
1128
1171
|
product: faceProduct(config),
|
|
1129
|
-
color:
|
|
1172
|
+
color: !process.env.NO_COLOR && process.env.TERM !== "dumb",
|
|
1130
1173
|
columns: process.stdout.columns
|
|
1131
1174
|
});
|
|
1132
1175
|
} catch {
|
|
@@ -1136,9 +1179,13 @@ function faceFor(config, options) {
|
|
|
1136
1179
|
function since(start) {
|
|
1137
1180
|
return (Date.now() - start) / 1e3;
|
|
1138
1181
|
}
|
|
1182
|
+
function outerConsoleOwnsOutcome() {
|
|
1183
|
+
return process.env.MM_OUTER_CONSOLE === "1";
|
|
1184
|
+
}
|
|
1139
1185
|
function printReceipt(face, config, print, ready, lines) {
|
|
1140
1186
|
const name = face ? face.identity.name : config.product;
|
|
1141
1187
|
const headline = ready ? `${name} is ready.` : `${name} is installed, but not ready yet.`;
|
|
1188
|
+
if (ready && outerConsoleOwnsOutcome()) return;
|
|
1142
1189
|
if (!face) {
|
|
1143
1190
|
print(headline);
|
|
1144
1191
|
for (const line of lines) print(line.trim());
|
|
@@ -1151,6 +1198,11 @@ function printReceipt(face, config, print, ready, lines) {
|
|
|
1151
1198
|
function printStep(face, print, title, seconds, kind = "ok") {
|
|
1152
1199
|
print(face ? face.step(title, seconds, kind) : title);
|
|
1153
1200
|
}
|
|
1201
|
+
function printProgress(face, print, progress) {
|
|
1202
|
+
for (const message of progress ?? []) {
|
|
1203
|
+
printStep(face, print, message.step, message.ms === void 0 ? null : message.ms / 1e3, message.state);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1154
1206
|
async function doInstall(config, dir, options, print) {
|
|
1155
1207
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1156
1208
|
const face = faceFor(config, options);
|
|
@@ -1159,13 +1211,13 @@ async function doInstall(config, dir, options, print) {
|
|
|
1159
1211
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
1160
1212
|
await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
|
|
1161
1213
|
writeState(dir, { version: manifest.version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1162
|
-
printStep(face, print, `installed ${config.product} ${manifest.version}
|
|
1214
|
+
printStep(face, print, `installed ${config.product} ${manifest.version}`, since(started));
|
|
1163
1215
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1164
1216
|
}
|
|
1165
1217
|
async function doUpdate(config, dir, options, print) {
|
|
1166
1218
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1167
1219
|
const face = faceFor(config, options);
|
|
1168
|
-
if (face) for (const line of face.welcome()) print(line);
|
|
1220
|
+
if (face && !outerConsoleOwnsOutcome()) for (const line of face.welcome()) print(line);
|
|
1169
1221
|
const accessToken = await fetchAccessTokenOrLogin(config, dir, options, print);
|
|
1170
1222
|
const started = Date.now();
|
|
1171
1223
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
@@ -1179,32 +1231,69 @@ async function doUpdate(config, dir, options, print) {
|
|
|
1179
1231
|
printStep(face, print, `updated ${config.product} to ${manifest.version}`, since(started));
|
|
1180
1232
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1181
1233
|
}
|
|
1234
|
+
function payloadEnv(dir) {
|
|
1235
|
+
const stored = readTokens(dir);
|
|
1236
|
+
if (!stored?.accessToken) return void 0;
|
|
1237
|
+
return { ...process.env, MM_INSTALLER_TOKEN: stored.accessToken };
|
|
1238
|
+
}
|
|
1239
|
+
var NEVER_A_PROGRAM = /\.(tgz|tar|gz|bz2|xz|zip|json|txt|md|lock)$/i;
|
|
1240
|
+
function payloadFileAsCommand(entry, payload) {
|
|
1241
|
+
const first = entry[0];
|
|
1242
|
+
if (!first || first === "$self") return null;
|
|
1243
|
+
if (first.includes("/") || first.includes("\\")) return null;
|
|
1244
|
+
const candidate = (0, import_node_path4.join)(payload, first);
|
|
1245
|
+
if (!(0, import_node_fs5.existsSync)(candidate)) return null;
|
|
1246
|
+
if (NEVER_A_PROGRAM.test(first)) return first;
|
|
1247
|
+
if (process.platform === "win32") return null;
|
|
1248
|
+
try {
|
|
1249
|
+
return ((0, import_node_fs5.statSync)(candidate).mode & 73) === 0 ? first : null;
|
|
1250
|
+
} catch {
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1182
1254
|
function finishLastMile(config, dir, options, print, face, version) {
|
|
1183
1255
|
const entry = readPayloadEntry(dir);
|
|
1184
1256
|
const payload = payloadDir(dir);
|
|
1185
1257
|
if (!entry) {
|
|
1186
1258
|
printReceipt(face, config, print, true, [
|
|
1187
|
-
`
|
|
1188
|
-
`
|
|
1259
|
+
`Installed ${version} into ${dir}`,
|
|
1260
|
+
`next step: run ${(0, import_node_path4.join)(payload, config.binName)} to start ${config.product}.`
|
|
1189
1261
|
]);
|
|
1190
1262
|
return 0;
|
|
1191
1263
|
}
|
|
1192
1264
|
const command = resolveEntry(entry);
|
|
1265
|
+
const dataFile = payloadFileAsCommand(entry, payload);
|
|
1266
|
+
if (dataFile) {
|
|
1267
|
+
printStep(face, print, `The payload names ${dataFile} as its command, but that is a file, not a program`, null, "fail");
|
|
1268
|
+
printReceipt(face, config, print, false, [
|
|
1269
|
+
`Downloaded ${version} into ${dir}`,
|
|
1270
|
+
`This payload was built wrong: its entry must be a command, not one of its own files.`,
|
|
1271
|
+
`Nothing on this machine can finish it \u2014 report it to the ${config.product} maintainers.`
|
|
1272
|
+
]);
|
|
1273
|
+
return 2;
|
|
1274
|
+
}
|
|
1193
1275
|
const started = Date.now();
|
|
1194
|
-
const result = (options.runEntry ?? defaultRunEntry)(command, payload);
|
|
1276
|
+
const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
|
|
1277
|
+
printProgress(face, print, result.progress);
|
|
1195
1278
|
if (result.ok) {
|
|
1196
1279
|
printStep(face, print, "Armed this machine", since(started));
|
|
1197
1280
|
printReceipt(face, config, print, true, [
|
|
1198
|
-
`
|
|
1199
|
-
`
|
|
1281
|
+
`Installed ${version} into ${dir}`,
|
|
1282
|
+
`Check health any time: ${config.binName} doctor`
|
|
1200
1283
|
]);
|
|
1201
1284
|
return 0;
|
|
1202
1285
|
}
|
|
1203
1286
|
const code = result.code ?? 1;
|
|
1204
|
-
printStep(
|
|
1287
|
+
printStep(
|
|
1288
|
+
face,
|
|
1289
|
+
print,
|
|
1290
|
+
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})`,
|
|
1291
|
+
null,
|
|
1292
|
+
"fail"
|
|
1293
|
+
);
|
|
1205
1294
|
printReceipt(face, config, print, false, [
|
|
1206
|
-
`
|
|
1207
|
-
`
|
|
1295
|
+
`Downloaded ${version} into ${dir}`,
|
|
1296
|
+
`Finish it with: (cd ${payload} && ${command.join(" ")})`
|
|
1208
1297
|
]);
|
|
1209
1298
|
return code;
|
|
1210
1299
|
}
|
|
@@ -1222,7 +1311,7 @@ function doForward(config, dir, options, command, argv, print) {
|
|
|
1222
1311
|
return 2;
|
|
1223
1312
|
}
|
|
1224
1313
|
const forwarded = [...resolveEntry(target), ...argv];
|
|
1225
|
-
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir));
|
|
1314
|
+
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir), payloadEnv(dir));
|
|
1226
1315
|
if (!result.ok && result.code === void 0) {
|
|
1227
1316
|
print(`failed: ${result.error ?? `could not run ${forwarded[0]}`}`);
|
|
1228
1317
|
return 1;
|
|
@@ -1273,7 +1362,7 @@ function doDoctor(config, dir, options, print) {
|
|
|
1273
1362
|
const verbs = readPayloadVerbs(dir);
|
|
1274
1363
|
const chains = target !== null && (verbs === "*" || Array.isArray(verbs) && verbs.includes("doctor"));
|
|
1275
1364
|
if (!chains) return 0;
|
|
1276
|
-
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir));
|
|
1365
|
+
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir), payloadEnv(dir));
|
|
1277
1366
|
return result.code ?? (result.ok ? 0 : 1);
|
|
1278
1367
|
}
|
|
1279
1368
|
function doLauncherDoctor(config, dir, options, print) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/installer-launcher",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Single-executable (SEA) launcher: sign-in, gated payload fetch, self-update. One copy ships per product.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -34,6 +34,6 @@
|
|
|
34
34
|
"vitest": "^4.1.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@mutmutco/installer-face": "^0.1
|
|
37
|
+
"@mutmutco/installer-face": "^0.2.1"
|
|
38
38
|
}
|
|
39
39
|
}
|