@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.
Files changed (65) hide show
  1. package/README.md +27 -19
  2. package/dist/automator-run.d.ts +1 -0
  3. package/dist/automator-run.d.ts.map +1 -1
  4. package/dist/automator-run.js +5 -3
  5. package/dist/automator-run.js.map +1 -1
  6. package/dist/bench/pod-harness.d.ts.map +1 -1
  7. package/dist/bench/pod-harness.js +49 -1
  8. package/dist/bench/pod-harness.js.map +1 -1
  9. package/dist/build-program.d.ts +10 -1
  10. package/dist/build-program.d.ts.map +1 -1
  11. package/dist/build-program.js +23 -3
  12. package/dist/build-program.js.map +1 -1
  13. package/dist/cli.js +10 -4
  14. package/dist/cli.js.map +1 -1
  15. package/dist/commands/capture.d.ts.map +1 -1
  16. package/dist/commands/capture.js +1 -2
  17. package/dist/commands/capture.js.map +1 -1
  18. package/dist/commands/init.d.ts +2 -0
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +404 -244
  21. package/dist/commands/init.js.map +1 -1
  22. package/dist/commands/install.d.ts +4 -0
  23. package/dist/commands/install.d.ts.map +1 -0
  24. package/dist/commands/install.js +107 -0
  25. package/dist/commands/install.js.map +1 -0
  26. package/dist/commands/outdated.d.ts.map +1 -1
  27. package/dist/commands/outdated.js +19 -5
  28. package/dist/commands/outdated.js.map +1 -1
  29. package/dist/commands/update.d.ts +90 -2
  30. package/dist/commands/update.d.ts.map +1 -1
  31. package/dist/commands/update.js +883 -48
  32. package/dist/commands/update.js.map +1 -1
  33. package/dist/flows/init-bootstrap.d.ts +7 -1
  34. package/dist/flows/init-bootstrap.d.ts.map +1 -1
  35. package/dist/flows/init-bootstrap.js +240 -69
  36. package/dist/flows/init-bootstrap.js.map +1 -1
  37. package/dist/index.d.ts +6 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/install/doctor-health.d.ts +16 -0
  42. package/dist/install/doctor-health.d.ts.map +1 -0
  43. package/dist/install/doctor-health.js +575 -0
  44. package/dist/install/doctor-health.js.map +1 -0
  45. package/dist/install/provider-inventory.d.ts +40 -0
  46. package/dist/install/provider-inventory.d.ts.map +1 -0
  47. package/dist/install/provider-inventory.js +73 -0
  48. package/dist/install/provider-inventory.js.map +1 -0
  49. package/dist/install/reconcile-engine.d.ts +87 -0
  50. package/dist/install/reconcile-engine.d.ts.map +1 -0
  51. package/dist/install/reconcile-engine.js +771 -0
  52. package/dist/install/reconcile-engine.js.map +1 -0
  53. package/dist/install/target-artifacts.d.ts +91 -0
  54. package/dist/install/target-artifacts.d.ts.map +1 -0
  55. package/dist/install/target-artifacts.js +597 -0
  56. package/dist/install/target-artifacts.js.map +1 -0
  57. package/dist/install/update-operation.d.ts +47 -0
  58. package/dist/install/update-operation.d.ts.map +1 -0
  59. package/dist/install/update-operation.js +318 -0
  60. package/dist/install/update-operation.js.map +1 -0
  61. package/dist/install/update-plan.d.ts +132 -0
  62. package/dist/install/update-plan.d.ts.map +1 -0
  63. package/dist/install/update-plan.js +418 -0
  64. package/dist/install/update-plan.js.map +1 -0
  65. package/package.json +16 -6
@@ -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, materializePodLocal, openRegistry, readIdentityCache, reconcilePublishFlow, recoverVaultsFromPodManifest, reconstructionExitCode, ReadlinePromptHandler, renderNextSteps, renderPodCard, resolveAskedState, runWizard, startSpinner, syncPodLedgerFlow, validateMeshName, } from "@younndai/lyt-vault";
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 the 12-phase setup wizard (detect/install Node + gh + agent runtime, gh auth login, install Lyt skills, install agent manual, cross-machine adopt-detect, create personal mesh + first vault + federation repo, pod-map vault, first-use demo). Conflicts with --auto/--custom/--discover/--json.")
28
- .option("--dry-run", "Only valid with --wizard. Walks all 12 wizard phases without spawn invocations or filesystem writes.")
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 noMode = opts.auto !== true &&
32
- opts.custom !== true &&
33
- opts.discover !== true &&
34
- opts.wizard !== true &&
35
- opts.json !== true &&
36
- opts.dryRun !== true;
37
- if (noMode) {
38
- let isFresh;
39
- try {
40
- isFresh = await probeFreshState();
41
- }
42
- catch (err) {
43
- const msg = err instanceof Error ? err.message : String(err);
44
- emitError(false, { error: "first-init-probe-failed", message: msg });
45
- process.exitCode = 1;
46
- return;
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 (isFresh) {
49
- if (process.stdin.isTTY !== true) {
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: "first-init-requires-tty",
52
- message: "lyt init: interactive required for first-init; use --auto for non-interactive bootstrap.",
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.wizard = true;
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
- if (opts.wizard === true) {
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 --wizard is mutually exclusive with --auto, --custom, --discover, and --json.",
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.dryRun !== true && process.stdin.isTTY !== true) {
73
- emitError(false, {
74
- error: "wizard-requires-tty",
75
- message: "lyt init --wizard requires an interactive terminal (or pass --dry-run for a non-TTY phase walk).",
76
- });
77
- process.exitCode = 3;
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
- const handler = opts.dryRun === true ? makeDryRunDefaultsHandler() : new ReadlinePromptHandler();
81
- try {
82
- const result = await runWizard({
83
- promptHandler: handler,
84
- dryRun: opts.dryRun === true,
85
- });
86
- const summaryLines = [
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
- catch (err) {
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-prompt-error",
154
- message: msg,
155
- });
156
- process.exitCode = 1;
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
- const useSpinner = opts.json !== true && mode !== "discover";
161
- const spinner = useSpinner ? startSpinner() : undefined;
162
- const flowArgs = {
163
- mode,
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
- if (refreshed.status === "skipped")
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
- else {
205
- emitHumanResult(result);
206
- if (result.heal !== undefined) {
207
- console.log(summarizeHeal(result.heal));
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
- if (result.branch === "fresh") {
210
- emitAutoPodCard(result);
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
- else if (result.branch === "adopt" && result.adopt !== undefined) {
213
- emitAdoptPodCard(result);
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
- await maybePromptAndPublish(result);
216
- if (mode === "custom") {
217
- await maybeOfferEmbeddings(false);
278
+ if (result.branch === "adopt" && result.adoptError !== undefined) {
279
+ process.exitCode = 1;
218
280
  }
219
- const currency = await checkCurrency({ cacheOnly: true });
220
- if (currency.stale) {
221
- console.log(`\nHeads up: ${formatCurrencyLine(currency)}`);
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
- if (result.branch === "re-init" &&
225
- result.integrityIssues !== undefined &&
226
- result.integrityIssues.length > 0 &&
227
- result.integrityIssues.every((i) => i.status !== "ok")) {
228
- process.exitCode = 1;
229
- }
230
- if (result.branch === "adopt" && result.adoptError !== undefined) {
231
- process.exitCode = 1;
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
- catch (err) {
240
- spinner?.stop();
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
- ? { onSurfaced: async () => void (await markAsked(registryDb, new Date().toISOString())) }
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 emitError(json, body) {
618
- if (json) {
619
- console.error(JSON.stringify(body, null, 2));
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
- else {
622
- console.error(`lyt init: ${String(body["message"] ?? body["error"])}`);
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