@younndai/lyt 0.13.5 → 0.20.0
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/README.md +27 -19
- package/dist/automator-run.d.ts +1 -0
- package/dist/automator-run.d.ts.map +1 -1
- package/dist/automator-run.js +5 -3
- package/dist/automator-run.js.map +1 -1
- package/dist/bench/pod-harness.d.ts.map +1 -1
- package/dist/bench/pod-harness.js +49 -1
- package/dist/bench/pod-harness.js.map +1 -1
- package/dist/build-program.d.ts +10 -1
- package/dist/build-program.d.ts.map +1 -1
- package/dist/build-program.js +23 -3
- package/dist/build-program.js.map +1 -1
- package/dist/cli.js +10 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +1 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +404 -244
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +107 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/outdated.d.ts.map +1 -1
- package/dist/commands/outdated.js +19 -5
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/update.d.ts +90 -2
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +883 -48
- package/dist/commands/update.js.map +1 -1
- package/dist/flows/init-bootstrap.d.ts +7 -1
- package/dist/flows/init-bootstrap.d.ts.map +1 -1
- package/dist/flows/init-bootstrap.js +240 -69
- package/dist/flows/init-bootstrap.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/install/doctor-health.d.ts +16 -0
- package/dist/install/doctor-health.d.ts.map +1 -0
- package/dist/install/doctor-health.js +575 -0
- package/dist/install/doctor-health.js.map +1 -0
- package/dist/install/provider-inventory.d.ts +40 -0
- package/dist/install/provider-inventory.d.ts.map +1 -0
- package/dist/install/provider-inventory.js +73 -0
- package/dist/install/provider-inventory.js.map +1 -0
- package/dist/install/reconcile-engine.d.ts +87 -0
- package/dist/install/reconcile-engine.d.ts.map +1 -0
- package/dist/install/reconcile-engine.js +771 -0
- package/dist/install/reconcile-engine.js.map +1 -0
- package/dist/install/target-artifacts.d.ts +91 -0
- package/dist/install/target-artifacts.d.ts.map +1 -0
- package/dist/install/target-artifacts.js +597 -0
- package/dist/install/target-artifacts.js.map +1 -0
- package/dist/install/update-operation.d.ts +47 -0
- package/dist/install/update-operation.d.ts.map +1 -0
- package/dist/install/update-operation.js +318 -0
- package/dist/install/update-operation.js.map +1 -0
- package/dist/install/update-plan.d.ts +132 -0
- package/dist/install/update-plan.d.ts.map +1 -0
- package/dist/install/update-plan.js +418 -0
- package/dist/install/update-plan.js.map +1 -0
- package/package.json +16 -6
package/dist/commands/init.js
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { Command } from "commander";
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
17
18
|
import { createInterface } from "node:readline/promises";
|
|
18
|
-
import { checkCurrency, closeRegistry, embeddingsOfferGate, formatCurrencyLine, getHandleFromIdentity, markAsked,
|
|
19
|
+
import { checkCurrency, closeRegistry, creationCheckpointPathDigest, creationLocalMutationCount, embeddingsOfferGate, formatCurrencyLine, getHandleFromIdentity, markAsked, openRegistry, readIdentityCache, newUuidv7Bytes, parseReceiptV1ForEmission, recoverVaultsFromPodManifest, reconstructionExitCode, ReadlinePromptHandler, renderNextSteps, renderPodCard, resolveAskedState, runWizard, startSpinner, syncPodLedgerFlow, validateMeshName, uuid7BytesToDashedString, } from "@younndai/lyt-vault";
|
|
19
20
|
import { initBootstrapFlow, probeFreshState, resolveLocalFirst, } from "../flows/init-bootstrap.js";
|
|
20
21
|
import { healPod, summarizeHeal } from "../flows/heal.js";
|
|
21
22
|
export function buildLytInitCommand() {
|
|
@@ -24,226 +25,285 @@ export function buildLytInitCommand() {
|
|
|
24
25
|
.option("--auto", "Force non-interactive bootstrap (skips first-init wizard auto-route)")
|
|
25
26
|
.option("--custom", "Three-prompt walkthrough; conflicts with --auto; requires a TTY")
|
|
26
27
|
.option("--discover", "Read-only GH delta; surface accessible lyt-* repos not in local registry")
|
|
27
|
-
.option("--wizard", "Force
|
|
28
|
-
.option("--dry-run", "Only valid with --wizard.
|
|
28
|
+
.option("--wizard", "Force guided setup: verify prerequisites and authentication, install agent integrations, detect/adopt another machine, then create one local pod/mesh/main-vault checkpoint. It never publishes, generates a pod-map, or creates demo content. Conflicts with --auto/--custom/--discover/--json.")
|
|
29
|
+
.option("--dry-run", "Only valid with --wizard. Plans setup without spawn invocations or filesystem writes.")
|
|
29
30
|
.option("--json", "Emit deterministic JSON")
|
|
30
31
|
.action(async (opts) => {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
const startedAt = new Date().toISOString();
|
|
33
|
+
const operationId = uuid7BytesToDashedString(newUuidv7Bytes());
|
|
34
|
+
const attemptId = uuid7BytesToDashedString(newUuidv7Bytes());
|
|
35
|
+
const originalLog = console.log;
|
|
36
|
+
console.log = (...args) => console.error(...args);
|
|
37
|
+
try {
|
|
38
|
+
const noMode = opts.auto !== true &&
|
|
39
|
+
opts.custom !== true &&
|
|
40
|
+
opts.discover !== true &&
|
|
41
|
+
opts.wizard !== true &&
|
|
42
|
+
opts.json !== true &&
|
|
43
|
+
opts.dryRun !== true;
|
|
44
|
+
if (noMode) {
|
|
45
|
+
let isFresh;
|
|
46
|
+
try {
|
|
47
|
+
isFresh = await probeFreshState();
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51
|
+
emitError(false, { error: "first-init-probe-failed", message: msg }, { operationId, attemptId, startedAt });
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (isFresh) {
|
|
56
|
+
if (process.stdin.isTTY !== true) {
|
|
57
|
+
emitError(false, {
|
|
58
|
+
error: "first-init-requires-tty",
|
|
59
|
+
message: "lyt init: interactive required for first-init; use --auto for non-interactive bootstrap.",
|
|
60
|
+
}, { operationId, attemptId, startedAt });
|
|
61
|
+
process.exitCode = 3;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
opts.wizard = true;
|
|
65
|
+
}
|
|
47
66
|
}
|
|
48
|
-
if (
|
|
49
|
-
if (
|
|
67
|
+
if (opts.wizard === true) {
|
|
68
|
+
if (opts.auto === true ||
|
|
69
|
+
opts.custom === true ||
|
|
70
|
+
opts.discover === true ||
|
|
71
|
+
opts.json === true) {
|
|
50
72
|
emitError(false, {
|
|
51
|
-
error: "
|
|
52
|
-
message: "lyt init
|
|
53
|
-
});
|
|
73
|
+
error: "flag-conflict",
|
|
74
|
+
message: "lyt init --wizard is mutually exclusive with --auto, --custom, --discover, and --json.",
|
|
75
|
+
}, { operationId, attemptId, startedAt });
|
|
76
|
+
process.exitCode = 2;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (opts.dryRun !== true && process.stdin.isTTY !== true) {
|
|
80
|
+
emitError(false, {
|
|
81
|
+
error: "wizard-requires-tty",
|
|
82
|
+
message: "lyt init --wizard requires an interactive terminal (or pass --dry-run for a non-TTY phase walk).",
|
|
83
|
+
}, { operationId, attemptId, startedAt });
|
|
54
84
|
process.exitCode = 3;
|
|
55
85
|
return;
|
|
56
86
|
}
|
|
57
|
-
opts.
|
|
87
|
+
const handler = opts.dryRun === true ? makeDryRunDefaultsHandler() : new ReadlinePromptHandler();
|
|
88
|
+
try {
|
|
89
|
+
const result = await runWizard({
|
|
90
|
+
promptHandler: handler,
|
|
91
|
+
dryRun: opts.dryRun === true,
|
|
92
|
+
});
|
|
93
|
+
const summaryLines = [
|
|
94
|
+
"",
|
|
95
|
+
"── Setup summary ──────────────────────────────────",
|
|
96
|
+
...result.phases.map((ph) => {
|
|
97
|
+
const glyph = ph.skipped === true ? "⊘" : ph.ok ? "✓" : "✗";
|
|
98
|
+
const num = String(ph.phase).padStart(2, " ");
|
|
99
|
+
return ` ${glyph} Phase ${num} ${ph.name} — ${ph.message}`;
|
|
100
|
+
}),
|
|
101
|
+
"───────────────────────────────────────────────────",
|
|
102
|
+
];
|
|
103
|
+
console.error(summaryLines.join("\n"));
|
|
104
|
+
if (result.status !== "completed") {
|
|
105
|
+
const adoptPhase = result.phases.find((ph) => ph.name === "adopt-pod");
|
|
106
|
+
process.exitCode = adoptPhase?.data?.reconstructExitCode ?? 1;
|
|
107
|
+
}
|
|
108
|
+
emitRootInitReceipt(makeRootInitReceipt({
|
|
109
|
+
operationId: result.creation === undefined
|
|
110
|
+
? operationId
|
|
111
|
+
: uuid7BytesToDashedString(Buffer.from(result.creation.plan.intended_effects.operation_id, "hex")),
|
|
112
|
+
attemptId: result.creation?.plan.attempt.attempt_id ?? attemptId,
|
|
113
|
+
startedAt,
|
|
114
|
+
logicalKey: result.creation?.plan.logical_replay_key ?? {
|
|
115
|
+
mode: "wizard",
|
|
116
|
+
dryRun: opts.dryRun === true,
|
|
117
|
+
},
|
|
118
|
+
status: result.status === "completed"
|
|
119
|
+
? result.creation === undefined ||
|
|
120
|
+
creationLocalMutationCount(result.creation.mutations) === 0
|
|
121
|
+
? "no-op"
|
|
122
|
+
: "success"
|
|
123
|
+
: result.creation !== undefined &&
|
|
124
|
+
creationLocalMutationCount(result.creation.mutations) > 0
|
|
125
|
+
? "partial"
|
|
126
|
+
: "failed",
|
|
127
|
+
local: result.creation === undefined
|
|
128
|
+
? 0
|
|
129
|
+
: creationLocalMutationCount(result.creation.mutations),
|
|
130
|
+
...(result.creation === undefined
|
|
131
|
+
? {}
|
|
132
|
+
: {
|
|
133
|
+
creation: result.creation,
|
|
134
|
+
}),
|
|
135
|
+
...(result.status === "completed"
|
|
136
|
+
? {}
|
|
137
|
+
: {
|
|
138
|
+
error: {
|
|
139
|
+
code: "wizard-incomplete",
|
|
140
|
+
summary: "Lyt setup did not complete.",
|
|
141
|
+
retryable: true,
|
|
142
|
+
},
|
|
143
|
+
next: {
|
|
144
|
+
code: "resume-lyt-init",
|
|
145
|
+
summary: "Correct the reported phase and rerun lyt init.",
|
|
146
|
+
},
|
|
147
|
+
exitCode: Number(process.exitCode ?? 1),
|
|
148
|
+
}),
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
153
|
+
emitError(false, { error: "wizard-error", message: msg }, { operationId, attemptId, startedAt });
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
handler.close?.();
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
58
160
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (opts.auto === true ||
|
|
62
|
-
opts.custom === true ||
|
|
63
|
-
opts.discover === true ||
|
|
64
|
-
opts.json === true) {
|
|
65
|
-
emitError(false, {
|
|
161
|
+
if (opts.dryRun === true) {
|
|
162
|
+
emitError(opts.json === true, {
|
|
66
163
|
error: "flag-conflict",
|
|
67
|
-
message: "lyt init --
|
|
68
|
-
});
|
|
164
|
+
message: "lyt init: --dry-run is only valid in combination with --wizard.",
|
|
165
|
+
}, { operationId, attemptId, startedAt });
|
|
69
166
|
process.exitCode = 2;
|
|
70
167
|
return;
|
|
71
168
|
}
|
|
72
|
-
if (opts.
|
|
73
|
-
emitError(
|
|
74
|
-
error: "
|
|
75
|
-
message: "lyt init --
|
|
76
|
-
});
|
|
77
|
-
process.exitCode =
|
|
169
|
+
if (opts.auto === true && opts.custom === true) {
|
|
170
|
+
emitError(opts.json === true, {
|
|
171
|
+
error: "flag-conflict",
|
|
172
|
+
message: "lyt init: --auto and --custom are mutually exclusive. Omit both (defaults to --auto), or pick one.",
|
|
173
|
+
}, { operationId, attemptId, startedAt });
|
|
174
|
+
process.exitCode = 2;
|
|
78
175
|
return;
|
|
79
176
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"",
|
|
88
|
-
"── Setup summary ──────────────────────────────────",
|
|
89
|
-
...result.phases.map((ph) => {
|
|
90
|
-
const glyph = ph.skipped === true ? "⊘" : ph.ok ? "✓" : "✗";
|
|
91
|
-
const num = String(ph.phase).padStart(2, " ");
|
|
92
|
-
return ` ${glyph} Phase ${num} ${ph.name} — ${ph.message}`;
|
|
93
|
-
}),
|
|
94
|
-
"───────────────────────────────────────────────────",
|
|
95
|
-
];
|
|
96
|
-
console.log(summaryLines.join("\n"));
|
|
97
|
-
if (result.status !== "completed") {
|
|
98
|
-
const adoptPhase = result.phases.find((ph) => ph.name === "adopt-pod");
|
|
99
|
-
process.exitCode = adoptPhase?.data?.reconstructExitCode ?? 1;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
104
|
-
emitError(false, { error: "wizard-error", message: msg });
|
|
105
|
-
process.exitCode = 1;
|
|
106
|
-
}
|
|
107
|
-
finally {
|
|
108
|
-
handler.close?.();
|
|
109
|
-
}
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (opts.dryRun === true) {
|
|
113
|
-
emitError(opts.json === true, {
|
|
114
|
-
error: "flag-conflict",
|
|
115
|
-
message: "lyt init: --dry-run is only valid in combination with --wizard.",
|
|
116
|
-
});
|
|
117
|
-
process.exitCode = 2;
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (opts.auto === true && opts.custom === true) {
|
|
121
|
-
emitError(opts.json === true, {
|
|
122
|
-
error: "flag-conflict",
|
|
123
|
-
message: "lyt init: --auto and --custom are mutually exclusive. Omit both (defaults to --auto), or pick one.",
|
|
124
|
-
});
|
|
125
|
-
process.exitCode = 2;
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (opts.discover === true && opts.custom === true) {
|
|
129
|
-
emitError(opts.json === true, {
|
|
130
|
-
error: "flag-conflict",
|
|
131
|
-
message: "lyt init: --discover and --custom are mutually exclusive (--discover is read-only).",
|
|
132
|
-
});
|
|
133
|
-
process.exitCode = 2;
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (opts.custom === true && (process.stdin.isTTY !== true || opts.json === true)) {
|
|
137
|
-
emitError(opts.json === true, {
|
|
138
|
-
error: "custom-requires-tty",
|
|
139
|
-
message: "lyt init --custom requires an interactive terminal and cannot be combined with --json.",
|
|
140
|
-
});
|
|
141
|
-
process.exitCode = 3;
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const mode = opts.discover === true ? "discover" : opts.custom === true ? "custom" : "auto";
|
|
145
|
-
let customOverrides;
|
|
146
|
-
if (mode === "custom") {
|
|
147
|
-
try {
|
|
148
|
-
customOverrides = await runCustomPrompts();
|
|
177
|
+
if (opts.discover === true && opts.custom === true) {
|
|
178
|
+
emitError(opts.json === true, {
|
|
179
|
+
error: "flag-conflict",
|
|
180
|
+
message: "lyt init: --discover and --custom are mutually exclusive (--discover is read-only).",
|
|
181
|
+
}, { operationId, attemptId, startedAt });
|
|
182
|
+
process.exitCode = 2;
|
|
183
|
+
return;
|
|
149
184
|
}
|
|
150
|
-
|
|
151
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
185
|
+
if (opts.custom === true && (process.stdin.isTTY !== true || opts.json === true)) {
|
|
152
186
|
emitError(opts.json === true, {
|
|
153
|
-
error: "custom-
|
|
154
|
-
message:
|
|
155
|
-
});
|
|
156
|
-
process.exitCode =
|
|
187
|
+
error: "custom-requires-tty",
|
|
188
|
+
message: "lyt init --custom requires an interactive terminal and cannot be combined with --json.",
|
|
189
|
+
}, { operationId, attemptId, startedAt });
|
|
190
|
+
process.exitCode = 3;
|
|
157
191
|
return;
|
|
158
192
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
heal: () => healPod(),
|
|
165
|
-
materializePublish: (db) => materializePodLocal(db, { push: false, setRemote: !isLocalFirstContext() }),
|
|
166
|
-
refreshExistingPod: async (db) => {
|
|
167
|
-
const refreshed = await syncPodLedgerFlow({
|
|
168
|
-
registryDb: db,
|
|
169
|
-
push: false,
|
|
170
|
-
refreshOnly: true,
|
|
171
|
-
});
|
|
172
|
-
if (refreshed.status === "conflict" || refreshed.status === "error") {
|
|
173
|
-
throw new Error(`existing pod refresh failed: ${refreshed.reason ?? refreshed.status}`);
|
|
193
|
+
const mode = opts.discover === true ? "discover" : opts.custom === true ? "custom" : "auto";
|
|
194
|
+
let customOverrides;
|
|
195
|
+
if (mode === "custom") {
|
|
196
|
+
try {
|
|
197
|
+
customOverrides = await runCustomPrompts();
|
|
174
198
|
}
|
|
175
|
-
|
|
199
|
+
catch (err) {
|
|
200
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
201
|
+
emitError(opts.json === true, {
|
|
202
|
+
error: "custom-prompt-error",
|
|
203
|
+
message: msg,
|
|
204
|
+
}, { operationId, attemptId, startedAt });
|
|
205
|
+
process.exitCode = 1;
|
|
176
206
|
return;
|
|
177
|
-
const recovered = await recoverVaultsFromPodManifest({
|
|
178
|
-
handle: getHandleFromIdentity(),
|
|
179
|
-
registryDb: db,
|
|
180
|
-
});
|
|
181
|
-
const exitCode = reconstructionExitCode(recovered);
|
|
182
|
-
if (exitCode !== 0) {
|
|
183
|
-
throw new Error(recovered.refusedReason ??
|
|
184
|
-
recovered.drops[0]?.reason ??
|
|
185
|
-
"pod manifest recovery was incomplete");
|
|
186
207
|
}
|
|
187
|
-
},
|
|
188
|
-
...(customOverrides !== undefined ? { customOverrides } : {}),
|
|
189
|
-
...(spinner !== undefined
|
|
190
|
-
? {
|
|
191
|
-
onPhase: async (op, label) => {
|
|
192
|
-
spinner.phase(op, label);
|
|
193
|
-
await new Promise((r) => setImmediate(r));
|
|
194
|
-
},
|
|
195
|
-
}
|
|
196
|
-
: {}),
|
|
197
|
-
};
|
|
198
|
-
try {
|
|
199
|
-
const result = await initBootstrapFlow(flowArgs);
|
|
200
|
-
spinner?.stop();
|
|
201
|
-
if (opts.json === true) {
|
|
202
|
-
emitJsonResult(result);
|
|
203
208
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
const useSpinner = opts.json !== true && mode !== "discover";
|
|
210
|
+
const spinner = useSpinner ? startSpinner() : undefined;
|
|
211
|
+
const flowArgs = {
|
|
212
|
+
mode,
|
|
213
|
+
heal: () => healPod(),
|
|
214
|
+
refreshExistingPod: async (db) => {
|
|
215
|
+
const refreshed = await syncPodLedgerFlow({
|
|
216
|
+
registryDb: db,
|
|
217
|
+
push: false,
|
|
218
|
+
refreshOnly: true,
|
|
219
|
+
});
|
|
220
|
+
if (refreshed.status === "conflict" || refreshed.status === "error") {
|
|
221
|
+
throw new Error(`existing pod refresh failed: ${refreshed.reason ?? refreshed.status}`);
|
|
222
|
+
}
|
|
223
|
+
if (refreshed.status === "skipped")
|
|
224
|
+
return;
|
|
225
|
+
const recovered = await recoverVaultsFromPodManifest({
|
|
226
|
+
handle: getHandleFromIdentity(),
|
|
227
|
+
registryDb: db,
|
|
228
|
+
});
|
|
229
|
+
const exitCode = reconstructionExitCode(recovered);
|
|
230
|
+
if (exitCode !== 0) {
|
|
231
|
+
throw new Error(recovered.refusedReason ??
|
|
232
|
+
recovered.drops[0]?.reason ??
|
|
233
|
+
"pod manifest recovery was incomplete");
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
...(customOverrides !== undefined ? { customOverrides } : {}),
|
|
237
|
+
...(spinner !== undefined
|
|
238
|
+
? {
|
|
239
|
+
onPhase: async (op, label) => {
|
|
240
|
+
spinner.phase(op, label);
|
|
241
|
+
await new Promise((r) => setImmediate(r));
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
: {}),
|
|
245
|
+
};
|
|
246
|
+
try {
|
|
247
|
+
const result = await initBootstrapFlow(flowArgs);
|
|
248
|
+
spinner?.stop();
|
|
249
|
+
if (opts.json === true) {
|
|
250
|
+
emitJsonResult(result);
|
|
208
251
|
}
|
|
209
|
-
|
|
210
|
-
|
|
252
|
+
else {
|
|
253
|
+
emitHumanResult(result);
|
|
254
|
+
if (result.heal !== undefined) {
|
|
255
|
+
console.log(summarizeHeal(result.heal));
|
|
256
|
+
}
|
|
257
|
+
if (result.branch === "fresh") {
|
|
258
|
+
emitAutoPodCard(result);
|
|
259
|
+
}
|
|
260
|
+
else if (result.branch === "adopt" && result.adopt !== undefined) {
|
|
261
|
+
emitAdoptPodCard(result);
|
|
262
|
+
}
|
|
263
|
+
console.error("Not published. Run `lyt sync` when you want to publish scoped vault changes.");
|
|
264
|
+
if (mode === "custom") {
|
|
265
|
+
await maybeOfferEmbeddings(false);
|
|
266
|
+
}
|
|
267
|
+
const currency = await checkCurrency({ cacheOnly: true });
|
|
268
|
+
if (currency.stale) {
|
|
269
|
+
console.log(`\nHeads up: ${formatCurrencyLine(currency)}`);
|
|
270
|
+
}
|
|
211
271
|
}
|
|
212
|
-
|
|
213
|
-
|
|
272
|
+
if (result.branch === "re-init" &&
|
|
273
|
+
result.integrityIssues !== undefined &&
|
|
274
|
+
result.integrityIssues.length > 0 &&
|
|
275
|
+
result.integrityIssues.every((i) => i.status !== "ok")) {
|
|
276
|
+
process.exitCode = 1;
|
|
214
277
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
await maybeOfferEmbeddings(false);
|
|
278
|
+
if (result.branch === "adopt" && result.adoptError !== undefined) {
|
|
279
|
+
process.exitCode = 1;
|
|
218
280
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
281
|
+
if (result.branch === "adopt" && result.adopt !== undefined) {
|
|
282
|
+
const exitCode = initBootstrapSemanticExitCode(result);
|
|
283
|
+
if (exitCode !== 0)
|
|
284
|
+
process.exitCode = exitCode;
|
|
222
285
|
}
|
|
286
|
+
emitRootInitReceipt(receiptForBootstrapResult({
|
|
287
|
+
result,
|
|
288
|
+
operationId,
|
|
289
|
+
attemptId,
|
|
290
|
+
startedAt,
|
|
291
|
+
mode,
|
|
292
|
+
exitCode: Number(process.exitCode ?? 0),
|
|
293
|
+
}));
|
|
223
294
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
process.exitCode =
|
|
232
|
-
}
|
|
233
|
-
if (result.branch === "adopt" && result.adopt !== undefined) {
|
|
234
|
-
const exitCode = initBootstrapSemanticExitCode(result);
|
|
235
|
-
if (exitCode !== 0)
|
|
236
|
-
process.exitCode = exitCode;
|
|
295
|
+
catch (err) {
|
|
296
|
+
spinner?.stop();
|
|
297
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
298
|
+
emitError(opts.json === true, {
|
|
299
|
+
error: "init-bootstrap-error",
|
|
300
|
+
message,
|
|
301
|
+
}, { operationId, attemptId, startedAt });
|
|
302
|
+
process.exitCode = 2;
|
|
237
303
|
}
|
|
238
304
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
242
|
-
emitError(opts.json === true, {
|
|
243
|
-
error: "init-bootstrap-error",
|
|
244
|
-
message,
|
|
245
|
-
});
|
|
246
|
-
process.exitCode = 2;
|
|
305
|
+
finally {
|
|
306
|
+
console.log = originalLog;
|
|
247
307
|
}
|
|
248
308
|
});
|
|
249
309
|
}
|
|
@@ -500,53 +560,6 @@ function emitAdoptPodCard(res) {
|
|
|
500
560
|
console.log(renderNextSteps({ unpublished: true }));
|
|
501
561
|
console.log("");
|
|
502
562
|
}
|
|
503
|
-
async function maybePromptAndPublish(res) {
|
|
504
|
-
const pub = res.publish;
|
|
505
|
-
if (pub === undefined || pub.skipped)
|
|
506
|
-
return;
|
|
507
|
-
if (isLocalFirstContext()) {
|
|
508
|
-
console.log("\nYour pod is local-only (not connected to GitHub). Run `lyt sync` to connect + back it up.");
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
const vaultCount = pub.vaults.filter((v) => !v.skipped).length;
|
|
512
|
-
if (process.stdin.isTTY !== true) {
|
|
513
|
-
console.log("\nYour pod is staged locally (not published). Run `lyt sync` to publish it to GitHub.");
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
517
|
-
let yes;
|
|
518
|
-
try {
|
|
519
|
-
const ans = (await rl.question(`\nPublish your pod to GitHub now? (pushes your pod + ${vaultCount} vault repo(s)) [Y/n]: `))
|
|
520
|
-
.trim()
|
|
521
|
-
.toLowerCase();
|
|
522
|
-
yes = ans === "" || ans === "y" || ans === "yes";
|
|
523
|
-
}
|
|
524
|
-
finally {
|
|
525
|
-
rl.close();
|
|
526
|
-
}
|
|
527
|
-
if (!yes) {
|
|
528
|
-
console.log("Staged. Run `lyt sync` when you're ready to publish to GitHub.");
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
console.log("Publishing your pod to GitHub…");
|
|
532
|
-
const result = await reconcilePublishFlow({ push: true });
|
|
533
|
-
if (result.skipped) {
|
|
534
|
-
console.log(`Publish skipped — ${result.reason ?? "no pod"}.`);
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
const pushed = result.vaultOutcomes.filter((o) => o.pushed).length;
|
|
538
|
-
if (result.ok) {
|
|
539
|
-
console.log(`✓ Published to GitHub — ${pushed} vault repo(s) + your pod.`);
|
|
540
|
-
}
|
|
541
|
-
else {
|
|
542
|
-
console.log(`⚠ Partial publish — ${pushed} vault(s) pushed; ${result.outboxRemaining} op(s) pending. Re-run \`lyt sync\` to finish (resumable, no data lost).`);
|
|
543
|
-
for (const o of result.vaultOutcomes) {
|
|
544
|
-
if (o.status === "conflict" || o.status === "failed") {
|
|
545
|
-
console.log(` ${o.status}: ${o.vaultName} — ${o.message}`);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
563
|
async function maybeOfferEmbeddings(json) {
|
|
551
564
|
try {
|
|
552
565
|
let askedState;
|
|
@@ -577,7 +590,9 @@ async function maybeOfferEmbeddings(json) {
|
|
|
577
590
|
},
|
|
578
591
|
...(askedState !== undefined ? { askedState } : {}),
|
|
579
592
|
...(registryDb !== undefined
|
|
580
|
-
? {
|
|
593
|
+
? {
|
|
594
|
+
onSurfaced: async () => void (await markAsked(registryDb, new Date().toISOString())),
|
|
595
|
+
}
|
|
581
596
|
: {}),
|
|
582
597
|
});
|
|
583
598
|
}
|
|
@@ -614,12 +629,157 @@ function isLocalFirstContext() {
|
|
|
614
629
|
return true;
|
|
615
630
|
}
|
|
616
631
|
}
|
|
617
|
-
function
|
|
618
|
-
|
|
619
|
-
|
|
632
|
+
function receiptForBootstrapResult(args) {
|
|
633
|
+
const creation = args.result.creation;
|
|
634
|
+
const publish = args.result.publish;
|
|
635
|
+
const materialized = publish?.vaults.filter((vault) => !vault.skipped).length ?? 0;
|
|
636
|
+
const local = creation === undefined
|
|
637
|
+
? (publish?.podCommitted === true ? 1 : 0) +
|
|
638
|
+
materialized +
|
|
639
|
+
(args.result.reconciledVaultPaths?.length ?? 0)
|
|
640
|
+
: creationLocalMutationCount(creation.mutations);
|
|
641
|
+
const failed = args.exitCode !== 0;
|
|
642
|
+
const status = failed ? (local > 0 ? "partial" : "failed") : local > 0 ? "success" : "no-op";
|
|
643
|
+
return makeRootInitReceipt({
|
|
644
|
+
operationId: creation === undefined
|
|
645
|
+
? args.operationId
|
|
646
|
+
: uuid7BytesToDashedString(Buffer.from(creation.plan.intended_effects.operation_id, "hex")),
|
|
647
|
+
attemptId: creation?.plan.attempt.attempt_id ?? args.attemptId,
|
|
648
|
+
startedAt: args.startedAt,
|
|
649
|
+
logicalKey: creation?.plan.logical_replay_key ?? {
|
|
650
|
+
mode: args.mode,
|
|
651
|
+
branch: args.result.branch,
|
|
652
|
+
},
|
|
653
|
+
status,
|
|
654
|
+
local,
|
|
655
|
+
branch: args.result.branch,
|
|
656
|
+
vaultCount: creation?.plan.intended_effects.vaults.length ?? materialized,
|
|
657
|
+
...(creation === undefined ? {} : { creation }),
|
|
658
|
+
...(failed
|
|
659
|
+
? {
|
|
660
|
+
error: {
|
|
661
|
+
code: "init-incomplete",
|
|
662
|
+
summary: "Lyt initialization did not complete.",
|
|
663
|
+
retryable: true,
|
|
664
|
+
},
|
|
665
|
+
next: {
|
|
666
|
+
code: "resume-lyt-init",
|
|
667
|
+
summary: "Inspect the reported state and rerun lyt init.",
|
|
668
|
+
},
|
|
669
|
+
exitCode: args.exitCode,
|
|
670
|
+
}
|
|
671
|
+
: {}),
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
function makeRootInitReceipt(args) {
|
|
675
|
+
const refused = args.status === "refused";
|
|
676
|
+
const after = [];
|
|
677
|
+
if (args.branch)
|
|
678
|
+
after.push({ kind: "init-branch", subject: args.branch });
|
|
679
|
+
if ((args.vaultCount ?? 0) > 0) {
|
|
680
|
+
after.push({
|
|
681
|
+
kind: "creation-scope",
|
|
682
|
+
subject: "locally materialized creation scope",
|
|
683
|
+
count: args.vaultCount,
|
|
684
|
+
});
|
|
620
685
|
}
|
|
621
|
-
|
|
622
|
-
|
|
686
|
+
if (args.creation !== undefined) {
|
|
687
|
+
after.push({
|
|
688
|
+
kind: "creation-plan",
|
|
689
|
+
subject: args.creation.plan.logical_replay_key,
|
|
690
|
+
});
|
|
691
|
+
after.push({
|
|
692
|
+
kind: "creation-checkpoints",
|
|
693
|
+
subject: "repository-qualified creation checkpoint evidence",
|
|
694
|
+
digest: creationCheckpointPathDigest(args.creation.mutations),
|
|
695
|
+
count: args.creation.mutations.checkpointRepositories.length,
|
|
696
|
+
});
|
|
697
|
+
if (args.creation.plan.children.length > 0) {
|
|
698
|
+
after.push({
|
|
699
|
+
kind: "creation-children",
|
|
700
|
+
subject: "bound child creation plans",
|
|
701
|
+
digest: createHash("sha256")
|
|
702
|
+
.update(JSON.stringify(args.creation.plan.children.map((child) => child.logical_replay_key).sort()))
|
|
703
|
+
.digest("hex"),
|
|
704
|
+
count: args.creation.plan.children.length,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
623
707
|
}
|
|
708
|
+
after.push({ kind: "publication", subject: "not-published" });
|
|
709
|
+
after.push({ kind: "next-sync", subject: "lyt sync" });
|
|
710
|
+
return parseReceiptV1ForEmission({
|
|
711
|
+
schema_id: "lyt.receipt",
|
|
712
|
+
schema_version: { major: 1, minor: 0 },
|
|
713
|
+
operation_id: args.operationId,
|
|
714
|
+
attempt_id: args.attemptId,
|
|
715
|
+
operation: args.operation ?? "lyt-init",
|
|
716
|
+
scope: { kind: "system" },
|
|
717
|
+
timestamps: { started_at: args.startedAt, finished_at: new Date().toISOString() },
|
|
718
|
+
replay: {
|
|
719
|
+
disposition: refused ? "rejected" : "new",
|
|
720
|
+
key_digest: typeof args.logicalKey === "string" && /^sha256:[a-f0-9]{64}$/i.test(args.logicalKey)
|
|
721
|
+
? args.logicalKey.slice("sha256:".length).toLowerCase()
|
|
722
|
+
: createHash("sha256").update(JSON.stringify(args.logicalKey)).digest("hex"),
|
|
723
|
+
},
|
|
724
|
+
status: args.status,
|
|
725
|
+
exit_code: args.exitCode ?? (args.status === "success" || args.status === "no-op" ? 0 : 2),
|
|
726
|
+
mutations: { local: args.local, remote: 0 },
|
|
727
|
+
evidence: { before: [], after },
|
|
728
|
+
next_action: args.next ?? null,
|
|
729
|
+
error: args.error ?? null,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
export function makeRootCliParseReceipt() {
|
|
733
|
+
const operationId = uuid7BytesToDashedString(newUuidv7Bytes());
|
|
734
|
+
const attemptId = uuid7BytesToDashedString(newUuidv7Bytes());
|
|
735
|
+
return makeRootInitReceipt({
|
|
736
|
+
operation: "cli-parse",
|
|
737
|
+
operationId,
|
|
738
|
+
attemptId,
|
|
739
|
+
startedAt: new Date().toISOString(),
|
|
740
|
+
logicalKey: { command: "lyt", outcome: "parser-refusal" },
|
|
741
|
+
status: "refused",
|
|
742
|
+
local: 0,
|
|
743
|
+
error: {
|
|
744
|
+
code: "invalid-cli-arguments",
|
|
745
|
+
summary: "Lyt command arguments were refused.",
|
|
746
|
+
retryable: false,
|
|
747
|
+
},
|
|
748
|
+
next: {
|
|
749
|
+
code: "correct-cli-arguments",
|
|
750
|
+
summary: "Correct the command arguments and retry.",
|
|
751
|
+
},
|
|
752
|
+
exitCode: 2,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
function emitRootInitReceipt(receipt) {
|
|
756
|
+
process.stdout.write(`${JSON.stringify(receipt)}\n`);
|
|
757
|
+
}
|
|
758
|
+
function emitError(json, body, identity) {
|
|
759
|
+
const message = String(body["message"] ?? body["error"] ?? "Lyt initialization failed.");
|
|
760
|
+
console.error(json ? JSON.stringify(body) : `lyt init: ${message}`);
|
|
761
|
+
const operationId = identity?.operationId ?? uuid7BytesToDashedString(newUuidv7Bytes());
|
|
762
|
+
const attemptId = identity?.attemptId ?? uuid7BytesToDashedString(newUuidv7Bytes());
|
|
763
|
+
const code = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(String(body["error"]))
|
|
764
|
+
? String(body["error"])
|
|
765
|
+
: "lyt-init-refused";
|
|
766
|
+
emitRootInitReceipt(makeRootInitReceipt({
|
|
767
|
+
operationId,
|
|
768
|
+
attemptId,
|
|
769
|
+
startedAt: identity?.startedAt ?? new Date().toISOString(),
|
|
770
|
+
logicalKey: { error: code },
|
|
771
|
+
status: "refused",
|
|
772
|
+
local: 0,
|
|
773
|
+
error: {
|
|
774
|
+
code,
|
|
775
|
+
summary: "Lyt initialization request was refused.",
|
|
776
|
+
retryable: false,
|
|
777
|
+
},
|
|
778
|
+
next: {
|
|
779
|
+
code: "correct-lyt-init",
|
|
780
|
+
summary: "Correct the reported init arguments and retry.",
|
|
781
|
+
},
|
|
782
|
+
exitCode: Number(process.exitCode ?? 2) || 2,
|
|
783
|
+
}));
|
|
624
784
|
}
|
|
625
785
|
//# sourceMappingURL=init.js.map
|