@mutmutco/installer-launcher 0.1.4 → 0.1.8

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 CHANGED
@@ -88,20 +88,26 @@ function wrapWords(text, width) {
88
88
  if (line) lines.push(line);
89
89
  return lines.length ? lines : [""];
90
90
  }
91
- function createFace({ product, color = false, columns }) {
91
+ function createFace({ product, color = false, columns, env = process.env }) {
92
92
  const identity = identityFor(product);
93
93
  const width = faceWidth(columns);
94
94
  const paint = (sgr, text) => color ? `\x1B[${sgr}m${text}\x1B[0m` : String(text);
95
95
  const bar = () => paint(PALETTE.muted, GLYPH.bar);
96
96
  const indent = " ".repeat(TITLE_COLUMN - 1);
97
- const welcome = () => [
97
+ const continuedPhases = new Set((env.MM_FACE_CONTINUES ?? "").split(",").map((phase) => phase.trim()).filter(Boolean));
98
+ const continuesFace = continuedPhases.size > 0;
99
+ const welcome = () => continuesFace ? [] : [
98
100
  `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, identity.name)} \u2014 Mutatis Mutandis`,
99
101
  bar(),
100
102
  `${bar()} ${identity.warm}`,
101
103
  bar()
102
104
  ];
105
+ const continues = (phase, kind = "ok") => {
106
+ const inherited = continuedPhases.delete(String(phase).trim());
107
+ return kind === "fail" ? false : inherited;
108
+ };
103
109
  const step = (title, seconds = null, kind = "ok") => {
104
- const glyph = kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.hollow) : paint(PALETTE.green, GLYPH.check);
110
+ const glyph = kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.dot) : paint(PALETTE.green, GLYPH.check);
105
111
  const time = seconds === null || seconds === void 0 ? "" : paint(PALETTE.muted, `${Math.max(0, Math.round(seconds))}s`);
106
112
  const column = Math.min(44, Math.max(0, width - 8));
107
113
  const reserved = time ? 6 : 0;
@@ -115,8 +121,7 @@ function createFace({ product, color = false, columns }) {
115
121
  const body = (Array.isArray(lines) ? lines : String(lines).split("\n")).flatMap((raw) => {
116
122
  const line = String(raw);
117
123
  if (visibleWidth(line) <= width - 6) return [line];
118
- const lead = /^\s*/u.exec(line)?.[0] ?? "";
119
- return wrapWords(line, width - 6 - lead.length).map((part) => `${lead}${part}`);
124
+ return wrapWords(line, width - 6);
120
125
  });
121
126
  const content = Math.min(width - 6, Math.max(...body.map(visibleWidth)));
122
127
  const rule = paint(identity.accent, GLYPH.rule.repeat(content + 4));
@@ -130,7 +135,7 @@ function createFace({ product, color = false, columns }) {
130
135
  };
131
136
  const signOff = () => `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, `${identity.name} \xB7 Mutatis Mutandis`)}`;
132
137
  const refusal = (message) => `${bar()} ${paint(identity.accent, GLYPH.cross)} ${message}`;
133
- return { identity, width, welcome, step, relay, receipt, signOff, refusal, paint };
138
+ return { identity, width, welcome, continues, step, relay, receipt, signOff, refusal, paint };
134
139
  }
135
140
  var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
136
141
  var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
@@ -863,7 +868,7 @@ function wipeProductDir(dir) {
863
868
  }
864
869
 
865
870
  // src/index.ts
866
- var LAUNCHER_VERSION = true ? "0.1.4" : readVersionFromPackage();
871
+ var LAUNCHER_VERSION = true ? "0.1.8" : readVersionFromPackage();
867
872
  function defaultPrint(message) {
868
873
  process.stdout.write(`${message}
869
874
  `);
@@ -1075,20 +1080,58 @@ function needsShell(command) {
1075
1080
  function quoteForShell(arg) {
1076
1081
  return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
1077
1082
  }
1083
+ var SUPPORTED_PROGRESS_PROTOCOLS = /* @__PURE__ */ new Set([1]);
1084
+ function parseProgress(raw) {
1085
+ if (raw === null || raw === void 0) return [];
1086
+ const progress = [];
1087
+ for (const line of raw.toString().split(/\r?\n/)) {
1088
+ if (!line) continue;
1089
+ try {
1090
+ const message = JSON.parse(line);
1091
+ if (!SUPPORTED_PROGRESS_PROTOCOLS.has(message.v) || typeof message.step !== "string") continue;
1092
+ const state = message.state ?? "ok";
1093
+ if (state !== "ok" && state !== "fail" && state !== "note") continue;
1094
+ if (message.ms !== void 0 && typeof message.ms !== "number") continue;
1095
+ progress.push({
1096
+ step: message.step,
1097
+ state,
1098
+ ...typeof message.ms === "number" ? { ms: message.ms } : {}
1099
+ });
1100
+ } catch {
1101
+ }
1102
+ }
1103
+ return progress;
1104
+ }
1078
1105
  function defaultRunEntry(entry, cwd, env) {
1079
1106
  const [command, ...args] = entry;
1080
1107
  const shell = needsShell(command);
1081
1108
  const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
1082
- const result = spawnSync2(commandLine, shell ? [] : args, {
1109
+ const spawn2 = (progress2) => spawnSync2(commandLine, shell ? [] : args, {
1083
1110
  cwd,
1084
- stdio: "inherit",
1111
+ stdio: progress2 ? ["inherit", "inherit", "inherit", "pipe"] : "inherit",
1085
1112
  shell,
1086
- env: env ?? process.env,
1113
+ env: progress2 ? { ...env ?? process.env, MM_PROGRESS_FD: "3", MM_PROGRESS_PROTOCOL: "1" } : env ?? process.env,
1087
1114
  windowsHide: true
1088
1115
  });
1116
+ let result;
1117
+ if (process.platform === "win32" && shell) {
1118
+ result = spawn2(false);
1119
+ } else {
1120
+ try {
1121
+ result = spawn2(true);
1122
+ } catch {
1123
+ result = spawn2(false);
1124
+ }
1125
+ }
1089
1126
  if (result.error) return { ok: false, error: result.error.message };
1090
1127
  const code = typeof result.status === "number" ? result.status : void 0;
1091
- return { ok: result.status === 0, error: result.status === 0 ? void 0 : `exit code ${result.status}`, code };
1128
+ const progress = parseProgress(result.output?.[3]);
1129
+ return {
1130
+ ok: result.status === 0,
1131
+ error: result.status === 0 ? void 0 : `exit code ${result.status}`,
1132
+ code,
1133
+ ...progress.length > 0 ? { progress } : {}
1134
+ };
1092
1135
  }
1093
1136
  function faceProduct(config) {
1094
1137
  const known = { mmi: "mmi-hub", jerv: "jerv-hub" };
@@ -1110,9 +1153,13 @@ function faceFor(config, options) {
1110
1153
  function since(start) {
1111
1154
  return (Date.now() - start) / 1e3;
1112
1155
  }
1156
+ function outerConsoleOwnsOutcome() {
1157
+ return process.env.MM_OUTER_CONSOLE === "1";
1158
+ }
1113
1159
  function printReceipt(face, config, print, ready, lines) {
1114
1160
  const name = face ? face.identity.name : config.product;
1115
1161
  const headline = ready ? `${name} is ready.` : `${name} is installed, but not ready yet.`;
1162
+ if (ready && outerConsoleOwnsOutcome()) return;
1116
1163
  if (!face) {
1117
1164
  print(headline);
1118
1165
  for (const line of lines) print(line.trim());
@@ -1125,6 +1172,11 @@ function printReceipt(face, config, print, ready, lines) {
1125
1172
  function printStep(face, print, title, seconds, kind = "ok") {
1126
1173
  print(face ? face.step(title, seconds, kind) : title);
1127
1174
  }
1175
+ function printProgress(face, print, progress) {
1176
+ for (const message of progress ?? []) {
1177
+ printStep(face, print, message.step, message.ms === void 0 ? null : message.ms / 1e3, message.state);
1178
+ }
1179
+ }
1128
1180
  async function doInstall(config, dir, options, print) {
1129
1181
  const fetchImpl = options.fetchImpl ?? fetch;
1130
1182
  const face = faceFor(config, options);
@@ -1139,7 +1191,7 @@ async function doInstall(config, dir, options, print) {
1139
1191
  async function doUpdate(config, dir, options, print) {
1140
1192
  const fetchImpl = options.fetchImpl ?? fetch;
1141
1193
  const face = faceFor(config, options);
1142
- if (face) for (const line of face.welcome()) print(line);
1194
+ if (face && !outerConsoleOwnsOutcome()) for (const line of face.welcome()) print(line);
1143
1195
  const accessToken = await fetchAccessTokenOrLogin(config, dir, options, print);
1144
1196
  const started = Date.now();
1145
1197
  const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
@@ -1178,8 +1230,8 @@ function finishLastMile(config, dir, options, print, face, version) {
1178
1230
  const payload = payloadDir(dir);
1179
1231
  if (!entry) {
1180
1232
  printReceipt(face, config, print, true, [
1181
- ` Installed ${version} into ${dir}`,
1182
- ` next step: run ${join4(payload, config.binName)} to start ${config.product}.`
1233
+ `Installed ${version} into ${dir}`,
1234
+ `next step: run ${join4(payload, config.binName)} to start ${config.product}.`
1183
1235
  ]);
1184
1236
  return 0;
1185
1237
  }
@@ -1188,19 +1240,20 @@ function finishLastMile(config, dir, options, print, face, version) {
1188
1240
  if (dataFile) {
1189
1241
  printStep(face, print, `The payload names ${dataFile} as its command, but that is a file, not a program`, null, "fail");
1190
1242
  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.`
1243
+ `Downloaded ${version} into ${dir}`,
1244
+ `This payload was built wrong: its entry must be a command, not one of its own files.`,
1245
+ `Nothing on this machine can finish it \u2014 report it to the ${config.product} maintainers.`
1194
1246
  ]);
1195
1247
  return 2;
1196
1248
  }
1197
1249
  const started = Date.now();
1198
1250
  const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
1251
+ printProgress(face, print, result.progress);
1199
1252
  if (result.ok) {
1200
1253
  printStep(face, print, "Armed this machine", since(started));
1201
1254
  printReceipt(face, config, print, true, [
1202
- ` Installed ${version} into ${dir}`,
1203
- ` Check health any time: ${config.binName} doctor`
1255
+ `Installed ${version} into ${dir}`,
1256
+ `Check health any time: ${config.binName} doctor`
1204
1257
  ]);
1205
1258
  return 0;
1206
1259
  }
@@ -1213,8 +1266,8 @@ function finishLastMile(config, dir, options, print, face, version) {
1213
1266
  "fail"
1214
1267
  );
1215
1268
  printReceipt(face, config, print, false, [
1216
- ` Downloaded ${version} into ${dir}`,
1217
- ` Finish it with: (cd ${payload} && ${command.join(" ")})`
1269
+ `Downloaded ${version} into ${dir}`,
1270
+ `Finish it with: (cd ${payload} && ${command.join(" ")})`
1218
1271
  ]);
1219
1272
  return code;
1220
1273
  }
@@ -118,20 +118,26 @@ function wrapWords(text, width) {
118
118
  if (line) lines.push(line);
119
119
  return lines.length ? lines : [""];
120
120
  }
121
- function createFace({ product, color = false, columns }) {
121
+ function createFace({ product, color = false, columns, env = process.env }) {
122
122
  const identity = identityFor(product);
123
123
  const width = faceWidth(columns);
124
124
  const paint = (sgr, text) => color ? `\x1B[${sgr}m${text}\x1B[0m` : String(text);
125
125
  const bar = () => paint(PALETTE.muted, GLYPH.bar);
126
126
  const indent = " ".repeat(TITLE_COLUMN - 1);
127
- const welcome = () => [
127
+ const continuedPhases = new Set((env.MM_FACE_CONTINUES ?? "").split(",").map((phase) => phase.trim()).filter(Boolean));
128
+ const continuesFace = continuedPhases.size > 0;
129
+ const welcome = () => continuesFace ? [] : [
128
130
  `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, identity.name)} \u2014 Mutatis Mutandis`,
129
131
  bar(),
130
132
  `${bar()} ${identity.warm}`,
131
133
  bar()
132
134
  ];
135
+ const continues = (phase, kind = "ok") => {
136
+ const inherited = continuedPhases.delete(String(phase).trim());
137
+ return kind === "fail" ? false : inherited;
138
+ };
133
139
  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);
140
+ const glyph = kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.dot) : paint(PALETTE.green, GLYPH.check);
135
141
  const time = seconds === null || seconds === void 0 ? "" : paint(PALETTE.muted, `${Math.max(0, Math.round(seconds))}s`);
136
142
  const column = Math.min(44, Math.max(0, width - 8));
137
143
  const reserved = time ? 6 : 0;
@@ -145,8 +151,7 @@ function createFace({ product, color = false, columns }) {
145
151
  const body = (Array.isArray(lines) ? lines : String(lines).split("\n")).flatMap((raw) => {
146
152
  const line = String(raw);
147
153
  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}`);
154
+ return wrapWords(line, width - 6);
150
155
  });
151
156
  const content = Math.min(width - 6, Math.max(...body.map(visibleWidth)));
152
157
  const rule = paint(identity.accent, GLYPH.rule.repeat(content + 4));
@@ -160,7 +165,7 @@ function createFace({ product, color = false, columns }) {
160
165
  };
161
166
  const signOff = () => `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, `${identity.name} \xB7 Mutatis Mutandis`)}`;
162
167
  const refusal = (message) => `${bar()} ${paint(identity.accent, GLYPH.cross)} ${message}`;
163
- return { identity, width, welcome, step, relay, receipt, signOff, refusal, paint };
168
+ return { identity, width, welcome, continues, step, relay, receipt, signOff, refusal, paint };
164
169
  }
165
170
  var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
166
171
  var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
@@ -894,7 +899,7 @@ function wipeProductDir(dir) {
894
899
  }
895
900
 
896
901
  // src/index.ts
897
- var LAUNCHER_VERSION = true ? "0.1.4" : readVersionFromPackage();
902
+ var LAUNCHER_VERSION = true ? "0.1.8" : readVersionFromPackage();
898
903
  function defaultPrint(message) {
899
904
  process.stdout.write(`${message}
900
905
  `);
@@ -1106,20 +1111,58 @@ function needsShell(command) {
1106
1111
  function quoteForShell(arg) {
1107
1112
  return /[\s"&|<>^%]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
1108
1113
  }
1114
+ var SUPPORTED_PROGRESS_PROTOCOLS = /* @__PURE__ */ new Set([1]);
1115
+ function parseProgress(raw) {
1116
+ if (raw === null || raw === void 0) return [];
1117
+ const progress = [];
1118
+ for (const line of raw.toString().split(/\r?\n/)) {
1119
+ if (!line) continue;
1120
+ try {
1121
+ const message = JSON.parse(line);
1122
+ if (!SUPPORTED_PROGRESS_PROTOCOLS.has(message.v) || typeof message.step !== "string") continue;
1123
+ const state = message.state ?? "ok";
1124
+ if (state !== "ok" && state !== "fail" && state !== "note") continue;
1125
+ if (message.ms !== void 0 && typeof message.ms !== "number") continue;
1126
+ progress.push({
1127
+ step: message.step,
1128
+ state,
1129
+ ...typeof message.ms === "number" ? { ms: message.ms } : {}
1130
+ });
1131
+ } catch {
1132
+ }
1133
+ }
1134
+ return progress;
1135
+ }
1109
1136
  function defaultRunEntry(entry, cwd, env) {
1110
1137
  const [command, ...args] = entry;
1111
1138
  const shell = needsShell(command);
1112
1139
  const commandLine = shell ? [command, ...args].map(quoteForShell).join(" ") : command;
1113
- const result = (0, import_node_child_process3.spawnSync)(commandLine, shell ? [] : args, {
1140
+ const spawn2 = (progress2) => (0, import_node_child_process3.spawnSync)(commandLine, shell ? [] : args, {
1114
1141
  cwd,
1115
- stdio: "inherit",
1142
+ stdio: progress2 ? ["inherit", "inherit", "inherit", "pipe"] : "inherit",
1116
1143
  shell,
1117
- env: env ?? process.env,
1144
+ env: progress2 ? { ...env ?? process.env, MM_PROGRESS_FD: "3", MM_PROGRESS_PROTOCOL: "1" } : env ?? process.env,
1118
1145
  windowsHide: true
1119
1146
  });
1147
+ let result;
1148
+ if (process.platform === "win32" && shell) {
1149
+ result = spawn2(false);
1150
+ } else {
1151
+ try {
1152
+ result = spawn2(true);
1153
+ } catch {
1154
+ result = spawn2(false);
1155
+ }
1156
+ }
1120
1157
  if (result.error) return { ok: false, error: result.error.message };
1121
1158
  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 };
1159
+ const progress = parseProgress(result.output?.[3]);
1160
+ return {
1161
+ ok: result.status === 0,
1162
+ error: result.status === 0 ? void 0 : `exit code ${result.status}`,
1163
+ code,
1164
+ ...progress.length > 0 ? { progress } : {}
1165
+ };
1123
1166
  }
1124
1167
  function faceProduct(config) {
1125
1168
  const known = { mmi: "mmi-hub", jerv: "jerv-hub" };
@@ -1141,9 +1184,13 @@ function faceFor(config, options) {
1141
1184
  function since(start) {
1142
1185
  return (Date.now() - start) / 1e3;
1143
1186
  }
1187
+ function outerConsoleOwnsOutcome() {
1188
+ return process.env.MM_OUTER_CONSOLE === "1";
1189
+ }
1144
1190
  function printReceipt(face, config, print, ready, lines) {
1145
1191
  const name = face ? face.identity.name : config.product;
1146
1192
  const headline = ready ? `${name} is ready.` : `${name} is installed, but not ready yet.`;
1193
+ if (ready && outerConsoleOwnsOutcome()) return;
1147
1194
  if (!face) {
1148
1195
  print(headline);
1149
1196
  for (const line of lines) print(line.trim());
@@ -1156,6 +1203,11 @@ function printReceipt(face, config, print, ready, lines) {
1156
1203
  function printStep(face, print, title, seconds, kind = "ok") {
1157
1204
  print(face ? face.step(title, seconds, kind) : title);
1158
1205
  }
1206
+ function printProgress(face, print, progress) {
1207
+ for (const message of progress ?? []) {
1208
+ printStep(face, print, message.step, message.ms === void 0 ? null : message.ms / 1e3, message.state);
1209
+ }
1210
+ }
1159
1211
  async function doInstall(config, dir, options, print) {
1160
1212
  const fetchImpl = options.fetchImpl ?? fetch;
1161
1213
  const face = faceFor(config, options);
@@ -1170,7 +1222,7 @@ async function doInstall(config, dir, options, print) {
1170
1222
  async function doUpdate(config, dir, options, print) {
1171
1223
  const fetchImpl = options.fetchImpl ?? fetch;
1172
1224
  const face = faceFor(config, options);
1173
- if (face) for (const line of face.welcome()) print(line);
1225
+ if (face && !outerConsoleOwnsOutcome()) for (const line of face.welcome()) print(line);
1174
1226
  const accessToken = await fetchAccessTokenOrLogin(config, dir, options, print);
1175
1227
  const started = Date.now();
1176
1228
  const manifest = await fetchVerifiedManifest(config, accessToken, fetchImpl);
@@ -1209,8 +1261,8 @@ function finishLastMile(config, dir, options, print, face, version) {
1209
1261
  const payload = payloadDir(dir);
1210
1262
  if (!entry) {
1211
1263
  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}.`
1264
+ `Installed ${version} into ${dir}`,
1265
+ `next step: run ${(0, import_node_path4.join)(payload, config.binName)} to start ${config.product}.`
1214
1266
  ]);
1215
1267
  return 0;
1216
1268
  }
@@ -1219,19 +1271,20 @@ function finishLastMile(config, dir, options, print, face, version) {
1219
1271
  if (dataFile) {
1220
1272
  printStep(face, print, `The payload names ${dataFile} as its command, but that is a file, not a program`, null, "fail");
1221
1273
  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.`
1274
+ `Downloaded ${version} into ${dir}`,
1275
+ `This payload was built wrong: its entry must be a command, not one of its own files.`,
1276
+ `Nothing on this machine can finish it \u2014 report it to the ${config.product} maintainers.`
1225
1277
  ]);
1226
1278
  return 2;
1227
1279
  }
1228
1280
  const started = Date.now();
1229
1281
  const result = (options.runEntry ?? defaultRunEntry)(command, payload, payloadEnv(dir));
1282
+ printProgress(face, print, result.progress);
1230
1283
  if (result.ok) {
1231
1284
  printStep(face, print, "Armed this machine", since(started));
1232
1285
  printReceipt(face, config, print, true, [
1233
- ` Installed ${version} into ${dir}`,
1234
- ` Check health any time: ${config.binName} doctor`
1286
+ `Installed ${version} into ${dir}`,
1287
+ `Check health any time: ${config.binName} doctor`
1235
1288
  ]);
1236
1289
  return 0;
1237
1290
  }
@@ -1244,8 +1297,8 @@ function finishLastMile(config, dir, options, print, face, version) {
1244
1297
  "fail"
1245
1298
  );
1246
1299
  printReceipt(face, config, print, false, [
1247
- ` Downloaded ${version} into ${dir}`,
1248
- ` Finish it with: (cd ${payload} && ${command.join(" ")})`
1300
+ `Downloaded ${version} into ${dir}`,
1301
+ `Finish it with: (cd ${payload} && ${command.join(" ")})`
1249
1302
  ]);
1250
1303
  return code;
1251
1304
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/installer-launcher",
3
- "version": "0.1.4",
3
+ "version": "0.1.8",
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.2.1"
37
+ "@mutmutco/installer-face": "^0.2.3"
38
38
  }
39
39
  }