@younndai/lyt 0.13.4 → 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 +406 -224
- 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 +8 -1
- package/dist/flows/init-bootstrap.d.ts.map +1 -1
- package/dist/flows/init-bootstrap.js +244 -74
- 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,204 +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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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();
|
|
198
|
+
}
|
|
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;
|
|
206
|
+
return;
|
|
173
207
|
}
|
|
174
|
-
: {}),
|
|
175
|
-
};
|
|
176
|
-
try {
|
|
177
|
-
const result = await initBootstrapFlow(flowArgs);
|
|
178
|
-
spinner?.stop();
|
|
179
|
-
if (opts.json === true) {
|
|
180
|
-
emitJsonResult(result);
|
|
181
208
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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);
|
|
186
251
|
}
|
|
187
|
-
|
|
188
|
-
|
|
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
|
+
}
|
|
189
271
|
}
|
|
190
|
-
|
|
191
|
-
|
|
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;
|
|
192
277
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
await maybeOfferEmbeddings(false);
|
|
278
|
+
if (result.branch === "adopt" && result.adoptError !== undefined) {
|
|
279
|
+
process.exitCode = 1;
|
|
196
280
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
281
|
+
if (result.branch === "adopt" && result.adopt !== undefined) {
|
|
282
|
+
const exitCode = initBootstrapSemanticExitCode(result);
|
|
283
|
+
if (exitCode !== 0)
|
|
284
|
+
process.exitCode = exitCode;
|
|
200
285
|
}
|
|
286
|
+
emitRootInitReceipt(receiptForBootstrapResult({
|
|
287
|
+
result,
|
|
288
|
+
operationId,
|
|
289
|
+
attemptId,
|
|
290
|
+
startedAt,
|
|
291
|
+
mode,
|
|
292
|
+
exitCode: Number(process.exitCode ?? 0),
|
|
293
|
+
}));
|
|
201
294
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
process.exitCode =
|
|
210
|
-
}
|
|
211
|
-
if (result.branch === "adopt" && result.adopt !== undefined) {
|
|
212
|
-
const exitCode = initBootstrapSemanticExitCode(result);
|
|
213
|
-
if (exitCode !== 0)
|
|
214
|
-
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;
|
|
215
303
|
}
|
|
216
304
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
220
|
-
emitError(opts.json === true, {
|
|
221
|
-
error: "init-bootstrap-error",
|
|
222
|
-
message,
|
|
223
|
-
});
|
|
224
|
-
process.exitCode = 2;
|
|
305
|
+
finally {
|
|
306
|
+
console.log = originalLog;
|
|
225
307
|
}
|
|
226
308
|
});
|
|
227
309
|
}
|
|
@@ -478,53 +560,6 @@ function emitAdoptPodCard(res) {
|
|
|
478
560
|
console.log(renderNextSteps({ unpublished: true }));
|
|
479
561
|
console.log("");
|
|
480
562
|
}
|
|
481
|
-
async function maybePromptAndPublish(res) {
|
|
482
|
-
const pub = res.publish;
|
|
483
|
-
if (pub === undefined || pub.skipped)
|
|
484
|
-
return;
|
|
485
|
-
if (isLocalFirstContext()) {
|
|
486
|
-
console.log("\nYour pod is local-only (not connected to GitHub). Run `lyt sync` to connect + back it up.");
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
const vaultCount = pub.vaults.filter((v) => !v.skipped).length;
|
|
490
|
-
if (process.stdin.isTTY !== true) {
|
|
491
|
-
console.log("\nYour pod is staged locally (not published). Run `lyt sync` to publish it to GitHub.");
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
495
|
-
let yes;
|
|
496
|
-
try {
|
|
497
|
-
const ans = (await rl.question(`\nPublish your pod to GitHub now? (pushes your pod + ${vaultCount} vault repo(s)) [Y/n]: `))
|
|
498
|
-
.trim()
|
|
499
|
-
.toLowerCase();
|
|
500
|
-
yes = ans === "" || ans === "y" || ans === "yes";
|
|
501
|
-
}
|
|
502
|
-
finally {
|
|
503
|
-
rl.close();
|
|
504
|
-
}
|
|
505
|
-
if (!yes) {
|
|
506
|
-
console.log("Staged. Run `lyt sync` when you're ready to publish to GitHub.");
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
console.log("Publishing your pod to GitHub…");
|
|
510
|
-
const result = await reconcilePublishFlow({ push: true });
|
|
511
|
-
if (result.skipped) {
|
|
512
|
-
console.log(`Publish skipped — ${result.reason ?? "no pod"}.`);
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
const pushed = result.vaultOutcomes.filter((o) => o.pushed).length;
|
|
516
|
-
if (result.ok) {
|
|
517
|
-
console.log(`✓ Published to GitHub — ${pushed} vault repo(s) + your pod.`);
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
console.log(`⚠ Partial publish — ${pushed} vault(s) pushed; ${result.outboxRemaining} op(s) pending. Re-run \`lyt sync\` to finish (resumable, no data lost).`);
|
|
521
|
-
for (const o of result.vaultOutcomes) {
|
|
522
|
-
if (o.status === "conflict" || o.status === "failed") {
|
|
523
|
-
console.log(` ${o.status}: ${o.vaultName} — ${o.message}`);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
563
|
async function maybeOfferEmbeddings(json) {
|
|
529
564
|
try {
|
|
530
565
|
let askedState;
|
|
@@ -555,7 +590,9 @@ async function maybeOfferEmbeddings(json) {
|
|
|
555
590
|
},
|
|
556
591
|
...(askedState !== undefined ? { askedState } : {}),
|
|
557
592
|
...(registryDb !== undefined
|
|
558
|
-
? {
|
|
593
|
+
? {
|
|
594
|
+
onSurfaced: async () => void (await markAsked(registryDb, new Date().toISOString())),
|
|
595
|
+
}
|
|
559
596
|
: {}),
|
|
560
597
|
});
|
|
561
598
|
}
|
|
@@ -592,12 +629,157 @@ function isLocalFirstContext() {
|
|
|
592
629
|
return true;
|
|
593
630
|
}
|
|
594
631
|
}
|
|
595
|
-
function
|
|
596
|
-
|
|
597
|
-
|
|
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
|
+
});
|
|
598
685
|
}
|
|
599
|
-
|
|
600
|
-
|
|
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
|
+
}
|
|
601
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
|
+
}));
|
|
602
784
|
}
|
|
603
785
|
//# sourceMappingURL=init.js.map
|