@mutmutco/installer-launcher 0.1.3 → 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 +51 -10
- package/dist/launcher.sea.cjs +50 -9
- 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.4" : readVersionFromPackage();
|
|
864
867
|
function defaultPrint(message) {
|
|
865
868
|
process.stdout.write(`${message}
|
|
866
869
|
`);
|
|
@@ -1072,7 +1075,7 @@ function needsShell(command) {
|
|
|
1072
1075
|
function quoteForShell(arg) {
|
|
1073
1076
|
return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
1074
1077
|
}
|
|
1075
|
-
function defaultRunEntry(entry, cwd) {
|
|
1078
|
+
function defaultRunEntry(entry, cwd, env) {
|
|
1076
1079
|
const [command, ...args] = entry;
|
|
1077
1080
|
const shell = needsShell(command);
|
|
1078
1081
|
const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
|
|
@@ -1080,6 +1083,7 @@ function defaultRunEntry(entry, cwd) {
|
|
|
1080
1083
|
cwd,
|
|
1081
1084
|
stdio: "inherit",
|
|
1082
1085
|
shell,
|
|
1086
|
+
env: env ?? process.env,
|
|
1083
1087
|
windowsHide: true
|
|
1084
1088
|
});
|
|
1085
1089
|
if (result.error) return { ok: false, error: result.error.message };
|
|
@@ -1092,10 +1096,11 @@ function faceProduct(config) {
|
|
|
1092
1096
|
}
|
|
1093
1097
|
function faceFor(config, options) {
|
|
1094
1098
|
const tty = options.tty ?? Boolean(process.stdout.isTTY);
|
|
1099
|
+
if (!tty) return null;
|
|
1095
1100
|
try {
|
|
1096
1101
|
return createFace({
|
|
1097
1102
|
product: faceProduct(config),
|
|
1098
|
-
color:
|
|
1103
|
+
color: !process.env.NO_COLOR && process.env.TERM !== "dumb",
|
|
1099
1104
|
columns: process.stdout.columns
|
|
1100
1105
|
});
|
|
1101
1106
|
} catch {
|
|
@@ -1128,7 +1133,7 @@ async function doInstall(config, dir, options, print) {
|
|
|
1128
1133
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
1129
1134
|
await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
|
|
1130
1135
|
writeState(dir, { version: manifest.version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1131
|
-
printStep(face, print, `installed ${config.product} ${manifest.version}
|
|
1136
|
+
printStep(face, print, `installed ${config.product} ${manifest.version}`, since(started));
|
|
1132
1137
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1133
1138
|
}
|
|
1134
1139
|
async function doUpdate(config, dir, options, print) {
|
|
@@ -1148,6 +1153,26 @@ async function doUpdate(config, dir, options, print) {
|
|
|
1148
1153
|
printStep(face, print, `updated ${config.product} to ${manifest.version}`, since(started));
|
|
1149
1154
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1150
1155
|
}
|
|
1156
|
+
function payloadEnv(dir) {
|
|
1157
|
+
const stored = readTokens(dir);
|
|
1158
|
+
if (!stored?.accessToken) return void 0;
|
|
1159
|
+
return { ...process.env, MM_INSTALLER_TOKEN: stored.accessToken };
|
|
1160
|
+
}
|
|
1161
|
+
var NEVER_A_PROGRAM = /\.(tgz|tar|gz|bz2|xz|zip|json|txt|md|lock)$/i;
|
|
1162
|
+
function payloadFileAsCommand(entry, payload) {
|
|
1163
|
+
const first = entry[0];
|
|
1164
|
+
if (!first || first === "$self") return null;
|
|
1165
|
+
if (first.includes("/") || first.includes("\\")) return null;
|
|
1166
|
+
const candidate = join4(payload, first);
|
|
1167
|
+
if (!existsSync4(candidate)) return null;
|
|
1168
|
+
if (NEVER_A_PROGRAM.test(first)) return first;
|
|
1169
|
+
if (process.platform === "win32") return null;
|
|
1170
|
+
try {
|
|
1171
|
+
return (statSync(candidate).mode & 73) === 0 ? first : null;
|
|
1172
|
+
} catch {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1151
1176
|
function finishLastMile(config, dir, options, print, face, version) {
|
|
1152
1177
|
const entry = readPayloadEntry(dir);
|
|
1153
1178
|
const payload = payloadDir(dir);
|
|
@@ -1159,8 +1184,18 @@ function finishLastMile(config, dir, options, print, face, version) {
|
|
|
1159
1184
|
return 0;
|
|
1160
1185
|
}
|
|
1161
1186
|
const command = resolveEntry(entry);
|
|
1187
|
+
const dataFile = payloadFileAsCommand(entry, payload);
|
|
1188
|
+
if (dataFile) {
|
|
1189
|
+
printStep(face, print, `The payload names ${dataFile} as its command, but that is a file, not a program`, null, "fail");
|
|
1190
|
+
printReceipt(face, config, print, false, [
|
|
1191
|
+
` Downloaded ${version} into ${dir}`,
|
|
1192
|
+
` This payload was built wrong: its entry must be a command, not one of its own files.`,
|
|
1193
|
+
` Nothing on this machine can finish it \u2014 report it to the ${config.product} maintainers.`
|
|
1194
|
+
]);
|
|
1195
|
+
return 2;
|
|
1196
|
+
}
|
|
1162
1197
|
const started = Date.now();
|
|
1163
|
-
const result = (options.runEntry ?? defaultRunEntry)(command, payload);
|
|
1198
|
+
const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
|
|
1164
1199
|
if (result.ok) {
|
|
1165
1200
|
printStep(face, print, "Armed this machine", since(started));
|
|
1166
1201
|
printReceipt(face, config, print, true, [
|
|
@@ -1170,7 +1205,13 @@ function finishLastMile(config, dir, options, print, face, version) {
|
|
|
1170
1205
|
return 0;
|
|
1171
1206
|
}
|
|
1172
1207
|
const code = result.code ?? 1;
|
|
1173
|
-
printStep(
|
|
1208
|
+
printStep(
|
|
1209
|
+
face,
|
|
1210
|
+
print,
|
|
1211
|
+
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})`,
|
|
1212
|
+
null,
|
|
1213
|
+
"fail"
|
|
1214
|
+
);
|
|
1174
1215
|
printReceipt(face, config, print, false, [
|
|
1175
1216
|
` Downloaded ${version} into ${dir}`,
|
|
1176
1217
|
` Finish it with: (cd ${payload} && ${command.join(" ")})`
|
|
@@ -1191,7 +1232,7 @@ function doForward(config, dir, options, command, argv, print) {
|
|
|
1191
1232
|
return 2;
|
|
1192
1233
|
}
|
|
1193
1234
|
const forwarded = [...resolveEntry(target), ...argv];
|
|
1194
|
-
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir));
|
|
1235
|
+
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir), payloadEnv(dir));
|
|
1195
1236
|
if (!result.ok && result.code === void 0) {
|
|
1196
1237
|
print(`failed: ${result.error ?? `could not run ${forwarded[0]}`}`);
|
|
1197
1238
|
return 1;
|
|
@@ -1242,7 +1283,7 @@ function doDoctor(config, dir, options, print) {
|
|
|
1242
1283
|
const verbs = readPayloadVerbs(dir);
|
|
1243
1284
|
const chains = target !== null && (verbs === "*" || Array.isArray(verbs) && verbs.includes("doctor"));
|
|
1244
1285
|
if (!chains) return 0;
|
|
1245
|
-
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir));
|
|
1286
|
+
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir), payloadEnv(dir));
|
|
1246
1287
|
return result.code ?? (result.ok ? 0 : 1);
|
|
1247
1288
|
}
|
|
1248
1289
|
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.4" : readVersionFromPackage();
|
|
895
898
|
function defaultPrint(message) {
|
|
896
899
|
process.stdout.write(`${message}
|
|
897
900
|
`);
|
|
@@ -1103,7 +1106,7 @@ function needsShell(command) {
|
|
|
1103
1106
|
function quoteForShell(arg) {
|
|
1104
1107
|
return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
1105
1108
|
}
|
|
1106
|
-
function defaultRunEntry(entry, cwd) {
|
|
1109
|
+
function defaultRunEntry(entry, cwd, env) {
|
|
1107
1110
|
const [command, ...args] = entry;
|
|
1108
1111
|
const shell = needsShell(command);
|
|
1109
1112
|
const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
|
|
@@ -1111,6 +1114,7 @@ function defaultRunEntry(entry, cwd) {
|
|
|
1111
1114
|
cwd,
|
|
1112
1115
|
stdio: "inherit",
|
|
1113
1116
|
shell,
|
|
1117
|
+
env: env ?? process.env,
|
|
1114
1118
|
windowsHide: true
|
|
1115
1119
|
});
|
|
1116
1120
|
if (result.error) return { ok: false, error: result.error.message };
|
|
@@ -1123,10 +1127,11 @@ function faceProduct(config) {
|
|
|
1123
1127
|
}
|
|
1124
1128
|
function faceFor(config, options) {
|
|
1125
1129
|
const tty = options.tty ?? Boolean(process.stdout.isTTY);
|
|
1130
|
+
if (!tty) return null;
|
|
1126
1131
|
try {
|
|
1127
1132
|
return createFace({
|
|
1128
1133
|
product: faceProduct(config),
|
|
1129
|
-
color:
|
|
1134
|
+
color: !process.env.NO_COLOR && process.env.TERM !== "dumb",
|
|
1130
1135
|
columns: process.stdout.columns
|
|
1131
1136
|
});
|
|
1132
1137
|
} catch {
|
|
@@ -1159,7 +1164,7 @@ async function doInstall(config, dir, options, print) {
|
|
|
1159
1164
|
const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
|
|
1160
1165
|
await downloadAndUnpack(config, dir, manifest, accessToken, { fetchImpl });
|
|
1161
1166
|
writeState(dir, { version: manifest.version, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1162
|
-
printStep(face, print, `installed ${config.product} ${manifest.version}
|
|
1167
|
+
printStep(face, print, `installed ${config.product} ${manifest.version}`, since(started));
|
|
1163
1168
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1164
1169
|
}
|
|
1165
1170
|
async function doUpdate(config, dir, options, print) {
|
|
@@ -1179,6 +1184,26 @@ async function doUpdate(config, dir, options, print) {
|
|
|
1179
1184
|
printStep(face, print, `updated ${config.product} to ${manifest.version}`, since(started));
|
|
1180
1185
|
return finishLastMile(config, dir, options, print, face, manifest.version);
|
|
1181
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
|
+
}
|
|
1182
1207
|
function finishLastMile(config, dir, options, print, face, version) {
|
|
1183
1208
|
const entry = readPayloadEntry(dir);
|
|
1184
1209
|
const payload = payloadDir(dir);
|
|
@@ -1190,8 +1215,18 @@ function finishLastMile(config, dir, options, print, face, version) {
|
|
|
1190
1215
|
return 0;
|
|
1191
1216
|
}
|
|
1192
1217
|
const command = resolveEntry(entry);
|
|
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
|
+
}
|
|
1193
1228
|
const started = Date.now();
|
|
1194
|
-
const result = (options.runEntry ?? defaultRunEntry)(command, payload);
|
|
1229
|
+
const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
|
|
1195
1230
|
if (result.ok) {
|
|
1196
1231
|
printStep(face, print, "Armed this machine", since(started));
|
|
1197
1232
|
printReceipt(face, config, print, true, [
|
|
@@ -1201,7 +1236,13 @@ function finishLastMile(config, dir, options, print, face, version) {
|
|
|
1201
1236
|
return 0;
|
|
1202
1237
|
}
|
|
1203
1238
|
const code = result.code ?? 1;
|
|
1204
|
-
printStep(
|
|
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
|
+
);
|
|
1205
1246
|
printReceipt(face, config, print, false, [
|
|
1206
1247
|
` Downloaded ${version} into ${dir}`,
|
|
1207
1248
|
` Finish it with: (cd ${payload} && ${command.join(" ")})`
|
|
@@ -1222,7 +1263,7 @@ function doForward(config, dir, options, command, argv, print) {
|
|
|
1222
1263
|
return 2;
|
|
1223
1264
|
}
|
|
1224
1265
|
const forwarded = [...resolveEntry(target), ...argv];
|
|
1225
|
-
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir));
|
|
1266
|
+
const result = (options.runEntry ?? defaultRunEntry)(forwarded, payloadDir(dir), payloadEnv(dir));
|
|
1226
1267
|
if (!result.ok && result.code === void 0) {
|
|
1227
1268
|
print(`failed: ${result.error ?? `could not run ${forwarded[0]}`}`);
|
|
1228
1269
|
return 1;
|
|
@@ -1273,7 +1314,7 @@ function doDoctor(config, dir, options, print) {
|
|
|
1273
1314
|
const verbs = readPayloadVerbs(dir);
|
|
1274
1315
|
const chains = target !== null && (verbs === "*" || Array.isArray(verbs) && verbs.includes("doctor"));
|
|
1275
1316
|
if (!chains) return 0;
|
|
1276
|
-
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir));
|
|
1317
|
+
const result = (options.runEntry ?? defaultRunEntry)([...resolveEntry(target), "doctor"], payloadDir(dir), payloadEnv(dir));
|
|
1277
1318
|
return result.code ?? (result.ok ? 0 : 1);
|
|
1278
1319
|
}
|
|
1279
1320
|
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.4",
|
|
4
4
|
"description": "Single-executable (SEA) launcher: sign-in, gated payload fetch, self-update. One copy ships per product.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -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
|
}
|