@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.
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 +406 -224
  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 +8 -1
  34. package/dist/flows/init-bootstrap.d.ts.map +1 -1
  35. package/dist/flows/init-bootstrap.js +244 -74
  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, reconstructionExitCode, ReadlinePromptHandler, renderNextSteps, renderPodCard, resolveAskedState, runWizard, startSpinner, 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,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 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
- ...(customOverrides !== undefined ? { customOverrides } : {}),
167
- ...(spinner !== undefined
168
- ? {
169
- onPhase: async (op, label) => {
170
- spinner.phase(op, label);
171
- await new Promise((r) => setImmediate(r));
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
- else {
183
- emitHumanResult(result);
184
- if (result.heal !== undefined) {
185
- 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);
186
251
  }
187
- if (result.branch === "fresh") {
188
- 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
+ }
189
271
  }
190
- else if (result.branch === "adopt" && result.adopt !== undefined) {
191
- 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;
192
277
  }
193
- await maybePromptAndPublish(result);
194
- if (mode === "custom") {
195
- await maybeOfferEmbeddings(false);
278
+ if (result.branch === "adopt" && result.adoptError !== undefined) {
279
+ process.exitCode = 1;
196
280
  }
197
- const currency = await checkCurrency({ cacheOnly: true });
198
- if (currency.stale) {
199
- 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;
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
- if (result.branch === "re-init" &&
203
- result.integrityIssues !== undefined &&
204
- result.integrityIssues.length > 0 &&
205
- result.integrityIssues.every((i) => i.status !== "ok")) {
206
- process.exitCode = 1;
207
- }
208
- if (result.branch === "adopt" && result.adoptError !== undefined) {
209
- process.exitCode = 1;
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
- catch (err) {
218
- spinner?.stop();
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
- ? { onSurfaced: async () => void (await markAsked(registryDb, new Date().toISOString())) }
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 emitError(json, body) {
596
- if (json) {
597
- 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
+ });
598
685
  }
599
- else {
600
- 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
+ }
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