@mutmutco/installer-face 0.2.1 → 0.4.1
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 +14 -0
- package/README.md +57 -16
- package/dist/conformance.d.ts +8 -0
- package/dist/face.d.ts +40 -6
- package/dist/index.d.ts +10 -1
- package/dist/index.js +688 -71
- package/dist/maintenance.d.ts +52 -0
- package/dist/outcome.d.ts +6 -0
- package/dist/payload.d.ts +7 -0
- package/dist/products.d.ts +6 -0
- package/dist/run.d.ts +86 -0
- package/dist/spinner.d.ts +9 -12
- package/dist/transcript.d.ts +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 0.4.1
|
|
2
|
+
|
|
3
|
+
- Capture bootstrap output and validate recorded installer transcripts.
|
|
4
|
+
- Preserve one final receipt across native bootstrap continuation.
|
|
5
|
+
- Use completed phase labels and retain transcript framing.
|
|
6
|
+
|
|
7
|
+
# 0.4.0
|
|
8
|
+
|
|
9
|
+
- Own maintenance ordering and outcomes from a validated product declaration.
|
|
10
|
+
- Share payload entry generation instead of embedding per-product installer scripts.
|
|
11
|
+
- Keep one console owner across nested installation and self-update handoff.
|
|
12
|
+
- Carry deferred and failed child outcomes across Windows shims.
|
|
13
|
+
- Record channel-tagged transcripts with authentication data excluded.
|
|
14
|
+
- Enforce completed-run conformance and preserve narrow-terminal diagnostics.
|
package/README.md
CHANGED
|
@@ -15,26 +15,67 @@ Jerv-Hub wrote this renderer, MM-Strategy copied it, and the copy immediately fo
|
|
|
15
15
|
breaking its own two-space rule. Four copies are four drifts waiting, and prose cannot stop drift;
|
|
16
16
|
only shared code and a shared guard can.
|
|
17
17
|
|
|
18
|
-
## Use
|
|
18
|
+
## Use the shared run
|
|
19
|
+
|
|
20
|
+
Product install/update entrypoints call `runMaintenance(declaration, args, engines)`.
|
|
21
|
+
The package owns phase ordering, self-update handoff, scheduler completion, outcome reporting and
|
|
22
|
+
cleanup. Product engines return version, surface and failure facts; they do not draw a run.
|
|
23
|
+
The declaration contains the canonical product key (`mmi` and `jerv` aliases are accepted), gate
|
|
24
|
+
URL, canonical doctor command, and a nonempty unique surface list
|
|
25
|
+
`{ id, npm?, bin?, kind?, activation? }`.
|
|
26
|
+
|
|
27
|
+
`createInstallerRun` is the lower-level owner used by the shared launcher, not a reason for each
|
|
28
|
+
product to author another installer. `renderPayloadEntry` generates the npm-install/converge entry
|
|
29
|
+
from tarball names and a declared convergence command. Product release scripts supply those facts.
|
|
30
|
+
|
|
31
|
+
The package owns greeting, phase names, spinner, surface status, receipt and sign-off.
|
|
32
|
+
`phase` accepts preflight, resolve, download, verify, install, activate, doctor, sign-in, check,
|
|
33
|
+
arm and verify-release. Its facts are state (running/ok/fail/note), seconds, measure and safe detail.
|
|
34
|
+
`surface` accepts updated/current/failed/skipped/retry/pending/kept. `finish` accepts version,
|
|
35
|
+
total, updated, failed and optional retry, dryRun, installed, deferred and safe detail.
|
|
36
|
+
Missing version or deferred work never reports ready. Counts must describe the same operation set.
|
|
37
|
+
Pass `dryRun: true` in options so individual rows also say “would update”.
|
|
38
|
+
|
|
39
|
+
`operation: "install"` selects the canonical first-install welcome.
|
|
40
|
+
`start()` and `finish()` are idempotent. `stop()` clears transient work. The orchestration wrapper
|
|
41
|
+
always stops the spinner, including when an operation throws; the caller retains error handling.
|
|
42
|
+
Use the manual lifecycle for a self-reexec that transfers final receipt ownership to its child.
|
|
43
|
+
|
|
44
|
+
`signIn({url, code})` owns device-login instructions. The code is visible in the terminal and
|
|
45
|
+
redacted in transcripts. `relay(text, channel)` and `milestone({step,state,ms})` accept only safe
|
|
46
|
+
child diagnostics: never pass authentication responses, credentials or raw provider errors.
|
|
47
|
+
|
|
48
|
+
TTY, color, width, environment, animation and output sink can be injected through options.
|
|
49
|
+
Non-TTY output is plain append-only text. NO_COLOR disables color, not the visual structure.
|
|
50
|
+
Nested processes suppress greetings and send their final outcome to the console owner.
|
|
51
|
+
The private outcome channel works through Windows command shims as well as direct child processes;
|
|
52
|
+
a successful process exit never turns a deferred result into readiness.
|
|
53
|
+
Animations never write milestones. `MM_FACE_TRANSCRIPT` appends JSONL `{channel,text}` records
|
|
54
|
+
for emitted output, preserving ANSI and stdout/stderr/spinner channels. Capture the owning outer
|
|
55
|
+
run for the console transcript. Untrusted external output is displayed but represented by an
|
|
56
|
+
omission marker in saved transcripts; authentication output cannot be a golden reference.
|
|
57
|
+
Transcripts are opt-in and must never contain secrets. A recorded fixture is not evidence of a
|
|
58
|
+
real signed-in advertised installer run; that acceptance remains a separate human check.
|
|
59
|
+
|
|
60
|
+
The lower-level `createFace` API remains for doctor rows and compatibility. New installer consumers
|
|
61
|
+
must not compose their own lifecycle from its primitives.
|
|
62
|
+
|
|
63
|
+
### Self re-exec continuation
|
|
64
|
+
|
|
65
|
+
A process that re-execs itself onto an inherited console sets `MM_FACE_CONTINUES` to the
|
|
66
|
+
comma-separated phase names already drawn. `createFace` reads it directly: `welcome()` returns no
|
|
67
|
+
lines, and each `face.continues(phase, kind)` consumes one inherited phase. A failure always returns
|
|
68
|
+
false and consumes the inherited phase, so neither it nor a later retry can be hidden.
|
|
19
69
|
|
|
20
70
|
```ts
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
product: 'jerv-hub',
|
|
25
|
-
color: Boolean(process.stdout.isTTY && !process.env.NO_COLOR && process.env.TERM !== 'dumb'),
|
|
26
|
-
columns: process.stdout.columns,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
for (const line of face.welcome()) console.log(line);
|
|
30
|
-
console.log(face.step('Registering MCP server', 2));
|
|
31
|
-
console.log(face.relay(childOutput)); // another program's output, inside the rail
|
|
32
|
-
for (const line of face.receipt([...])) console.log(line);
|
|
33
|
-
console.log(face.signOff());
|
|
71
|
+
if (!face.continues('preflight', result.ok ? 'ok' : 'fail')) {
|
|
72
|
+
console.log(face.step('preflight', result.seconds, result.ok ? 'ok' : 'fail'));
|
|
73
|
+
}
|
|
34
74
|
```
|
|
35
75
|
|
|
36
|
-
|
|
37
|
-
|
|
76
|
+
The optional `env` input exists for tests; normal callers use `process.env`. Missing or empty means a
|
|
77
|
+
fresh face. Only an inheriting spawn sets the variable — never a person starting a wrapped command
|
|
78
|
+
on a fresh console.
|
|
38
79
|
|
|
39
80
|
### Served one-liners
|
|
40
81
|
|
package/dist/conformance.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ export interface ConformanceOptions {
|
|
|
4
4
|
columns?: number;
|
|
5
5
|
/** False asserts the PLAIN lane: no colour, and no glyphs or frames at all. */
|
|
6
6
|
tty?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* True asserts the NESTED lane — a run under `MM_OUTER_CONSOLE=1`, where an outer program already
|
|
9
|
+
* opened the console and will close it. Steps and failures still print; a greeting, a success
|
|
10
|
+
* receipt or a sign-off from here is the duplicate face the owner saw on 2026-09-20 (#6893).
|
|
11
|
+
*/
|
|
12
|
+
nested?: boolean;
|
|
13
|
+
/** Assert a completed human run, not a single renderer fragment. */
|
|
14
|
+
complete?: boolean;
|
|
7
15
|
}
|
|
8
16
|
/**
|
|
9
17
|
* Assert a rendered surface against the contract. `lines` is what the surface actually printed,
|
package/dist/face.d.ts
CHANGED
|
@@ -38,17 +38,48 @@ export declare function faceWidth(columns?: number): number;
|
|
|
38
38
|
/** Break at word boundaries, hard-slicing a token longer than the line itself. */
|
|
39
39
|
export declare function wrapWords(text: string, width: number): string[];
|
|
40
40
|
export type StepKind = 'ok' | 'fail' | 'note';
|
|
41
|
+
/**
|
|
42
|
+
* What the RIGHT column carries (#6895, owner 2026-09-20).
|
|
43
|
+
*
|
|
44
|
+
* One column, one rule: progress while the work runs, elapsed time when it finishes. A number is
|
|
45
|
+
* measured seconds and renders as `2s`; a string is the surface's own measure and renders verbatim
|
|
46
|
+
* (`6.9 MB`, `3.1/6.9 MB`). Before this there were two visual languages — a right-aligned time for
|
|
47
|
+
* steps and a homegrown `[████░░] 42%` bar for downloads — and no ASCII bar returns with it.
|
|
48
|
+
*/
|
|
49
|
+
export type StepMeasure = number | string | null;
|
|
50
|
+
export interface ReceiptOptions {
|
|
51
|
+
/**
|
|
52
|
+
* False when the run did NOT reach ready. A failure always prints, in every lane — suppressing it
|
|
53
|
+
* would hide a failed install behind someone else's box (#6810, #6858).
|
|
54
|
+
*/
|
|
55
|
+
ready?: boolean;
|
|
56
|
+
}
|
|
41
57
|
export interface Face {
|
|
42
58
|
readonly identity: ProductIdentity;
|
|
43
59
|
readonly width: number;
|
|
44
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* True when an OUTER console owns this run's outcome (`MM_OUTER_CONSOLE=1`). Read it to skip work
|
|
62
|
+
* that only the console owner should do — animating a spinner, printing a summary of its own.
|
|
63
|
+
*/
|
|
64
|
+
readonly nested: boolean;
|
|
65
|
+
/** True when finished steps leave as milestones on the parent's channel instead of being drawn. */
|
|
66
|
+
readonly emitsProgress: boolean;
|
|
67
|
+
/** `◆ <product> — Mutatis Mutandis` and the product's own warm line; empty on a continuation. */
|
|
45
68
|
welcome(): string[];
|
|
46
|
-
/**
|
|
47
|
-
|
|
69
|
+
/** Consume one phase inherited through MM_FACE_CONTINUES; failures are always new information. */
|
|
70
|
+
continues(phase: string, kind?: StepKind): boolean;
|
|
71
|
+
/** One durable line for one finished step: glyph, title, and the right-column measure. */
|
|
72
|
+
step(title: string, measure?: StepMeasure, kind?: StepKind): string;
|
|
73
|
+
/**
|
|
74
|
+
* The canonical success receipt, composed from the product table: `✔ <name> is ready.`, the one
|
|
75
|
+
* fact the surface alone knows, and the table's own health command. A surface supplies ONLY what
|
|
76
|
+
* changed — the other two lines are never retyped at a call site (#6895).
|
|
77
|
+
*/
|
|
78
|
+
outcome(changed: string, options?: ReceiptOptions): string[];
|
|
48
79
|
/** Another program's output, kept inside the face at the step-title column. */
|
|
49
80
|
relay(text: string): string;
|
|
50
|
-
/** The closing box: what changed, and one suggested command. */
|
|
51
|
-
receipt(lines: string[] | string): string[];
|
|
81
|
+
/** The closing box: what changed, and one suggested command. Empty when an outer console owns it. */
|
|
82
|
+
receipt(lines: string[] | string, options?: ReceiptOptions): string[];
|
|
52
83
|
/** The sign-off, in the product accent. */
|
|
53
84
|
signOff(): string;
|
|
54
85
|
/** A refusal that reads as a face line rather than a bare error string. */
|
|
@@ -56,9 +87,12 @@ export interface Face {
|
|
|
56
87
|
paint(sgr: string, text: string): string;
|
|
57
88
|
}
|
|
58
89
|
export interface FaceOptions {
|
|
90
|
+
operation?: 'install' | 'update';
|
|
59
91
|
product: string;
|
|
60
92
|
/** False emits no escape codes at all, so a piped or logged run is byte-stable plain text. */
|
|
61
93
|
color?: boolean;
|
|
62
94
|
columns?: number;
|
|
95
|
+
/** Injectable only so tests do not mutate process.env; normal callers inherit process.env. */
|
|
96
|
+
env?: Readonly<NodeJS.ProcessEnv>;
|
|
63
97
|
}
|
|
64
|
-
export declare function createFace({ product, color, columns }: FaceOptions): Face;
|
|
98
|
+
export declare function createFace({ product, color, columns, env, operation }: FaceOptions): Face;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createFace, faceWidth, stripColor, visibleWidth, wrapWords, GLYPH, PALETTE, TITLE_COLUMN } from './face.js';
|
|
2
|
-
export type { Face, FaceOptions, StepKind } from './face.js';
|
|
2
|
+
export type { Face, FaceOptions, ReceiptOptions, StepKind, StepMeasure } from './face.js';
|
|
3
3
|
export { PRODUCTS, identityFor } from './products.js';
|
|
4
4
|
export type { ProductIdentity } from './products.js';
|
|
5
5
|
export { renderShellFace, renderPowerShellFace } from './shell.js';
|
|
@@ -9,3 +9,12 @@ export { createSpinner, SPINNER_FRAMES } from './spinner.js';
|
|
|
9
9
|
export type { Spinner, SpinnerOptions } from './spinner.js';
|
|
10
10
|
export { assertFaceConformance, assertScriptConformance } from './conformance.js';
|
|
11
11
|
export type { ConformanceOptions, ScriptConformanceOptions } from './conformance.js';
|
|
12
|
+
export { createInstallerRun, validateInstallerProduct, runInstaller } from './run.js';
|
|
13
|
+
export type { InstallerProduct, InstallerRun, InstallerRunOptions, InstallerPhase, InstallerPhaseFacts, InstallerSurfaceFacts, InstallerFinish, InstallerChannel } from './run.js';
|
|
14
|
+
export { renderPayloadEntry } from './payload.js';
|
|
15
|
+
export type { PayloadEntryOptions } from './payload.js';
|
|
16
|
+
export { runMaintenance } from './maintenance.js';
|
|
17
|
+
export type { MaintenanceArm, MaintenanceSummary } from './maintenance.js';
|
|
18
|
+
export { validateInstallerOutcome, readInstallerOutcome, writeInstallerOutcome } from './outcome.js';
|
|
19
|
+
export type { InstallerOutcome } from './outcome.js';
|
|
20
|
+
export { assertInstallerTranscript } from './transcript.js';
|