@mutmutco/hub 4.4.11 → 4.4.13
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 +171 -27
- 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
|
|
@@ -277,7 +282,26 @@ var RELAY_INDENT = " ".repeat(TITLE_COLUMN - 1);
|
|
|
277
282
|
// ../installer/face/src/spinner.ts
|
|
278
283
|
var import_node_fs2 = require("node:fs");
|
|
279
284
|
var import_node_worker_threads = require("node:worker_threads");
|
|
280
|
-
|
|
285
|
+
|
|
286
|
+
// ../installer/face/src/encoding.ts
|
|
287
|
+
var UNICODE_SPINNER_FRAMES = Object.freeze(["\u25D2", "\u25D0", "\u25D3", "\u25D1"]);
|
|
288
|
+
var ASCII_SPINNER_FRAMES = Object.freeze(["-", "\\", "|", "/"]);
|
|
289
|
+
function consoleRendersUnicode(env = process.env, platform = process.platform) {
|
|
290
|
+
if (env.MM_FACE_ASCII === "1" || env.NO_UNICODE === "1") return false;
|
|
291
|
+
const locale = env.LC_ALL || env.LC_CTYPE || env.LANG || "";
|
|
292
|
+
if (locale === "C" || locale === "POSIX" || /^C\./u.test(locale) || /^POSIX\./u.test(locale)) return false;
|
|
293
|
+
if (/cp437|ibm437|\b437\b/iu.test(locale)) return false;
|
|
294
|
+
if (/utf-?8/iu.test(locale)) return true;
|
|
295
|
+
if (platform === "win32") {
|
|
296
|
+
return Boolean(env.WT_SESSION) || env.TERM_PROGRAM === "vscode" || env.CHCP === "65001" || env.ConEmuANSI === "ON";
|
|
297
|
+
}
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
function spinnerFramesFor(env = process.env, platform = process.platform) {
|
|
301
|
+
return consoleRendersUnicode(env, platform) ? UNICODE_SPINNER_FRAMES : ASCII_SPINNER_FRAMES;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ../installer/face/src/spinner.ts
|
|
281
305
|
var WORKER_SOURCE = `
|
|
282
306
|
const { parentPort, workerData } = require('node:worker_threads');
|
|
283
307
|
const { writeSync, appendFileSync } = require('node:fs');
|
|
@@ -287,7 +311,7 @@ function draw() {
|
|
|
287
311
|
if (Atomics.compareExchange(control, 1, 0, 1) !== 0) return;
|
|
288
312
|
try {
|
|
289
313
|
if (Atomics.load(control, 0) || Atomics.load(control, 2) || !frames.length) return;
|
|
290
|
-
const text = frames[frame++ % frames.length];
|
|
314
|
+
const text = frames[frame++ % frames.length].replace('__elapsed__', Math.floor((Date.now() - workerData.started) / 1000) + 's');
|
|
291
315
|
writeSync(2, text);
|
|
292
316
|
if (workerData.transcriptPath) appendFileSync(workerData.transcriptPath, JSON.stringify({ channel: 'spinner', text }) + '\\n');
|
|
293
317
|
} finally {
|
|
@@ -299,17 +323,20 @@ parentPort.on('message', (next) => {
|
|
|
299
323
|
if (Atomics.load(control, 2)) return;
|
|
300
324
|
frames = next.frames;
|
|
301
325
|
frame = next.frame;
|
|
302
|
-
Atomics.store(control, 0, 0);
|
|
303
326
|
});
|
|
304
327
|
setInterval(draw, workerData.intervalMs);
|
|
305
328
|
`;
|
|
306
|
-
function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath }) {
|
|
329
|
+
function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath, env = process.env, platform = process.platform }) {
|
|
307
330
|
animate = animate && !face.nested && !face.emitsProgress;
|
|
331
|
+
const glyphs = spinnerFramesFor(env, platform);
|
|
332
|
+
const ascii = glyphs[0] === ASCII_SPINNER_FRAMES[0];
|
|
308
333
|
let timer = null;
|
|
309
334
|
let worker = null;
|
|
310
335
|
let control = null;
|
|
311
336
|
let frames = [];
|
|
312
337
|
let frame = 0;
|
|
338
|
+
let started = 0;
|
|
339
|
+
let suspended = false;
|
|
313
340
|
const write = (text) => {
|
|
314
341
|
if (stream) stream.write(text);
|
|
315
342
|
else {
|
|
@@ -319,11 +346,12 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
319
346
|
}
|
|
320
347
|
};
|
|
321
348
|
const render = (text, measure) => {
|
|
322
|
-
const line = face.step(text, measure, "note").split("\n")[0];
|
|
323
|
-
|
|
349
|
+
const line = face.step(text, measure ?? "__elapsed__", "note").split("\n")[0];
|
|
350
|
+
const prepared = line && ascii ? line.replaceAll(GLYPH.bar, "|") : line;
|
|
351
|
+
frames = prepared ? glyphs.map((glyph) => `\r\x1B[2K${prepared.replace(GLYPH.dot, glyph)}`) : [];
|
|
324
352
|
};
|
|
325
353
|
const draw = () => {
|
|
326
|
-
if (animate && frames.length) write(frames[frame++ % frames.length]);
|
|
354
|
+
if (animate && !suspended && frames.length) write(frames[frame++ % frames.length].replace("__elapsed__", `${Math.floor((Date.now() - started) / 1e3)}s`));
|
|
327
355
|
};
|
|
328
356
|
const pause = () => {
|
|
329
357
|
if (!control) return;
|
|
@@ -343,6 +371,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
343
371
|
start(text, measure = null) {
|
|
344
372
|
if (!animate) return;
|
|
345
373
|
halt();
|
|
374
|
+
suspended = false;
|
|
375
|
+
started = Date.now();
|
|
346
376
|
render(text, measure);
|
|
347
377
|
frame = 0;
|
|
348
378
|
draw();
|
|
@@ -356,7 +386,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
356
386
|
frames,
|
|
357
387
|
frame,
|
|
358
388
|
intervalMs,
|
|
359
|
-
transcriptPath
|
|
389
|
+
transcriptPath,
|
|
390
|
+
started
|
|
360
391
|
} });
|
|
361
392
|
const active = worker;
|
|
362
393
|
worker.on("error", () => {
|
|
@@ -374,14 +405,26 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
374
405
|
render(text, measure);
|
|
375
406
|
draw();
|
|
376
407
|
worker?.postMessage({ frames, frame });
|
|
408
|
+
if (control && !suspended) Atomics.store(control, 0, 0);
|
|
409
|
+
},
|
|
410
|
+
pause() {
|
|
411
|
+
if (suspended) return;
|
|
412
|
+
suspended = true;
|
|
413
|
+
pause();
|
|
414
|
+
if (animate && frames.length) write("\r\x1B[2K");
|
|
415
|
+
},
|
|
416
|
+
resume() {
|
|
417
|
+
suspended = false;
|
|
418
|
+
if (control) Atomics.store(control, 0, 0);
|
|
419
|
+
if (timer || worker) draw();
|
|
377
420
|
},
|
|
378
421
|
stop() {
|
|
379
422
|
halt();
|
|
423
|
+
frames = [];
|
|
380
424
|
if (animate) write("\r\x1B[2K");
|
|
381
425
|
}
|
|
382
426
|
};
|
|
383
427
|
}
|
|
384
|
-
var SPINNER_FRAMES = Object.freeze([...FRAMES]);
|
|
385
428
|
|
|
386
429
|
// ../installer/face/src/conformance.ts
|
|
387
430
|
var ALLOWED = new Set(Object.values(GLYPH));
|
|
@@ -607,10 +650,19 @@ var PHASES = {
|
|
|
607
650
|
"verify-release": ["Checking the release version", "Verified the release version"],
|
|
608
651
|
verify: ["Verifying the payload", "Verified the payload"],
|
|
609
652
|
install: ["Installing the product", "Installed the product"],
|
|
653
|
+
configure: ["Configuring the product", "Configured the product"],
|
|
610
654
|
activate: ["Activating surfaces", "Activated surfaces"],
|
|
611
655
|
doctor: ["Checking health", "Checked health"],
|
|
612
656
|
rollback: ["Restoring the previous version", "Restored the previous version"]
|
|
613
657
|
};
|
|
658
|
+
var UPDATE_CHECK_PHASES = /* @__PURE__ */ new Set([
|
|
659
|
+
"preflight",
|
|
660
|
+
"resolve",
|
|
661
|
+
"verify-release",
|
|
662
|
+
"download",
|
|
663
|
+
"sign-in",
|
|
664
|
+
"check"
|
|
665
|
+
]);
|
|
614
666
|
function createInstallerRun(value, options = {}) {
|
|
615
667
|
const declaration = validateInstallerProduct(value);
|
|
616
668
|
const env = options.env ?? process.env;
|
|
@@ -646,13 +698,26 @@ function createInstallerRun(value, options = {}) {
|
|
|
646
698
|
};
|
|
647
699
|
const spinner = createSpinner(face, {
|
|
648
700
|
animate: tty && env.TERM !== "dumb" && (options.animate ?? Boolean(process.stderr.isTTY)),
|
|
701
|
+
env,
|
|
649
702
|
...options.write ? { stream: { write: (text) => {
|
|
650
703
|
emit(String(text), "spinner");
|
|
651
704
|
return true;
|
|
652
705
|
} } } : { transcriptPath: env.MM_FACE_TRANSCRIPT }
|
|
653
706
|
});
|
|
707
|
+
let activeTitle;
|
|
708
|
+
let chunkOpen = false;
|
|
709
|
+
let chunkChannel = "stdout";
|
|
710
|
+
let chunkColumn = 0;
|
|
711
|
+
const trailingCR = /* @__PURE__ */ new Set();
|
|
712
|
+
let deferredActivity;
|
|
713
|
+
const closeChunk = () => {
|
|
714
|
+
if (chunkOpen) emit("\n", chunkChannel);
|
|
715
|
+
chunkOpen = false;
|
|
716
|
+
chunkColumn = 0;
|
|
717
|
+
};
|
|
654
718
|
let started = false;
|
|
655
719
|
let finished = false;
|
|
720
|
+
let operation = options.operation;
|
|
656
721
|
const start = () => {
|
|
657
722
|
if (started || finished) return;
|
|
658
723
|
started = true;
|
|
@@ -660,11 +725,14 @@ function createInstallerRun(value, options = {}) {
|
|
|
660
725
|
const welcome = face.welcome();
|
|
661
726
|
if (tty) lines(welcome);
|
|
662
727
|
else if (welcome.length) {
|
|
663
|
-
const warm =
|
|
728
|
+
const warm = operation === "install" ? face.identity.installWarm : face.identity.warm;
|
|
664
729
|
lines([`${face.identity.name} - Mutatis Mutandis`, warm.replaceAll("\u2014", "-").replaceAll("\u2026", "...")]);
|
|
665
730
|
}
|
|
666
731
|
};
|
|
667
732
|
const durable = (title, measure, kind) => {
|
|
733
|
+
closeChunk();
|
|
734
|
+
activeTitle = void 0;
|
|
735
|
+
deferredActivity = void 0;
|
|
668
736
|
spinner.stop();
|
|
669
737
|
const rendered = face.step(title, measure, kind);
|
|
670
738
|
if (rendered) lines([tty ? rendered : `${kind === "fail" ? "Failed: " : ""}${title}${measure === null ? "" : ` (${typeof measure === "number" ? `${Math.max(0, Math.round(measure))}s` : measure})`}`]);
|
|
@@ -673,15 +741,31 @@ function createInstallerRun(value, options = {}) {
|
|
|
673
741
|
get errors() {
|
|
674
742
|
return [...errors];
|
|
675
743
|
},
|
|
744
|
+
get operation() {
|
|
745
|
+
return operation;
|
|
746
|
+
},
|
|
747
|
+
set operation(value2) {
|
|
748
|
+
operation = value2;
|
|
749
|
+
},
|
|
676
750
|
start,
|
|
677
751
|
phase(id, facts = {}) {
|
|
678
752
|
if (finished) throw new Error("installer run already finished");
|
|
679
753
|
if (!Object.hasOwn(PHASES, id)) throw new Error("installer run: unknown phase");
|
|
680
754
|
start();
|
|
681
755
|
const state = facts.state ?? "ok";
|
|
682
|
-
const
|
|
756
|
+
const fold = operation === "update" && UPDATE_CHECK_PHASES.has(id) && state !== "fail";
|
|
757
|
+
const title = PHASES[fold ? "check" : id][state === "ok" ? 1 : 0];
|
|
683
758
|
if (state === "running") {
|
|
684
|
-
|
|
759
|
+
if (face.progress(title, facts.measure ?? null)) return;
|
|
760
|
+
if (activeTitle === title) spinner.say(title, facts.measure ?? null);
|
|
761
|
+
else {
|
|
762
|
+
activeTitle = title;
|
|
763
|
+
spinner.start(title, facts.measure ?? null);
|
|
764
|
+
}
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (fold && id !== "check") {
|
|
768
|
+
if (facts.detail) run.relay(facts.detail);
|
|
685
769
|
return;
|
|
686
770
|
}
|
|
687
771
|
if (!face.continues(id, state)) durable(title, facts.seconds ?? facts.measure ?? null, state);
|
|
@@ -701,8 +785,20 @@ function createInstallerRun(value, options = {}) {
|
|
|
701
785
|
durable(`${facts.id}${versions} ${separator} ${status}${activation}`, completed ? facts.seconds ?? null : null, facts.state === "failed" ? "fail" : facts.state === "updated" ? "ok" : "note");
|
|
702
786
|
if (facts.detail) run.relay(facts.detail);
|
|
703
787
|
},
|
|
704
|
-
milestone({ step, state, ms }) {
|
|
788
|
+
milestone({ step, state, ms, measure }) {
|
|
705
789
|
start();
|
|
790
|
+
if (state === "running") {
|
|
791
|
+
if (chunkOpen) {
|
|
792
|
+
deferredActivity = { title: step, measure: measure ?? null };
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (activeTitle === step) spinner.say(step, measure ?? null);
|
|
796
|
+
else {
|
|
797
|
+
activeTitle = step;
|
|
798
|
+
spinner.start(step, measure ?? null);
|
|
799
|
+
}
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
706
802
|
durable(step, ms === void 0 ? null : ms / 1e3, state);
|
|
707
803
|
},
|
|
708
804
|
signIn({ url, code }) {
|
|
@@ -716,7 +812,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
716
812
|
},
|
|
717
813
|
// Only pass safe diagnostic text, never authentication output or credentials.
|
|
718
814
|
relay(text, channel = "stdout", record = true) {
|
|
719
|
-
|
|
815
|
+
closeChunk();
|
|
816
|
+
spinner.pause();
|
|
720
817
|
const rendered = tty ? face.relay(text) : stripColor(text);
|
|
721
818
|
for (const row of rendered.split("\n")) if (row) {
|
|
722
819
|
emit(`${row}
|
|
@@ -724,6 +821,37 @@ function createInstallerRun(value, options = {}) {
|
|
|
724
821
|
` : `${tty ? face.relay("[external output omitted]") : "[external output omitted]"}
|
|
725
822
|
`);
|
|
726
823
|
}
|
|
824
|
+
spinner.resume();
|
|
825
|
+
},
|
|
826
|
+
relayChunk(text, channel = "stdout") {
|
|
827
|
+
spinner.pause();
|
|
828
|
+
if (chunkOpen && chunkChannel !== channel) closeChunk();
|
|
829
|
+
chunkChannel = channel;
|
|
830
|
+
if (trailingCR.delete(channel) && text.startsWith("\n")) text = text.slice(1);
|
|
831
|
+
if (text.endsWith("\r")) trailingCR.add(channel);
|
|
832
|
+
const parts = text.replace(/\r\n|\r/g, "\n").split("\n");
|
|
833
|
+
for (let i = 0; i < parts.length; i++) {
|
|
834
|
+
const characters = [...stripColor(parts[i])];
|
|
835
|
+
while (characters.length) {
|
|
836
|
+
if (tty && chunkColumn >= face.width - 6) closeChunk();
|
|
837
|
+
const part = characters.splice(0, tty ? face.width - 6 - chunkColumn : characters.length).join("");
|
|
838
|
+
emit(tty && !chunkOpen ? face.relay(part) : part, channel, chunkOpen ? "" : tty ? face.relay("[external output omitted]") : "[external output omitted]");
|
|
839
|
+
chunkColumn += [...part].length;
|
|
840
|
+
chunkOpen = true;
|
|
841
|
+
}
|
|
842
|
+
if (i < parts.length - 1) {
|
|
843
|
+
emit("\n", channel);
|
|
844
|
+
chunkOpen = false;
|
|
845
|
+
chunkColumn = 0;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (!chunkOpen) {
|
|
849
|
+
if (deferredActivity) {
|
|
850
|
+
activeTitle = deferredActivity.title;
|
|
851
|
+
spinner.start(deferredActivity.title, deferredActivity.measure);
|
|
852
|
+
deferredActivity = void 0;
|
|
853
|
+
} else spinner.resume();
|
|
854
|
+
}
|
|
727
855
|
},
|
|
728
856
|
cancel() {
|
|
729
857
|
if (finished) return;
|
|
@@ -732,6 +860,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
732
860
|
{ total: 0, updated: 0, failed: 0, deferred: true, detail: "Operation cancelled." }
|
|
733
861
|
);
|
|
734
862
|
start();
|
|
863
|
+
closeChunk();
|
|
864
|
+
activeTitle = void 0;
|
|
735
865
|
spinner.stop();
|
|
736
866
|
finished = true;
|
|
737
867
|
if (!face.nested || !env.MM_INSTALLER_OUTCOME_FILE) {
|
|
@@ -744,6 +874,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
744
874
|
validateInstallerOutcome(facts);
|
|
745
875
|
if (env.MM_INSTALLER_OUTCOME_FILE) writeInstallerOutcome(env.MM_INSTALLER_OUTCOME_FILE, facts);
|
|
746
876
|
start();
|
|
877
|
+
closeChunk();
|
|
878
|
+
activeTitle = void 0;
|
|
747
879
|
spinner.stop();
|
|
748
880
|
finished = true;
|
|
749
881
|
if (options.quiet && facts.updated === 0 && facts.failed === 0 && !facts.operationFailed) return;
|
|
@@ -764,6 +896,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
764
896
|
if (tty) lines([face.signOff()]);
|
|
765
897
|
},
|
|
766
898
|
stop() {
|
|
899
|
+
closeChunk();
|
|
900
|
+
activeTitle = void 0;
|
|
767
901
|
spinner.stop();
|
|
768
902
|
}
|
|
769
903
|
};
|
|
@@ -789,6 +923,7 @@ async function runMaintenance(product, args, dependencies) {
|
|
|
789
923
|
quiet,
|
|
790
924
|
operation: installed ? "install" : "update"
|
|
791
925
|
});
|
|
926
|
+
if (run) run.operation = installed ? "install" : "update";
|
|
792
927
|
if (!quiet) run?.start();
|
|
793
928
|
let active;
|
|
794
929
|
let activeFacts;
|
|
@@ -3026,6 +3161,9 @@ async function reconcile(options) {
|
|
|
3026
3161
|
options.narrate?.(line);
|
|
3027
3162
|
options.onPhase?.(line);
|
|
3028
3163
|
};
|
|
3164
|
+
const phase = (id, state, detail) => {
|
|
3165
|
+
options.onPhaseEvent?.({ id, state, ...detail ? { detail } : {} });
|
|
3166
|
+
};
|
|
3029
3167
|
const run = `${(/* @__PURE__ */ new Date()).toISOString()}-${process.pid}`;
|
|
3030
3168
|
const paths = ensureState(env);
|
|
3031
3169
|
const repoUrl = env.MMI_UPDATER_REPO || REPO_URL_DEFAULT;
|
|
@@ -3047,6 +3185,7 @@ async function reconcile(options) {
|
|
|
3047
3185
|
say("acquiring single-writer lease");
|
|
3048
3186
|
const leaseWaitMs = Number(env.MMI_UPDATER_LEASE_WAIT_MS) || void 0;
|
|
3049
3187
|
return await withLease(paths.leasePath, async () => {
|
|
3188
|
+
phase("preflight", "running");
|
|
3050
3189
|
const journalEvents = parseJournal(paths.journalPath);
|
|
3051
3190
|
if (!options.dryRun) {
|
|
3052
3191
|
const pendingBefore = readPending(paths);
|
|
@@ -3058,6 +3197,8 @@ async function reconcile(options) {
|
|
|
3058
3197
|
summary.reap = reap({ paths, dryRun: options.dryRun, env });
|
|
3059
3198
|
for (const row of summary.reap) journal({ kind: "reap", surface: row.category, verdict: row.verdict, count: row.reaped, detail: row.detail, durationMs: row.durationMs });
|
|
3060
3199
|
for (const row of summary.reap.filter((entry) => entry.verdict !== "skip")) say(`reap ${row.category}: ${row.verdict}${row.detail ? " \u2014 " + row.detail : ""}`);
|
|
3200
|
+
phase("preflight", "ok");
|
|
3201
|
+
phase("resolve", "running");
|
|
3061
3202
|
if (shipped) {
|
|
3062
3203
|
say(`shipped target v${shipped} \u2014 the payload is the release; no npm poll, no gate`);
|
|
3063
3204
|
summary.target = shipped;
|
|
@@ -3116,7 +3257,9 @@ async function reconcile(options) {
|
|
|
3116
3257
|
}
|
|
3117
3258
|
}
|
|
3118
3259
|
say(`target v${summary.target} (${summary.gate})`);
|
|
3260
|
+
phase("resolve", "ok");
|
|
3119
3261
|
options.onTargetResolved?.(summary.target, summary.gate);
|
|
3262
|
+
phase("check", "running");
|
|
3120
3263
|
say("updater: self-bootstrap");
|
|
3121
3264
|
const selfStartedAt = import_node_perf_hooks2.performance.now();
|
|
3122
3265
|
const self = npmGlobalArm({
|
|
@@ -3179,6 +3322,7 @@ async function reconcile(options) {
|
|
|
3179
3322
|
journal({ kind: "surface", surface: probe.id, present: true, verdict: "skip", detail: `present, its converge arm has not landed yet (target ${summary.target})` });
|
|
3180
3323
|
}
|
|
3181
3324
|
}
|
|
3325
|
+
phase("check", "ok");
|
|
3182
3326
|
return summary;
|
|
3183
3327
|
}, leaseWaitMs);
|
|
3184
3328
|
} catch (error) {
|
package/package.json
CHANGED