@mutmutco/installer-face 0.4.4 → 0.4.5
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/CHANGELOG.md +7 -0
- package/dist/face.d.ts +2 -0
- package/dist/index.js +118 -26
- package/dist/install.html.template +261 -0
- package/dist/install.ps1 +250 -0
- package/dist/install.sh +157 -0
- package/dist/run.d.ts +5 -2
- package/dist/spinner.d.ts +2 -0
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# 0.4.5
|
|
2
|
+
|
|
3
|
+
- Keep one compact terminal stream and resume live progress after child output.
|
|
4
|
+
- Carry nested running phases through Windows command wrappers.
|
|
5
|
+
- Use the same install/update wording and hourly-update clock across products.
|
|
6
|
+
- Ship the shared download page and bootstrap templates for both public homes.
|
|
7
|
+
|
|
1
8
|
# 0.4.4
|
|
2
9
|
|
|
3
10
|
- Report engine-measured surface times without including release discovery or callback delays.
|
package/dist/face.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ProductIdentity } from './products.js';
|
|
2
2
|
/** The guidebook glyph set. Nothing outside this may appear in a face. */
|
|
3
3
|
export declare const GLYPH: Readonly<{
|
|
4
|
+
clock: "⏰";
|
|
4
5
|
diamond: "◆";
|
|
5
6
|
hollow: "◇";
|
|
6
7
|
bar: "│";
|
|
@@ -70,6 +71,7 @@ export interface Face {
|
|
|
70
71
|
continues(phase: string, kind?: StepKind): boolean;
|
|
71
72
|
/** One durable line for one finished step: glyph, title, and the right-column measure. */
|
|
72
73
|
step(title: string, measure?: StepMeasure, kind?: StepKind): string;
|
|
74
|
+
progress(title: string, measure?: StepMeasure): boolean;
|
|
73
75
|
/**
|
|
74
76
|
* The canonical success receipt, composed from the product table: `✔ <name> is ready.`, the one
|
|
75
77
|
* fact the surface alone knows, and the table's own health command. A surface supplies ONLY what
|
package/dist/index.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
// src/face.ts
|
|
2
|
-
import { writeSync } from "node:fs";
|
|
2
|
+
import { appendFileSync, writeSync } from "node:fs";
|
|
3
3
|
|
|
4
4
|
// src/products.ts
|
|
5
5
|
var PRODUCTS = Object.freeze({
|
|
6
6
|
"mm-strategy": Object.freeze({
|
|
7
7
|
name: "MM Strategy",
|
|
8
|
-
installWarm: "Welcome. Let's
|
|
8
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
9
9
|
accent: "38;2;249;115;22",
|
|
10
|
-
warm: "Welcome.
|
|
10
|
+
warm: "Welcome back. Checking for updates...",
|
|
11
11
|
doctor: "mm-strategy doctor"
|
|
12
12
|
}),
|
|
13
13
|
"mmi-hub": Object.freeze({
|
|
14
14
|
name: "mmi-hub",
|
|
15
|
-
installWarm: "Welcome.
|
|
15
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
16
16
|
accent: "38;2;125;211;252",
|
|
17
|
-
warm: "Welcome back. Checking
|
|
17
|
+
warm: "Welcome back. Checking for updates...",
|
|
18
18
|
doctor: "mmi doctor"
|
|
19
19
|
}),
|
|
20
20
|
"jerv-hub": Object.freeze({
|
|
21
21
|
name: "jerv-hub",
|
|
22
|
-
installWarm: "Welcome.
|
|
22
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
23
23
|
accent: "38;2;248;113;113",
|
|
24
|
-
warm: "Welcome back. Checking
|
|
24
|
+
warm: "Welcome back. Checking for updates...",
|
|
25
25
|
doctor: "jerv doctor"
|
|
26
26
|
}),
|
|
27
27
|
jervcode: Object.freeze({
|
|
28
28
|
name: "JervCode",
|
|
29
|
-
installWarm: "Welcome.
|
|
29
|
+
installWarm: "Welcome. Let's get you ready.",
|
|
30
30
|
accent: "38;2;192;132;252",
|
|
31
|
-
warm: "Welcome back.
|
|
31
|
+
warm: "Welcome back. Checking for updates...",
|
|
32
32
|
doctor: "jervcode doctor"
|
|
33
33
|
})
|
|
34
34
|
});
|
|
@@ -42,6 +42,7 @@ function identityFor(product) {
|
|
|
42
42
|
|
|
43
43
|
// src/face.ts
|
|
44
44
|
var GLYPH = Object.freeze({
|
|
45
|
+
clock: "\u23F0",
|
|
45
46
|
diamond: "\u25C6",
|
|
46
47
|
hollow: "\u25C7",
|
|
47
48
|
bar: "\u2502",
|
|
@@ -113,12 +114,16 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
113
114
|
const continuesFace = continuedPhases.size > 0;
|
|
114
115
|
const nested = env.MM_OUTER_CONSOLE === "1";
|
|
115
116
|
const progressFd = readProgressFd(env);
|
|
117
|
+
const progressFile = env.MM_PROGRESS_PROTOCOL === "1" ? env.MM_PROGRESS_FILE : void 0;
|
|
116
118
|
const emitMilestone = (title, measure, kind) => {
|
|
117
|
-
if (progressFd === null) return false;
|
|
119
|
+
if (progressFd === null && !progressFile) return false;
|
|
118
120
|
const record = { v: PROGRESS_PROTOCOL, step: stripColor(title), state: kind };
|
|
119
121
|
if (typeof measure === "number") record.ms = Math.max(0, Math.round(measure * 1e3));
|
|
122
|
+
if (kind === "running" && typeof measure === "string") record.measure = measure;
|
|
120
123
|
try {
|
|
121
|
-
|
|
124
|
+
if (progressFile) appendFileSync(progressFile, `${JSON.stringify(record)}
|
|
125
|
+
`);
|
|
126
|
+
else writeSync(progressFd, `${JSON.stringify(record)}
|
|
122
127
|
`);
|
|
123
128
|
return true;
|
|
124
129
|
} catch {
|
|
@@ -137,7 +142,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
137
142
|
};
|
|
138
143
|
const step = (title, measure = null, kind = "ok") => {
|
|
139
144
|
if (emitMilestone(title, measure, kind)) return "";
|
|
140
|
-
const glyph = kind === "fail" ? paint(identity.accent, GLYPH.cross) : kind === "note" ? paint(PALETTE.muted, GLYPH.dot) : paint(PALETTE.green, GLYPH.check);
|
|
145
|
+
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);
|
|
141
146
|
const measured = measure === null || measure === void 0 ? "" : paint(PALETTE.muted, typeof measure === "number" ? `${Math.max(0, Math.round(measure))}s` : String(measure).trim());
|
|
142
147
|
const time = measured;
|
|
143
148
|
const column = Math.min(44, Math.max(0, width - 8));
|
|
@@ -147,7 +152,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
147
152
|
const pad = Math.max(1, column - visibleWidth(head));
|
|
148
153
|
return [time ? `${head}${" ".repeat(pad)}${time}` : head, ...rest.map((line) => `${bar()}${indent}${line}`)].join("\n");
|
|
149
154
|
};
|
|
150
|
-
const relay = (text) => String(text).
|
|
155
|
+
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");
|
|
151
156
|
const receipt = (lines, { ready = true } = {}) => {
|
|
152
157
|
if (ready && nested) return [];
|
|
153
158
|
const body = (Array.isArray(lines) ? lines : String(lines).split("\n")).flatMap((raw) => {
|
|
@@ -178,7 +183,7 @@ function createFace({ product, color = false, columns, env = process.env, operat
|
|
|
178
183
|
};
|
|
179
184
|
const signOff = () => nested ? "" : `${paint(identity.accent, GLYPH.diamond)} ${paint(identity.accent, `${identity.name} \xB7 Mutatis Mutandis`)}`;
|
|
180
185
|
const refusal = (message) => `${bar()} ${paint(identity.accent, GLYPH.cross)} ${message}`;
|
|
181
|
-
return { identity, width, nested, emitsProgress: progressFd !== null, welcome, continues, step, relay, receipt, outcome, signOff, refusal, paint };
|
|
186
|
+
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 };
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
// src/shell.ts
|
|
@@ -510,7 +515,7 @@ function clip(text, width) {
|
|
|
510
515
|
}
|
|
511
516
|
|
|
512
517
|
// src/spinner.ts
|
|
513
|
-
import { appendFileSync, writeSync as writeSync2 } from "node:fs";
|
|
518
|
+
import { appendFileSync as appendFileSync2, writeSync as writeSync2 } from "node:fs";
|
|
514
519
|
import { Worker } from "node:worker_threads";
|
|
515
520
|
var FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
516
521
|
var WORKER_SOURCE = `
|
|
@@ -522,7 +527,7 @@ function draw() {
|
|
|
522
527
|
if (Atomics.compareExchange(control, 1, 0, 1) !== 0) return;
|
|
523
528
|
try {
|
|
524
529
|
if (Atomics.load(control, 0) || Atomics.load(control, 2) || !frames.length) return;
|
|
525
|
-
const text = frames[frame++ % frames.length];
|
|
530
|
+
const text = frames[frame++ % frames.length].replace('__elapsed__', Math.floor((Date.now() - workerData.started) / 1000) + 's');
|
|
526
531
|
writeSync(2, text);
|
|
527
532
|
if (workerData.transcriptPath) appendFileSync(workerData.transcriptPath, JSON.stringify({ channel: 'spinner', text }) + '\\n');
|
|
528
533
|
} finally {
|
|
@@ -534,7 +539,6 @@ parentPort.on('message', (next) => {
|
|
|
534
539
|
if (Atomics.load(control, 2)) return;
|
|
535
540
|
frames = next.frames;
|
|
536
541
|
frame = next.frame;
|
|
537
|
-
Atomics.store(control, 0, 0);
|
|
538
542
|
});
|
|
539
543
|
setInterval(draw, workerData.intervalMs);
|
|
540
544
|
`;
|
|
@@ -545,20 +549,22 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
545
549
|
let control = null;
|
|
546
550
|
let frames = [];
|
|
547
551
|
let frame = 0;
|
|
552
|
+
let started = 0;
|
|
553
|
+
let suspended = false;
|
|
548
554
|
const write = (text) => {
|
|
549
555
|
if (stream) stream.write(text);
|
|
550
556
|
else {
|
|
551
557
|
writeSync2(2, text);
|
|
552
|
-
if (transcriptPath)
|
|
558
|
+
if (transcriptPath) appendFileSync2(transcriptPath, `${JSON.stringify({ channel: "spinner", text })}
|
|
553
559
|
`);
|
|
554
560
|
}
|
|
555
561
|
};
|
|
556
562
|
const render = (text, measure) => {
|
|
557
|
-
const line = face.step(text, measure, "note").split("\n")[0];
|
|
563
|
+
const line = face.step(text, measure ?? "__elapsed__", "note").split("\n")[0];
|
|
558
564
|
frames = line ? FRAMES.map((glyph) => `\r\x1B[2K${line.replace(GLYPH.dot, glyph)}`) : [];
|
|
559
565
|
};
|
|
560
566
|
const draw = () => {
|
|
561
|
-
if (animate && frames.length) write(frames[frame++ % frames.length]);
|
|
567
|
+
if (animate && !suspended && frames.length) write(frames[frame++ % frames.length].replace("__elapsed__", `${Math.floor((Date.now() - started) / 1e3)}s`));
|
|
562
568
|
};
|
|
563
569
|
const pause = () => {
|
|
564
570
|
if (!control) return;
|
|
@@ -578,6 +584,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
578
584
|
start(text, measure = null) {
|
|
579
585
|
if (!animate) return;
|
|
580
586
|
halt();
|
|
587
|
+
suspended = false;
|
|
588
|
+
started = Date.now();
|
|
581
589
|
render(text, measure);
|
|
582
590
|
frame = 0;
|
|
583
591
|
draw();
|
|
@@ -591,7 +599,8 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
591
599
|
frames,
|
|
592
600
|
frame,
|
|
593
601
|
intervalMs,
|
|
594
|
-
transcriptPath
|
|
602
|
+
transcriptPath,
|
|
603
|
+
started
|
|
595
604
|
} });
|
|
596
605
|
const active = worker;
|
|
597
606
|
worker.on("error", () => {
|
|
@@ -609,9 +618,22 @@ function createSpinner(face, { animate, stream, intervalMs = 90, transcriptPath
|
|
|
609
618
|
render(text, measure);
|
|
610
619
|
draw();
|
|
611
620
|
worker?.postMessage({ frames, frame });
|
|
621
|
+
if (control && !suspended) Atomics.store(control, 0, 0);
|
|
622
|
+
},
|
|
623
|
+
pause() {
|
|
624
|
+
if (suspended) return;
|
|
625
|
+
suspended = true;
|
|
626
|
+
pause();
|
|
627
|
+
if (animate && frames.length) write("\r\x1B[2K");
|
|
628
|
+
},
|
|
629
|
+
resume() {
|
|
630
|
+
suspended = false;
|
|
631
|
+
if (control) Atomics.store(control, 0, 0);
|
|
632
|
+
if (timer || worker) draw();
|
|
612
633
|
},
|
|
613
634
|
stop() {
|
|
614
635
|
halt();
|
|
636
|
+
frames = [];
|
|
615
637
|
if (animate) write("\r\x1B[2K");
|
|
616
638
|
}
|
|
617
639
|
};
|
|
@@ -789,7 +811,7 @@ function assertScriptConformance(script, options) {
|
|
|
789
811
|
}
|
|
790
812
|
|
|
791
813
|
// src/run.ts
|
|
792
|
-
import { appendFileSync as
|
|
814
|
+
import { appendFileSync as appendFileSync3 } from "node:fs";
|
|
793
815
|
|
|
794
816
|
// src/outcome.ts
|
|
795
817
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
@@ -882,6 +904,7 @@ var PHASES = {
|
|
|
882
904
|
"verify-release": ["Checking the release version", "Verified the release version"],
|
|
883
905
|
verify: ["Verifying the payload", "Verified the payload"],
|
|
884
906
|
install: ["Installing the product", "Installed the product"],
|
|
907
|
+
configure: ["Configuring the product", "Configured the product"],
|
|
885
908
|
activate: ["Activating surfaces", "Activated surfaces"],
|
|
886
909
|
doctor: ["Checking health", "Checked health"],
|
|
887
910
|
rollback: ["Restoring the previous version", "Restored the previous version"]
|
|
@@ -911,7 +934,7 @@ function createInstallerRun(value, options = {}) {
|
|
|
911
934
|
if (!text) return;
|
|
912
935
|
write(text, channel);
|
|
913
936
|
if (env.MM_FACE_TRANSCRIPT) {
|
|
914
|
-
|
|
937
|
+
appendFileSync3(env.MM_FACE_TRANSCRIPT, `${JSON.stringify({ channel, text: recorded })}
|
|
915
938
|
`, "utf8");
|
|
916
939
|
}
|
|
917
940
|
};
|
|
@@ -926,6 +949,17 @@ function createInstallerRun(value, options = {}) {
|
|
|
926
949
|
return true;
|
|
927
950
|
} } } : { transcriptPath: env.MM_FACE_TRANSCRIPT }
|
|
928
951
|
});
|
|
952
|
+
let activeTitle;
|
|
953
|
+
let chunkOpen = false;
|
|
954
|
+
let chunkChannel = "stdout";
|
|
955
|
+
let chunkColumn = 0;
|
|
956
|
+
const trailingCR = /* @__PURE__ */ new Set();
|
|
957
|
+
let deferredActivity;
|
|
958
|
+
const closeChunk = () => {
|
|
959
|
+
if (chunkOpen) emit("\n", chunkChannel);
|
|
960
|
+
chunkOpen = false;
|
|
961
|
+
chunkColumn = 0;
|
|
962
|
+
};
|
|
929
963
|
let started = false;
|
|
930
964
|
let finished = false;
|
|
931
965
|
const start = () => {
|
|
@@ -940,6 +974,9 @@ function createInstallerRun(value, options = {}) {
|
|
|
940
974
|
}
|
|
941
975
|
};
|
|
942
976
|
const durable = (title, measure, kind) => {
|
|
977
|
+
closeChunk();
|
|
978
|
+
activeTitle = void 0;
|
|
979
|
+
deferredActivity = void 0;
|
|
943
980
|
spinner.stop();
|
|
944
981
|
const rendered = face.step(title, measure, kind);
|
|
945
982
|
if (rendered) lines([tty ? rendered : `${kind === "fail" ? "Failed: " : ""}${title}${measure === null ? "" : ` (${typeof measure === "number" ? `${Math.max(0, Math.round(measure))}s` : measure})`}`]);
|
|
@@ -956,7 +993,12 @@ function createInstallerRun(value, options = {}) {
|
|
|
956
993
|
const state = facts.state ?? "ok";
|
|
957
994
|
const title = PHASES[id][state === "ok" ? 1 : 0];
|
|
958
995
|
if (state === "running") {
|
|
959
|
-
|
|
996
|
+
if (face.progress(title, facts.measure ?? null)) return;
|
|
997
|
+
if (activeTitle === title) spinner.say(title, facts.measure ?? null);
|
|
998
|
+
else {
|
|
999
|
+
activeTitle = title;
|
|
1000
|
+
spinner.start(title, facts.measure ?? null);
|
|
1001
|
+
}
|
|
960
1002
|
return;
|
|
961
1003
|
}
|
|
962
1004
|
if (!face.continues(id, state)) durable(title, facts.seconds ?? facts.measure ?? null, state);
|
|
@@ -976,8 +1018,20 @@ function createInstallerRun(value, options = {}) {
|
|
|
976
1018
|
durable(`${facts.id}${versions} ${separator} ${status}${activation}`, completed ? facts.seconds ?? null : null, facts.state === "failed" ? "fail" : facts.state === "updated" ? "ok" : "note");
|
|
977
1019
|
if (facts.detail) run.relay(facts.detail);
|
|
978
1020
|
},
|
|
979
|
-
milestone({ step, state, ms }) {
|
|
1021
|
+
milestone({ step, state, ms, measure }) {
|
|
980
1022
|
start();
|
|
1023
|
+
if (state === "running") {
|
|
1024
|
+
if (chunkOpen) {
|
|
1025
|
+
deferredActivity = { title: step, measure: measure ?? null };
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (activeTitle === step) spinner.say(step, measure ?? null);
|
|
1029
|
+
else {
|
|
1030
|
+
activeTitle = step;
|
|
1031
|
+
spinner.start(step, measure ?? null);
|
|
1032
|
+
}
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
981
1035
|
durable(step, ms === void 0 ? null : ms / 1e3, state);
|
|
982
1036
|
},
|
|
983
1037
|
signIn({ url, code }) {
|
|
@@ -991,7 +1045,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
991
1045
|
},
|
|
992
1046
|
// Only pass safe diagnostic text, never authentication output or credentials.
|
|
993
1047
|
relay(text, channel = "stdout", record = true) {
|
|
994
|
-
|
|
1048
|
+
closeChunk();
|
|
1049
|
+
spinner.pause();
|
|
995
1050
|
const rendered = tty ? face.relay(text) : stripColor(text);
|
|
996
1051
|
for (const row of rendered.split("\n")) if (row) {
|
|
997
1052
|
emit(`${row}
|
|
@@ -999,6 +1054,37 @@ function createInstallerRun(value, options = {}) {
|
|
|
999
1054
|
` : `${tty ? face.relay("[external output omitted]") : "[external output omitted]"}
|
|
1000
1055
|
`);
|
|
1001
1056
|
}
|
|
1057
|
+
spinner.resume();
|
|
1058
|
+
},
|
|
1059
|
+
relayChunk(text, channel = "stdout") {
|
|
1060
|
+
spinner.pause();
|
|
1061
|
+
if (chunkOpen && chunkChannel !== channel) closeChunk();
|
|
1062
|
+
chunkChannel = channel;
|
|
1063
|
+
if (trailingCR.delete(channel) && text.startsWith("\n")) text = text.slice(1);
|
|
1064
|
+
if (text.endsWith("\r")) trailingCR.add(channel);
|
|
1065
|
+
const parts = text.replace(/\r\n|\r/g, "\n").split("\n");
|
|
1066
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1067
|
+
const characters = [...stripColor(parts[i])];
|
|
1068
|
+
while (characters.length) {
|
|
1069
|
+
if (tty && chunkColumn >= face.width - 6) closeChunk();
|
|
1070
|
+
const part = characters.splice(0, tty ? face.width - 6 - chunkColumn : characters.length).join("");
|
|
1071
|
+
emit(tty && !chunkOpen ? face.relay(part) : part, channel, chunkOpen ? "" : tty ? face.relay("[external output omitted]") : "[external output omitted]");
|
|
1072
|
+
chunkColumn += [...part].length;
|
|
1073
|
+
chunkOpen = true;
|
|
1074
|
+
}
|
|
1075
|
+
if (i < parts.length - 1) {
|
|
1076
|
+
emit("\n", channel);
|
|
1077
|
+
chunkOpen = false;
|
|
1078
|
+
chunkColumn = 0;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (!chunkOpen) {
|
|
1082
|
+
if (deferredActivity) {
|
|
1083
|
+
activeTitle = deferredActivity.title;
|
|
1084
|
+
spinner.start(deferredActivity.title, deferredActivity.measure);
|
|
1085
|
+
deferredActivity = void 0;
|
|
1086
|
+
} else spinner.resume();
|
|
1087
|
+
}
|
|
1002
1088
|
},
|
|
1003
1089
|
cancel() {
|
|
1004
1090
|
if (finished) return;
|
|
@@ -1007,6 +1093,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
1007
1093
|
{ total: 0, updated: 0, failed: 0, deferred: true, detail: "Operation cancelled." }
|
|
1008
1094
|
);
|
|
1009
1095
|
start();
|
|
1096
|
+
closeChunk();
|
|
1097
|
+
activeTitle = void 0;
|
|
1010
1098
|
spinner.stop();
|
|
1011
1099
|
finished = true;
|
|
1012
1100
|
if (!face.nested || !env.MM_INSTALLER_OUTCOME_FILE) {
|
|
@@ -1019,6 +1107,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
1019
1107
|
validateInstallerOutcome(facts);
|
|
1020
1108
|
if (env.MM_INSTALLER_OUTCOME_FILE) writeInstallerOutcome(env.MM_INSTALLER_OUTCOME_FILE, facts);
|
|
1021
1109
|
start();
|
|
1110
|
+
closeChunk();
|
|
1111
|
+
activeTitle = void 0;
|
|
1022
1112
|
spinner.stop();
|
|
1023
1113
|
finished = true;
|
|
1024
1114
|
if (options.quiet && facts.updated === 0 && facts.failed === 0 && !facts.operationFailed) return;
|
|
@@ -1039,6 +1129,8 @@ function createInstallerRun(value, options = {}) {
|
|
|
1039
1129
|
if (tty) lines([face.signOff()]);
|
|
1040
1130
|
},
|
|
1041
1131
|
stop() {
|
|
1132
|
+
closeChunk();
|
|
1133
|
+
activeTitle = void 0;
|
|
1042
1134
|
spinner.stop();
|
|
1043
1135
|
}
|
|
1044
1136
|
};
|