@mutmutco/hub 4.4.11 → 4.4.12
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/index.cjs +113 -21
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -95,30 +95,30 @@ var import_node_fs = require("node:fs");
|
|
|
95
95
|
var PRODUCTS = Object.freeze({
|
|
96
96
|
"mm-strategy": Object.freeze({
|
|
97
97
|
name: "MM Strategy",
|
|
98
|
-
installWarm: "Welcome. Let's
|
|
98
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
99
99
|
accent: "38;2;249;115;22",
|
|
100
|
-
warm: "Welcome.
|
|
100
|
+
warm: "Welcome back. Checking for updates...",
|
|
101
101
|
doctor: "mm-strategy doctor"
|
|
102
102
|
}),
|
|
103
103
|
"mmi-hub": Object.freeze({
|
|
104
104
|
name: "mmi-hub",
|
|
105
|
-
installWarm: "Welcome.
|
|
105
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
106
106
|
accent: "38;2;125;211;252",
|
|
107
|
-
warm: "Welcome back. Checking
|
|
107
|
+
warm: "Welcome back. Checking for updates...",
|
|
108
108
|
doctor: "mmi doctor"
|
|
109
109
|
}),
|
|
110
110
|
"jerv-hub": Object.freeze({
|
|
111
111
|
name: "jerv-hub",
|
|
112
|
-
installWarm: "Welcome.
|
|
112
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
113
113
|
accent: "38;2;248;113;113",
|
|
114
|
-
warm: "Welcome back. Checking
|
|
114
|
+
warm: "Welcome back. Checking for updates...",
|
|
115
115
|
doctor: "jerv doctor"
|
|
116
116
|
}),
|
|
117
117
|
jervcode: Object.freeze({
|
|
118
118
|
name: "JervCode",
|
|
119
|
-
installWarm: "Welcome.
|
|
119
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
120
120
|
accent: "38;2;192;132;252",
|
|
121
|
-
warm: "Welcome back.
|
|
121
|
+
warm: "Welcome back. Checking for updates...",
|
|
122
122
|
doctor: "jervcode doctor"
|
|
123
123
|
})
|
|
124
124
|
});
|
|
@@ -132,6 +132,7 @@ function identityFor(product) {
|
|
|
132
132
|
|
|
133
133
|
// ../installer/face/src/face.ts
|
|
134
134
|
var GLYPH = Object.freeze({
|
|
135
|
+
clock: "\u23F0",
|
|
135
136
|
diamond: "\u25C6",
|
|
136
137
|
hollow: "\u25C7",
|
|
137
138
|
bar: "\u2502",
|
|
@@ -203,12 +204,16 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
203
204
|
const continuesFace = continuedPhases.size > 0;
|
|
204
205
|
const nested = env.MM_OUTER_CONSOLE === "1";
|
|
205
206
|
const progressFd = readProgressFd(env);
|
|
207
|
+
const progressFile = env.MM_PROGRESS_PROTOCOL === "1" ? env.MM_PROGRESS_FILE : void 0;
|
|
206
208
|
const emitMilestone = (title, measure, kind) => {
|
|
207
|
-
if (progressFd === null) return false;
|
|
209
|
+
if (progressFd === null && !progressFile) return false;
|
|
208
210
|
const record = { v: PROGRESS_PROTOCOL, step: stripColor(title), state: kind };
|
|
209
211
|
if (typeof measure === "number") record.ms = Math.max(0, Math.round(measure * 1e3));
|
|
212
|
+
if (kind === "running" && typeof measure === "string") record.measure = measure;
|
|
210
213
|
try {
|
|
211
|
-
(0, import_node_fs.
|
|
214
|
+
if (progressFile) (0, import_node_fs.appendFileSync)(progressFile, `${JSON.stringify(record)}
|
|
215
|
+
`);
|
|
216
|
+
else (0, import_node_fs.writeSync)(progressFd, `${JSON.stringify(record)}
|
|
212
217
|
`);
|
|
213
218
|
return true;
|
|
214
219
|
} catch {
|
|
@@ -227,7 +232,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
227
232
|
};
|
|
228
233
|
const step = (title, measure = null, kind = "ok") => {
|
|
229
234
|
if (emitMilestone(title, measure, kind)) return "";
|
|
230
|
-
const glyph = kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.dot) : paint(PALETTE.green, GLYPH.check);
|
|
235
|
+
const glyph = title === "Armed hourly updates" && kind === "ok" ? GLYPH.clock : kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.dot) : paint(PALETTE.green, GLYPH.check);
|
|
231
236
|
const measured = measure === null || measure === void 0 ? "" : paint(PALETTE.muted, typeof measure === "number" ? `${Math.max(0, Math.round(measure))}s` : String(measure).trim());
|
|
232
237
|
const time = measured;
|
|
233
238
|
const column = Math.min(44, Math.max(0, width - 8));
|
|
@@ -237,7 +242,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
237
242
|
const pad = Math.max(1, column - visibleWidth(head));
|
|
238
243
|
return [time ? `${head}${" ".repeat(pad)}${time}` : head, ...rest.map((line) => `${bar()}${indent}${line}`)].join("\n");
|
|
239
244
|
};
|
|
240
|
-
const relay = (text) => String(text).
|
|
245
|
+
const relay = (text) => String(text).replace(/\r?\n$/, "").split(/\r?\n/).flatMap((line) => line.trim() === "" ? [bar()] : (visibleWidth(line) <= width - TITLE_COLUMN ? [line] : wrapWords(line, width - TITLE_COLUMN)).map((part) => `${bar()}${indent}${part}`)).join("\n");
|
|
241
246
|
const receipt = (lines, { ready = true } = {}) => {
|
|
242
247
|
if (ready && nested) return [];
|
|
243
248
|
const body = (Array.isArray(lines) ? lines : String(lines).split("\n")).flatMap((raw) => {
|
|
@@ -268,7 +273,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
268
273
|
};
|
|
269
274
|
const signOff = () => nested ? "" : `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, `${identity.name} \xB7 Mutatis Mutandis`)}`;
|
|
270
275
|
const refusal = (message3) => `${bar()} ${paint(identity.accent, GLYPH.cross)} ${message3}`;
|
|
271
|
-
return { identity, width, nested, emitsProgress: progressFd !== null, welcome, continues, step, relay, receipt, outcome, signOff, refusal, paint };
|
|
276
|
+
return { identity, width, nested, emitsProgress: progressFd !== null || Boolean(progressFile), welcome, continues, step, progress: (title, measure = null) => emitMilestone(title, measure, "running"), relay, receipt, outcome, signOff, refusal, paint };
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
// ../installer/face/src/shell.ts
|
|
@@ -287,7 +292,7 @@ function draw() {
|
|
|
287
292
|
if (Atomics.compareExchange(control, 1, 0, 1) !== 0) return;
|
|
288
293
|
try {
|
|
289
294
|
if (Atomics.load(control, 0) || Atomics.load(control, 2) || !frames.length) return;
|
|
290
|
-
const text = frames[frame++ % frames.length];
|
|
295
|
+
const text = frames[frame++ % frames.length].replace('__elapsed__', Math.floor((Date.now() - workerData.started) / 1000) + 's');
|
|
291
296
|
writeSync(2, text);
|
|
292
297
|
if (workerData.transcriptPath) appendFileSync(workerData.transcriptPath, JSON.stringify({ channel: 'spinner', text }) + '\\n');
|
|
293
298
|
} finally {
|
|
@@ -299,7 +304,6 @@ parentPort.on('message', (next) => {
|
|
|
299
304
|
if (Atomics.load(control, 2)) return;
|
|
300
305
|
frames = next.frames;
|
|
301
306
|
frame = next.frame;
|
|
302
|
-
Atomics.store(control, 0, 0);
|
|
303
307
|
});
|
|
304
308
|
setInterval(draw, workerData.intervalMs);
|
|
305
309
|
`;
|
|
@@ -310,6 +314,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
310
314
|
let control = null;
|
|
311
315
|
let frames = [];
|
|
312
316
|
let frame = 0;
|
|
317
|
+
let started = 0;
|
|
318
|
+
let suspended = false;
|
|
313
319
|
const write = (text) => {
|
|
314
320
|
if (stream) stream.write(text);
|
|
315
321
|
else {
|
|
@@ -319,11 +325,11 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
319
325
|
}
|
|
320
326
|
};
|
|
321
327
|
const render = (text, measure) => {
|
|
322
|
-
const line = face.step(text, measure, "note").split("\n")[0];
|
|
328
|
+
const line = face.step(text, measure ?? "__elapsed__", "note").split("\n")[0];
|
|
323
329
|
frames = line ? FRAMES.map((glyph) => `\r\x1B[2K${line.replace(GLYPH.dot, glyph)}`) : [];
|
|
324
330
|
};
|
|
325
331
|
const draw = () => {
|
|
326
|
-
if (animate && frames.length) write(frames[frame++ % frames.length]);
|
|
332
|
+
if (animate && !suspended && frames.length) write(frames[frame++ % frames.length].replace("__elapsed__", `${Math.floor((Date.now() - started) / 1e3)}s`));
|
|
327
333
|
};
|
|
328
334
|
const pause = () => {
|
|
329
335
|
if (!control) return;
|
|
@@ -343,6 +349,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
343
349
|
start(text, measure = null) {
|
|
344
350
|
if (!animate) return;
|
|
345
351
|
halt();
|
|
352
|
+
suspended = false;
|
|
353
|
+
started = Date.now();
|
|
346
354
|
render(text, measure);
|
|
347
355
|
frame = 0;
|
|
348
356
|
draw();
|
|
@@ -356,7 +364,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
356
364
|
frames,
|
|
357
365
|
frame,
|
|
358
366
|
intervalMs,
|
|
359
|
-
transcriptPath
|
|
367
|
+
transcriptPath,
|
|
368
|
+
started
|
|
360
369
|
} });
|
|
361
370
|
const active = worker;
|
|
362
371
|
worker.on("error", () => {
|
|
@@ -374,9 +383,22 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
374
383
|
render(text, measure);
|
|
375
384
|
draw();
|
|
376
385
|
worker?.postMessage({ frames, frame });
|
|
386
|
+
if (control && !suspended) Atomics.store(control, 0, 0);
|
|
387
|
+
},
|
|
388
|
+
pause() {
|
|
389
|
+
if (suspended) return;
|
|
390
|
+
suspended = true;
|
|
391
|
+
pause();
|
|
392
|
+
if (animate && frames.length) write("\r\x1B[2K");
|
|
393
|
+
},
|
|
394
|
+
resume() {
|
|
395
|
+
suspended = false;
|
|
396
|
+
if (control) Atomics.store(control, 0, 0);
|
|
397
|
+
if (timer || worker) draw();
|
|
377
398
|
},
|
|
378
399
|
stop() {
|
|
379
400
|
halt();
|
|
401
|
+
frames = [];
|
|
380
402
|
if (animate) write("\r\x1B[2K");
|
|
381
403
|
}
|
|
382
404
|
};
|
|
@@ -607,6 +629,7 @@ var PHASES = {
|
|
|
607
629
|
"verify-release": ["Checking the release version", "Verified the release version"],
|
|
608
630
|
verify: ["Verifying the payload", "Verified the payload"],
|
|
609
631
|
install: ["Installing the product", "Installed the product"],
|
|
632
|
+
configure: ["Configuring the product", "Configured the product"],
|
|
610
633
|
activate: ["Activating surfaces", "Activated surfaces"],
|
|
611
634
|
doctor: ["Checking health", "Checked health"],
|
|
612
635
|
rollback: ["Restoring the previous version", "Restored the previous version"]
|
|
@@ -651,6 +674,17 @@ function createInstallerRun(value, options = {}) {
|
|
|
651
674
|
return true;
|
|
652
675
|
} } } : { transcriptPath: env.MM_FACE_TRANSCRIPT }
|
|
653
676
|
});
|
|
677
|
+
let activeTitle;
|
|
678
|
+
let chunkOpen = false;
|
|
679
|
+
let chunkChannel = "stdout";
|
|
680
|
+
let chunkColumn = 0;
|
|
681
|
+
const trailingCR = /* @__PURE__ */ new Set();
|
|
682
|
+
let deferredActivity;
|
|
683
|
+
const closeChunk = () => {
|
|
684
|
+
if (chunkOpen) emit("\n", chunkChannel);
|
|
685
|
+
chunkOpen = false;
|
|
686
|
+
chunkColumn = 0;
|
|
687
|
+
};
|
|
654
688
|
let started = false;
|
|
655
689
|
let finished = false;
|
|
656
690
|
const start = () => {
|
|
@@ -665,6 +699,9 @@ function createInstallerRun(value, options = {}) {
|
|
|
665
699
|
}
|
|
666
700
|
};
|
|
667
701
|
const durable = (title, measure, kind) => {
|
|
702
|
+
closeChunk();
|
|
703
|
+
activeTitle = void 0;
|
|
704
|
+
deferredActivity = void 0;
|
|
668
705
|
spinner.stop();
|
|
669
706
|
const rendered = face.step(title, measure, kind);
|
|
670
707
|
if (rendered) lines([tty ? rendered : `${kind === "fail" ? "Failed: " : ""}${title}${measure === null ? "" : ` (${typeof measure === "number" ? `${Math.max(0, Math.round(measure))}s` : measure})`}`]);
|
|
@@ -681,7 +718,12 @@ function createInstallerRun(value, options = {}) {
|
|
|
681
718
|
const state = facts.state ?? "ok";
|
|
682
719
|
const title = PHASES[id][state === "ok" ? 1 : 0];
|
|
683
720
|
if (state === "running") {
|
|
684
|
-
|
|
721
|
+
if (face.progress(title, facts.measure ?? null)) return;
|
|
722
|
+
if (activeTitle === title) spinner.say(title, facts.measure ?? null);
|
|
723
|
+
else {
|
|
724
|
+
activeTitle = title;
|
|
725
|
+
spinner.start(title, facts.measure ?? null);
|
|
726
|
+
}
|
|
685
727
|
return;
|
|
686
728
|
}
|
|
687
729
|
if (!face.continues(id, state)) durable(title, facts.seconds ?? facts.measure ?? null, state);
|
|
@@ -701,8 +743,20 @@ function createInstallerRun(value, options = {}) {
|
|
|
701
743
|
durable(`${facts.id}${versions} ${separator} ${status}${activation}`, completed ? facts.seconds ?? null : null, facts.state === "failed" ? "fail" : facts.state === "updated" ? "ok" : "note");
|
|
702
744
|
if (facts.detail) run.relay(facts.detail);
|
|
703
745
|
},
|
|
704
|
-
milestone({ step, state, ms }) {
|
|
746
|
+
milestone({ step, state, ms, measure }) {
|
|
705
747
|
start();
|
|
748
|
+
if (state === "running") {
|
|
749
|
+
if (chunkOpen) {
|
|
750
|
+
deferredActivity = { title: step, measure: measure ?? null };
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (activeTitle === step) spinner.say(step, measure ?? null);
|
|
754
|
+
else {
|
|
755
|
+
activeTitle = step;
|
|
756
|
+
spinner.start(step, measure ?? null);
|
|
757
|
+
}
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
706
760
|
durable(step, ms === void 0 ? null : ms / 1e3, state);
|
|
707
761
|
},
|
|
708
762
|
signIn({ url, code }) {
|
|
@@ -716,7 +770,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
716
770
|
},
|
|
717
771
|
// Only pass safe diagnostic text, never authentication output or credentials.
|
|
718
772
|
relay(text, channel = "stdout", record = true) {
|
|
719
|
-
|
|
773
|
+
closeChunk();
|
|
774
|
+
spinner.pause();
|
|
720
775
|
const rendered = tty ? face.relay(text) : stripColor(text);
|
|
721
776
|
for (const row of rendered.split("\n")) if (row) {
|
|
722
777
|
emit(`${row}
|
|
@@ -724,6 +779,37 @@ function createInstallerRun(value, options = {}) {
|
|
|
724
779
|
` : `${tty ? face.relay("[external output omitted]") : "[external output omitted]"}
|
|
725
780
|
`);
|
|
726
781
|
}
|
|
782
|
+
spinner.resume();
|
|
783
|
+
},
|
|
784
|
+
relayChunk(text, channel = "stdout") {
|
|
785
|
+
spinner.pause();
|
|
786
|
+
if (chunkOpen && chunkChannel !== channel) closeChunk();
|
|
787
|
+
chunkChannel = channel;
|
|
788
|
+
if (trailingCR.delete(channel) && text.startsWith("\n")) text = text.slice(1);
|
|
789
|
+
if (text.endsWith("\r")) trailingCR.add(channel);
|
|
790
|
+
const parts = text.replace(/\r\n|\r/g, "\n").split("\n");
|
|
791
|
+
for (let i = 0; i < parts.length; i++) {
|
|
792
|
+
const characters = [...stripColor(parts[i])];
|
|
793
|
+
while (characters.length) {
|
|
794
|
+
if (tty && chunkColumn >= face.width - 6) closeChunk();
|
|
795
|
+
const part = characters.splice(0, tty ? face.width - 6 - chunkColumn : characters.length).join("");
|
|
796
|
+
emit(tty && !chunkOpen ? face.relay(part) : part, channel, chunkOpen ? "" : tty ? face.relay("[external output omitted]") : "[external output omitted]");
|
|
797
|
+
chunkColumn += [...part].length;
|
|
798
|
+
chunkOpen = true;
|
|
799
|
+
}
|
|
800
|
+
if (i < parts.length - 1) {
|
|
801
|
+
emit("\n", channel);
|
|
802
|
+
chunkOpen = false;
|
|
803
|
+
chunkColumn = 0;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (!chunkOpen) {
|
|
807
|
+
if (deferredActivity) {
|
|
808
|
+
activeTitle = deferredActivity.title;
|
|
809
|
+
spinner.start(deferredActivity.title, deferredActivity.measure);
|
|
810
|
+
deferredActivity = void 0;
|
|
811
|
+
} else spinner.resume();
|
|
812
|
+
}
|
|
727
813
|
},
|
|
728
814
|
cancel() {
|
|
729
815
|
if (finished) return;
|
|
@@ -732,6 +818,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
732
818
|
{ total: 0, updated: 0, failed: 0, deferred: true, detail: "Operation cancelled." }
|
|
733
819
|
);
|
|
734
820
|
start();
|
|
821
|
+
closeChunk();
|
|
822
|
+
activeTitle = void 0;
|
|
735
823
|
spinner.stop();
|
|
736
824
|
finished = true;
|
|
737
825
|
if (!face.nested || !env.MM_INSTALLER_OUTCOME_FILE) {
|
|
@@ -744,6 +832,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
744
832
|
validateInstallerOutcome(facts);
|
|
745
833
|
if (env.MM_INSTALLER_OUTCOME_FILE) writeInstallerOutcome(env.MM_INSTALLER_OUTCOME_FILE, facts);
|
|
746
834
|
start();
|
|
835
|
+
closeChunk();
|
|
836
|
+
activeTitle = void 0;
|
|
747
837
|
spinner.stop();
|
|
748
838
|
finished = true;
|
|
749
839
|
if (options.quiet && facts.updated === 0 && facts.failed === 0 && !facts.operationFailed) return;
|
|
@@ -764,6 +854,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
764
854
|
if (tty) lines([face.signOff()]);
|
|
765
855
|
},
|
|
766
856
|
stop() {
|
|
857
|
+
closeChunk();
|
|
858
|
+
activeTitle = void 0;
|
|
767
859
|
spinner.stop();
|
|
768
860
|
}
|
|
769
861
|
};
|
package/package.json
CHANGED