yaml-flow 6.0.0 → 7.0.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 (162) hide show
  1. package/board-live-cards-cli.js +4 -4
  2. package/browser/asset-integrity.json +3 -3
  3. package/browser/board-livecards-client.js +2 -0
  4. package/browser/board-livecards-client.js.map +1 -0
  5. package/browser/board-livecards-localstorage.js +10 -0
  6. package/browser/board-livecards-localstorage.js.map +1 -0
  7. package/browser/board-livegraph-engine.js +2 -2
  8. package/browser/board-livegraph-engine.js.map +1 -1
  9. package/browser/card-compute.js +28 -28
  10. package/browser/compute-jsonata.js +5 -0
  11. package/browser/compute-jsonata.js.map +1 -0
  12. package/browser/live-cards.js +261 -150
  13. package/card-store.js +4 -4
  14. package/dist/{board-live-cards-public-CltXYgaY.d.cts → board-live-cards-public-CW5074xr.d.cts} +9 -5
  15. package/dist/{board-live-cards-public-f-E-FAyp.d.ts → board-live-cards-public-hnZo0mAf.d.ts} +9 -5
  16. package/dist/board-livegraph-runtime/index.cjs +2 -2
  17. package/dist/board-livegraph-runtime/index.cjs.map +1 -1
  18. package/dist/board-livegraph-runtime/index.d.cts +11 -9
  19. package/dist/board-livegraph-runtime/index.d.ts +11 -9
  20. package/dist/board-livegraph-runtime/index.js +2 -2
  21. package/dist/board-livegraph-runtime/index.js.map +1 -1
  22. package/dist/board-livegraph-runtime/jsonata-sync.cjs +37 -1
  23. package/dist/card-compute/index.cjs +4 -4
  24. package/dist/card-compute/index.cjs.map +1 -1
  25. package/dist/card-compute/index.d.cts +5 -1
  26. package/dist/card-compute/index.d.ts +5 -1
  27. package/dist/card-compute/index.js +4 -4
  28. package/dist/card-compute/index.js.map +1 -1
  29. package/dist/card-compute/jsonata-sync.cjs +37 -1
  30. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -1
  31. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -1
  32. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +27 -14
  33. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +27 -14
  34. package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -1
  35. package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -1
  36. package/dist/cli/browser-api/card-store-browser-api.cjs +1 -1
  37. package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -1
  38. package/dist/cli/browser-api/card-store-browser-api.js +1 -1
  39. package/dist/cli/browser-api/card-store-browser-api.js.map +1 -1
  40. package/dist/cli/browser-api/jsonata-sync.cjs +37 -1
  41. package/dist/cli/node/artifacts-store-cli.cjs +8 -8
  42. package/dist/cli/node/artifacts-store-cli.cjs.map +1 -1
  43. package/dist/cli/node/artifacts-store-cli.js +8 -8
  44. package/dist/cli/node/artifacts-store-cli.js.map +1 -1
  45. package/dist/cli/node/board-live-cards-cli.cjs +7 -7
  46. package/dist/cli/node/board-live-cards-cli.cjs.map +1 -1
  47. package/dist/cli/node/board-live-cards-cli.js +7 -7
  48. package/dist/cli/node/board-live-cards-cli.js.map +1 -1
  49. package/dist/cli/node/card-store-cli.cjs +5 -5
  50. package/dist/cli/node/card-store-cli.cjs.map +1 -1
  51. package/dist/cli/node/card-store-cli.js +5 -5
  52. package/dist/cli/node/card-store-cli.js.map +1 -1
  53. package/dist/cli/node/execution-adapter.cjs +3 -0
  54. package/dist/cli/node/execution-adapter.cjs.map +1 -0
  55. package/dist/cli/node/execution-adapter.d.cts +174 -0
  56. package/dist/cli/node/execution-adapter.d.ts +174 -0
  57. package/dist/cli/node/execution-adapter.js +3 -0
  58. package/dist/cli/node/execution-adapter.js.map +1 -0
  59. package/dist/cli/node/fs-board-adapter.cjs +7 -7
  60. package/dist/cli/node/fs-board-adapter.cjs.map +1 -1
  61. package/dist/cli/node/fs-board-adapter.d.cts +2 -2
  62. package/dist/cli/node/fs-board-adapter.d.ts +2 -2
  63. package/dist/cli/node/fs-board-adapter.js +7 -7
  64. package/dist/cli/node/fs-board-adapter.js.map +1 -1
  65. package/dist/cli/node/jsonata-sync.cjs +37 -1
  66. package/dist/cli/node/source-cli-task-executor.cjs +4 -4
  67. package/dist/cli/node/source-cli-task-executor.cjs.map +1 -1
  68. package/dist/cli/node/source-cli-task-executor.js +4 -4
  69. package/dist/cli/node/source-cli-task-executor.js.map +1 -1
  70. package/dist/continuous-event-graph/index.cjs +2 -2
  71. package/dist/continuous-event-graph/index.cjs.map +1 -1
  72. package/dist/continuous-event-graph/index.js +2 -2
  73. package/dist/continuous-event-graph/index.js.map +1 -1
  74. package/dist/continuous-event-graph/jsonata-sync.cjs +37 -1
  75. package/dist/execution-refs.cjs +2 -1
  76. package/dist/execution-refs.cjs.map +1 -1
  77. package/dist/execution-refs.d.cts +49 -11
  78. package/dist/execution-refs.d.ts +49 -11
  79. package/dist/execution-refs.js +2 -1
  80. package/dist/execution-refs.js.map +1 -1
  81. package/dist/index.cjs +10 -10
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.js +10 -10
  84. package/dist/index.js.map +1 -1
  85. package/dist/jsonata-sync.cjs +37 -1
  86. package/dist/server-runtime/index.cjs +9 -0
  87. package/dist/server-runtime/index.cjs.map +1 -0
  88. package/dist/server-runtime/index.d.cts +31 -0
  89. package/dist/server-runtime/index.d.ts +31 -0
  90. package/dist/server-runtime/index.js +9 -0
  91. package/dist/server-runtime/index.js.map +1 -0
  92. package/dist/server-runtime/jsonata-sync.cjs +7623 -0
  93. package/dist/step-machine-public/index.cjs +2 -0
  94. package/dist/step-machine-public/index.cjs.map +1 -0
  95. package/dist/step-machine-public/index.d.cts +159 -0
  96. package/dist/step-machine-public/index.d.ts +159 -0
  97. package/dist/step-machine-public/index.js +2 -0
  98. package/dist/step-machine-public/index.js.map +1 -0
  99. package/dist/step-machine-public/jsonata-sync.cjs +7623 -0
  100. package/dist/storage-refs.cjs +2 -2
  101. package/dist/storage-refs.cjs.map +1 -1
  102. package/dist/storage-refs.d.cts +7 -6
  103. package/dist/storage-refs.d.ts +7 -6
  104. package/dist/storage-refs.js +2 -2
  105. package/dist/storage-refs.js.map +1 -1
  106. package/dist/types-B1ZRa4aI.d.ts +147 -0
  107. package/dist/types-BxEFcVK9.d.cts +147 -0
  108. package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +9 -10
  109. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.js +357 -0
  110. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-public.js +9 -10
  111. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.js +300 -0
  112. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.py +617 -0
  113. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-sse-worker.js +48 -0
  114. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.py +11 -10
  115. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +19 -4
  116. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +4 -8
  117. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +6 -10
  118. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/poll-status-cli.js +8 -16
  119. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +2 -6
  120. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +4 -8
  121. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +3 -7
  122. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +4 -8
  123. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +7 -16
  124. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +2 -6
  125. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/_board_pycli.py +13 -3
  126. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/add-cards.py +2 -1
  127. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/init-board.py +2 -1
  128. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/poll-status.py +2 -1
  129. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +20 -24
  130. package/examples/cli/step-machine-cli/portfolio-tracker/run-inline-python-demo-pycli.py +0 -3
  131. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +8 -13
  132. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +33 -9
  133. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +3 -1
  134. package/examples/cli/step-machine-demo/step2-double-cli.js +6 -12
  135. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +66 -4
  136. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +13 -5
  137. package/examples/example-board/cards/_index.json +47 -0
  138. package/examples/example-board/cards/card-market-prices.json +33 -9
  139. package/examples/example-board/cards/card-my-identity.json +30 -6
  140. package/examples/example-board/cards/card-portfolio-action.json +24 -6
  141. package/examples/example-board/cards/card-portfolio-intelligence.json +97 -0
  142. package/examples/example-board/cards/card-portfolio-risks.json +24 -6
  143. package/examples/example-board/cards/card-portfolio-value.json +38 -10
  144. package/examples/example-board/cards/card-portfolio.json +57 -13
  145. package/examples/example-board/cards/card-rebalance-impact.json +22 -6
  146. package/examples/example-board/cards/card-rebalance-sim.json +66 -15
  147. package/examples/example-board/demo-server.js +360 -69
  148. package/examples/example-board/demo-shell-localstorage.html +774 -0
  149. package/examples/example-board/demo-shell-with-server.html +18 -36
  150. package/examples/example-board/demo-shell.html +5 -4
  151. package/examples/example-board/demo-task-executor.js +217 -265
  152. package/package.json +15 -13
  153. package/step-machine-cli.js +43 -310
  154. package/board-livecards-server-runtime.js +0 -1513
  155. package/browser/board-livecards-runtime-client.js +0 -263
  156. package/dist/pycli/quickjs-board-runtime.global.js +0 -9
  157. package/dist/pycli/quickjs-board-runtime.global.js.map +0 -1
  158. package/dist/pycli/quickjs-step-machine-runtime.global.js +0 -5
  159. package/dist/pycli/quickjs-step-machine-runtime.global.js.map +0 -1
  160. package/examples/cli/step-machine-demo/two-step-math-handlers.js +0 -32
  161. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +0 -24
  162. package/examples/example-board/demo-shell-browser.html +0 -675
@@ -1,17 +1,19 @@
1
1
  // live-cards.js — LiveCards v3: Node-based Board/Canvas engine
2
2
  //
3
3
  // Schema: Each node has { id } required; all else optional.
4
- // id, meta, card_data, requires, provides, source_defs, compute, view
5
- // Nodes with view render as cards; nodes with source_defs but no view render as source pills in canvas.
6
- // compute[] ordered array of { bindTo, expr } JSONata steps writes to node.computed_values (ephemeral)
7
- // source_defs[] open objects: only bindTo + outputFile matter to the engine; all other fields are
8
- // passed verbatim to the board's task-executor (--in JSON). Users define their own
9
- // shape (kind, url, mailbox, channel, model, ...) per executor.
10
- // requires[] — upstream node IDs; engine subscribes automatically
4
+ // id, meta, card_data, requires, provides, view
5
+ // Nodes with view render as cards; nodes with no view but with source_defs declared on the
6
+ // underlying card definition render as source pills in canvas mode (source_defs are runtime-only;
7
+ // they are not interpreted here).
8
+ // requires[] upstream provider tokens; engine subscribes automatically
11
9
  // provides[] — [{ bindTo, src }] explicit downstream token bindings
10
+ // computed_values — derived values produced by the runtime; rendered as-is, never recomputed here
11
+ //
12
+ // Rendering contract: this module renders derived state only. View bind paths resolve to one of
13
+ // card_data | requires | computed_values | runtime_state. Raw fetched-source payloads stay in the
14
+ // runtime and never reach the Board.
12
15
  //
13
16
  // Uses Bootstrap 5 for layout/forms, optional Chart.js for charts.
14
- // Uses CardCompute (card-compute.js) for declarative compute expressions.
15
17
  //
16
18
  // API:
17
19
  // const engine = LiveCard.init({ resolve, onPatch, onPatchState, onRefresh, onAction, getChatMessages, markdown, sanitize, chartLib });
@@ -24,8 +26,15 @@
24
26
  // engine.appendChatMessage(nodeId, role, text)
25
27
  // engine.registerRenderer(name, fn)
26
28
  //
27
- // const board = LiveCard.Board(engine, el, { nodes, positions?, mode, canvas })
28
- // board.setMode('board'|'canvas'), board.autoLayout(), board.add(node), board.remove(id)
29
+ // Reactive board (preferred): state in, view out. No destructive re-renders.
30
+ // const board = LiveCard.Board(engine, el, { initialState, getNodeIds, selectNode, mode?, canvas? });
31
+ // board.setState(nextState) — diff vs prev; per-node updates only
32
+ // board.destroy()
33
+ //
34
+ // Imperative core (advanced): direct node-list manipulation.
35
+ // const core = LiveCard.BoardCore(engine, el, { nodes, positions?, mode, canvas });
36
+ // core.add(node), core.remove(id), core.reorder(ids), core.updateNode(id, model)
37
+ // core.setMode('board'|'canvas'), core.setDevMode(flag), core.autoLayout(), core.clear(), core.destroy()
29
38
 
30
39
  // eslint-disable-next-line no-unused-vars
31
40
  var LiveCard = (function () {
@@ -230,6 +239,30 @@ var LiveCard = (function () {
230
239
  const _formState = {}; // stateKey → { baseValues, journal }
231
240
  const _notesState = {}; // stateKey → { baseContent, journal|null }
232
241
  const _todoState = {}; // stateKey → { currentState, pending } for todo dirty tracking
242
+
243
+ /**
244
+ * Overlay a "Saving…" spinner over `el` while a patch is in-flight.
245
+ * The overlay is removed automatically on the next SSE re-render because
246
+ * every editable renderer does `el.innerHTML = …` on refresh.
247
+ */
248
+ function _showSavingOverlay(el) {
249
+ // Ensure the container is a positioned ancestor so the overlay can fill it.
250
+ if (getComputedStyle(el).position === 'static') el.style.position = 'relative';
251
+ const overlay = document.createElement('div');
252
+ overlay.className = 'lc-saving-overlay';
253
+ overlay.setAttribute('aria-live', 'polite');
254
+ overlay.style.cssText = [
255
+ 'position:absolute', 'inset:0',
256
+ 'background:rgba(255,255,255,0.78)',
257
+ 'display:flex', 'align-items:center', 'justify-content:center',
258
+ 'gap:0.5rem', 'z-index:20', 'border-radius:inherit',
259
+ 'pointer-events:all', // blocks all clicks on underlying inputs
260
+ ].join(';');
261
+ overlay.innerHTML =
262
+ '<span class="spinner-border spinner-border-sm text-primary" role="status" aria-hidden="true"></span>' +
263
+ '<span class="text-primary fw-medium small">Saving…</span>';
264
+ el.appendChild(overlay);
265
+ }
233
266
  const _renderers = {}; // kind → fn
234
267
  const _nodeEls = {}; // nodeId → { container, resultEl, uid }
235
268
  const _chatModal = {
@@ -275,11 +308,6 @@ var LiveCard = (function () {
275
308
  return _cleanup[id];
276
309
  }
277
310
 
278
- function _runCompute() {
279
- // Runtime payload is authoritative; UI never recomputes derived values.
280
- return Promise.resolve();
281
- }
282
-
283
311
  function _ensureChatModal() {
284
312
  if (_chatModal.backdrop) return;
285
313
 
@@ -767,11 +795,9 @@ var LiveCard = (function () {
767
795
  const ns = {
768
796
  card: node && node.card ? node.card : {},
769
797
  card_data: node && node.card_data ? node.card_data : {},
770
- fetched_sources: node && node.fetched_sources ? node.fetched_sources : {},
771
798
  requires: node && node.requires ? node.requires : {},
772
799
  computed_values: node && node.computed_values ? node.computed_values : {},
773
800
  runtime_state: node && node.runtime_state ? node.runtime_state : {},
774
- data_objects: node && node.data_objects ? node.data_objects : {},
775
801
  };
776
802
 
777
803
  if (!Object.prototype.hasOwnProperty.call(ns, root)) return undefined;
@@ -1181,6 +1207,7 @@ var LiveCard = (function () {
1181
1207
  const nextValues = getEffectiveValues();
1182
1208
  cfg.onPatchState(node.id, { fieldValues: nextValues });
1183
1209
  btn.textContent = 'Saving...';
1210
+ _showSavingOverlay(el);
1184
1211
  }, { signal });
1185
1212
 
1186
1213
  discardBtn.addEventListener('click', () => {
@@ -1259,6 +1286,7 @@ var LiveCard = (function () {
1259
1286
  const nextValue = textarea.value;
1260
1287
  cfg.onPatchState(node.id, { notes: nextValue });
1261
1288
  saveBtn.textContent = 'Saving...';
1289
+ _showSavingOverlay(el);
1262
1290
  }, { signal });
1263
1291
 
1264
1292
  discardBtn.addEventListener('click', () => {
@@ -1343,6 +1371,7 @@ var LiveCard = (function () {
1343
1371
  cfg.onPatchState(node.id, { fieldValues: rows });
1344
1372
  const saveBtn = el.querySelector('.lc-et-save');
1345
1373
  if (saveBtn) saveBtn.textContent = 'Saving...';
1374
+ _showSavingOverlay(el);
1346
1375
  }
1347
1376
 
1348
1377
  function commitDiscard() {
@@ -1961,11 +1990,11 @@ var LiveCard = (function () {
1961
1990
  //
1962
1991
  // Usage:
1963
1992
  // { "kind": "ref",
1964
- // "data": { "bind": "fetched_sources.rebalance.proposed_trades",
1993
+ // "data": { "bind": "computed_values.proposed_trades",
1965
1994
  // "viewBind": "card_data.display_mode",
1966
1995
  // "fallbackKind": "table" } }
1967
1996
  //
1968
- // viewBind can point to any namespace: card_data, fetched_sources, requires, computed_values.
1997
+ // viewBind can point to any namespace: card_data, requires, computed_values, runtime_state.
1969
1998
  // If the resolved view object contains a "bind" sub-path, that overrides data.bind.
1970
1999
  const _REF_KIND_WHITELIST = new Set([
1971
2000
  'table','editable-table','chart','metric','list','badge',
@@ -2180,7 +2209,7 @@ var LiveCard = (function () {
2180
2209
  if (node.card_data && node.card_data.status === 'error' && node.card_data.error) {
2181
2210
  resultEl.innerHTML = `<div class="text-danger small fw-semibold">Refresh failed</div><pre class="text-muted small mt-1" style="white-space:pre-wrap">${_esc(node.card_data.error)}</pre>`;
2182
2211
  } else {
2183
- _runCompute(node).then(function () { _renderElements(node, resultEl); });
2212
+ _renderElements(node, resultEl);
2184
2213
  }
2185
2214
 
2186
2215
  // ---- Wire refresh ----
@@ -2275,7 +2304,7 @@ var LiveCard = (function () {
2275
2304
  if (node.card_data.status === 'error' && node.card_data.error) {
2276
2305
  info.resultEl.innerHTML = `<div class="text-danger small fw-semibold">Refresh failed</div><pre class="text-muted small mt-1" style="white-space:pre-wrap">${_esc(node.card_data.error)}</pre>`;
2277
2306
  } else {
2278
- _runCompute(node).then(function () { _renderElements(node, info.resultEl); });
2307
+ _renderElements(node, info.resultEl);
2279
2308
  }
2280
2309
  }
2281
2310
 
@@ -2354,10 +2383,11 @@ var LiveCard = (function () {
2354
2383
  }
2355
2384
 
2356
2385
  // ===========================================================================
2357
- // Board — grid (board) and DAG (canvas) modes
2386
+ // BoardCoreimperative grid (board) and DAG (canvas) modes.
2387
+ // Most callers should use Board (reactive wrapper) instead.
2358
2388
  // ===========================================================================
2359
2389
 
2360
- function Board(engine, containerEl, opts) {
2390
+ function BoardCore(engine, containerEl, opts) {
2361
2391
  opts = opts || {};
2362
2392
  const mode = { current: opts.mode || 'board' };
2363
2393
  const devMode = { current: opts.devMode || false };
@@ -2409,20 +2439,18 @@ var LiveCard = (function () {
2409
2439
 
2410
2440
  const canvasEl = document.createElement('div');
2411
2441
  canvasEl.className = 'lc-canvas';
2412
- const canvasHeight = co.height || '600px';
2413
- const canvasOverflow = co.overflow || 'auto';
2414
- canvasEl.style.cssText = 'position:relative;overflow:' + canvasOverflow + ';width:100%;height:' + canvasHeight + ';';
2442
+ canvasEl.style.cssText = 'position:relative;overflow:auto;width:100%;';
2415
2443
  const canvasInner = document.createElement('div');
2416
2444
  canvasInner.className = 'lc-canvas-inner';
2417
- canvasInner.style.cssText = 'position:absolute;top:0;left:0;transform-origin:0 0;';
2445
+ canvasInner.style.cssText = 'position:relative;transform-origin:0 0;min-width:100%;min-height:100%;';
2418
2446
  canvasEl.appendChild(canvasInner);
2419
2447
 
2420
2448
  // SVG overlay for edges
2421
2449
  const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
2422
2450
  svgEl.setAttribute('class', 'lc-canvas-edges');
2423
- svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:visible;';
2451
+ svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:hidden;z-index:0;';
2424
2452
  const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
2425
- defs.innerHTML = '<marker id="lc-arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="var(--bs-secondary,#6c757d)"/></marker>';
2453
+ defs.innerHTML = '<marker id="lc-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 1 L 8 5 L 0 9 z" fill="rgba(108,117,125,0.55)"/></marker>';
2426
2454
  svgEl.appendChild(defs);
2427
2455
  canvasInner.appendChild(svgEl);
2428
2456
 
@@ -2433,13 +2461,13 @@ var LiveCard = (function () {
2433
2461
  s.textContent = `
2434
2462
  .lc-canvas-card { position:absolute; min-width:${cvs.minWidth}px; cursor:grab; user-select:none; z-index:1; }
2435
2463
  .lc-canvas-card.lc-dragging { cursor:grabbing; z-index:10; box-shadow:0 8px 24px rgba(0,0,0,0.18)!important; }
2436
- .lc-canvas-card .card-body { overflow:auto; }
2464
+ .lc-canvas-card .card-body { overflow:hidden; }
2437
2465
  .lc-canvas-card.lc-resizing { cursor:nwse-resize; z-index:10; }
2438
2466
  .lc-resize-handle { position:absolute; bottom:0; right:0; width:14px; height:14px; cursor:nwse-resize; z-index:2; opacity:0.4; transition:opacity .15s; }
2439
2467
  .lc-resize-handle:hover { opacity:1; }
2440
2468
  .lc-resize-handle::after { content:''; position:absolute; bottom:3px; right:3px; width:8px; height:8px; border-right:2px solid var(--bs-secondary,#6c757d); border-bottom:2px solid var(--bs-secondary,#6c757d); }
2441
- .lc-canvas-edges path.lc-edge-path { stroke:var(--bs-secondary,#6c757d); stroke-width:1.5; stroke-dasharray:6 4; animation:lc-edge-flow 0.6s linear infinite; }
2442
- .lc-canvas-edges line { stroke:var(--bs-secondary,#6c757d); stroke-width:1.5; }
2469
+ .lc-canvas-edges path.lc-edge-path { stroke:rgba(100,140,200,0.5); stroke-width:2; fill:none; stroke-linecap:round; }
2470
+ .lc-canvas-edges line { stroke:rgba(100,140,200,0.4); stroke-width:2; }
2443
2471
  @keyframes lc-edge-flow { to { stroke-dashoffset:-10; } }
2444
2472
  .lc-source-node { position:absolute; cursor:grab; user-select:none; z-index:1; }
2445
2473
  .lc-source-node.lc-dragging { cursor:grabbing; z-index:10; }
@@ -2545,32 +2573,9 @@ var LiveCard = (function () {
2545
2573
 
2546
2574
  const cardSection = document.createElement('div');
2547
2575
  cardSection.className = 'mb-4';
2548
- cardSection.innerHTML = '<h6 class="fw-semibold mb-2">Card Object (Editable)</h6>';
2549
-
2550
- const editableCardObject = JSON.parse(JSON.stringify((node && node.card) ? node.card : {}));
2551
-
2552
- const editor = document.createElement('textarea');
2553
- editor.className = 'form-control form-control-sm font-monospace';
2554
- editor.rows = 16;
2555
- editor.style.whiteSpace = 'pre';
2556
- editor.value = JSON.stringify(editableCardObject, null, 2);
2557
-
2558
- const editorHint = document.createElement('div');
2559
- editorHint.className = 'small text-muted mt-2';
2560
- editorHint.textContent = 'Edit JSON and click Submit to apply updates to this card.';
2561
-
2562
- const editorError = document.createElement('div');
2563
- editorError.className = 'small text-danger mt-1 d-none';
2564
-
2565
- const submitBtn = document.createElement('button');
2566
- submitBtn.type = 'button';
2567
- submitBtn.className = 'btn btn-primary btn-sm mb-2';
2568
- submitBtn.textContent = 'Submit';
2569
-
2570
- cardSection.appendChild(submitBtn);
2571
- cardSection.appendChild(editor);
2572
- cardSection.appendChild(editorHint);
2573
- cardSection.appendChild(editorError);
2576
+ cardSection.innerHTML = '<h6 class="fw-semibold mb-2">Card Definition (Read-only)</h6>';
2577
+ const cardDef = (node && node.card) ? node.card : {};
2578
+ cardSection.innerHTML += `<pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${_esc(JSON.stringify(cardDef, null, 2))}</pre>`;
2574
2579
  body.appendChild(cardSection);
2575
2580
 
2576
2581
  const computedSection = document.createElement('div');
@@ -2580,13 +2585,6 @@ var LiveCard = (function () {
2580
2585
  computedSection.innerHTML += `<pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${_esc(JSON.stringify(computedValues, null, 2))}</pre>`;
2581
2586
  body.appendChild(computedSection);
2582
2587
 
2583
- const sourcesSection = document.createElement('div');
2584
- sourcesSection.className = 'mb-4';
2585
- sourcesSection.innerHTML = '<h6 class="fw-semibold mb-2">Fetched Sources (Read-only)</h6>';
2586
- const sourcesData = node.fetched_sources || {};
2587
- sourcesSection.innerHTML += `<pre style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">${_esc(JSON.stringify(sourcesData, null, 2))}</pre>`;
2588
- body.appendChild(sourcesSection);
2589
-
2590
2588
  const requiresSection = document.createElement('div');
2591
2589
  requiresSection.className = 'mb-4';
2592
2590
  requiresSection.innerHTML = '<h6 class="fw-semibold mb-2">Requires (Read-only)</h6>';
@@ -2609,43 +2607,6 @@ var LiveCard = (function () {
2609
2607
  closeBtn.textContent = 'Close';
2610
2608
  closeBtn.addEventListener('click', closeModal);
2611
2609
 
2612
- submitBtn.addEventListener('click', function () {
2613
- editorError.classList.add('d-none');
2614
- editorError.textContent = '';
2615
- try {
2616
- const parsed = JSON.parse(editor.value);
2617
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
2618
- throw new Error('Card Object must be a JSON object.');
2619
- }
2620
- if (parsed.id && parsed.id !== node.id) {
2621
- throw new Error('Changing card id is not supported in the inspector.');
2622
- }
2623
-
2624
- const fixedId = node.id;
2625
- const preservedRuntime = {
2626
- card_data: node.card_data,
2627
- fetched_sources: node.fetched_sources,
2628
- requires: node.requires,
2629
- computed_values: node.computed_values,
2630
- runtime_state: node.runtime_state,
2631
- data_objects: node.data_objects,
2632
- };
2633
- node.card = parsed;
2634
- node.id = fixedId;
2635
- Object.assign(node, preservedRuntime);
2636
-
2637
- engine.notify(node.id, { inspector: 'card-object-updated' });
2638
- _render();
2639
-
2640
- submitBtn.textContent = '✓ Saved';
2641
- setTimeout(function () { submitBtn.textContent = 'Submit'; }, 1200);
2642
- closeModal();
2643
- } catch (err) {
2644
- editorError.textContent = 'Invalid JSON: ' + String((err && err.message) || err);
2645
- editorError.classList.remove('d-none');
2646
- }
2647
- });
2648
-
2649
2610
  footer.appendChild(closeBtn);
2650
2611
  content.appendChild(header);
2651
2612
  content.appendChild(body);
@@ -2796,8 +2757,23 @@ var LiveCard = (function () {
2796
2757
 
2797
2758
  // ---- Board mode ----
2798
2759
 
2760
+ // Compute canvas inner size from card positions + padding
2761
+ function _fitCanvasToContent() {
2762
+ var pad = 100;
2763
+ var maxR = 0, maxB = 0;
2764
+ canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(function(el) {
2765
+ var r = el.offsetLeft + el.offsetWidth;
2766
+ var b = el.offsetTop + el.offsetHeight;
2767
+ if (r > maxR) maxR = r;
2768
+ if (b > maxB) maxB = b;
2769
+ });
2770
+ canvasInner.style.width = (maxR + pad) + 'px';
2771
+ canvasInner.style.height = (maxB + pad) + 'px';
2772
+ }
2773
+
2799
2774
  function _renderBoard() {
2800
2775
  _destroyEdges();
2776
+ document.body.style.overflow = '';
2801
2777
  root.innerHTML = '';
2802
2778
  root.appendChild(gridEl);
2803
2779
  gridEl.innerHTML = '';
@@ -2836,13 +2812,13 @@ var LiveCard = (function () {
2836
2812
  */
2837
2813
  function _updateTokenAvailability() {
2838
2814
  var tokenMap = _buildTokenMap();
2839
- // Determine which nodes "have data" (non-empty card_data or fetched_sources, or status=fresh/completed)
2815
+ // A node "has data" when card_data or computed_values is non-empty, or status is fresh/completed.
2840
2816
  var nodeHasData = {};
2841
2817
  nodeList.forEach(function(node) {
2842
2818
  var cd = node.card_data || (node.card && node.card.card_data);
2843
- var fs = node.fetched_sources;
2819
+ var cv = node.computed_values;
2844
2820
  var status = cd && cd.status;
2845
- var hasOutput = (cd && Object.keys(cd).length > 0) || (fs && Object.keys(fs).length > 0);
2821
+ var hasOutput = (cd && Object.keys(cd).length > 0) || (cv && Object.keys(cv).length > 0);
2846
2822
  nodeHasData[node.id] = hasOutput || status === 'fresh' || status === 'completed';
2847
2823
  });
2848
2824
 
@@ -2881,41 +2857,7 @@ var LiveCard = (function () {
2881
2857
  // Build token → provider nodeId map
2882
2858
  var tokenMap = _buildTokenMap();
2883
2859
 
2884
- // For each consumer node, for each required token, draw edge from
2885
- // the provider's "provides" badge → the consumer's "requires" badge.
2886
- if (typeof LeaderLine !== 'undefined') {
2887
- nodeList.forEach(function(node) {
2888
- var tgtInfo = nodeMap[node.id];
2889
- if (!tgtInfo || !tgtInfo.colEl) return;
2890
- _getRequires(node).forEach(function(token) {
2891
- var srcId = tokenMap[token];
2892
- if (!srcId) return;
2893
- var srcInfo = nodeMap[srcId];
2894
- if (!srcInfo || !srcInfo.colEl) return;
2895
- // Find the specific gem elements for this token
2896
- var srcGem = srcInfo.colEl.querySelector('.lc-token-gem-provides[data-token="' + token + '"]');
2897
- var tgtGem = tgtInfo.colEl.querySelector('.lc-token-gem-requires[data-token="' + token + '"]');
2898
- var startEl = srcGem || srcInfo.colEl;
2899
- var endEl = tgtGem || tgtInfo.colEl;
2900
- try {
2901
- var lineOpts = {
2902
- color: edgeCfg.color,
2903
- size: edgeCfg.size,
2904
- endPlug: edgeCfg.endPlug,
2905
- startSocket: srcGem ? 'bottom' : 'right',
2906
- endSocket: tgtGem ? 'top' : 'left',
2907
- };
2908
- if (edgeCfg.dash) {
2909
- lineOpts.dash = edgeCfg.animation ? { animation: true } : true;
2910
- }
2911
- _edges.push(new LeaderLine(startEl, endEl, lineOpts));
2912
- } catch(e) { /* skip edge on error */ }
2913
- });
2914
- });
2915
- return;
2916
- }
2917
-
2918
- // SVG fallback — connect badge-to-badge with curved paths
2860
+ // SVG edges rendered behind cards (z-index:0) for a clean look
2919
2861
  nodeList.forEach(function(node) {
2920
2862
  var tgtInfo = nodeMap[node.id];
2921
2863
  if (!tgtInfo || !tgtInfo.colEl) return;
@@ -2947,8 +2889,26 @@ var LiveCard = (function () {
2947
2889
  tx = tEl.offsetLeft + tEl.offsetWidth / 2;
2948
2890
  ty = tEl.offsetTop;
2949
2891
  }
2950
- var cpOffset = Math.min(Math.abs(ty - sy) * 0.5, 80);
2951
- var d = 'M ' + sx + ' ' + sy + ' C ' + sx + ' ' + (sy + cpOffset) + ', ' + tx + ' ' + (ty - cpOffset) + ', ' + tx + ' ' + ty;
2892
+ // Route bezier curves around cards offset control points outward
2893
+ var dx = tx - sx;
2894
+ var dy = ty - sy;
2895
+ var dist = Math.sqrt(dx * dx + dy * dy);
2896
+ var cpLen = Math.max(40, Math.min(dist * 0.4, 120));
2897
+ // Determine if src is roughly above, below, left, or right of target
2898
+ var absDx = Math.abs(dx);
2899
+ var absDy = Math.abs(dy);
2900
+ var cp1x, cp1y, cp2x, cp2y;
2901
+ if (absDy > absDx * 0.4) {
2902
+ // Mostly vertical — curve control points go straight down/up
2903
+ cp1x = sx; cp1y = sy + cpLen;
2904
+ cp2x = tx; cp2y = ty - cpLen;
2905
+ } else {
2906
+ // Mostly horizontal — swing control points outward to avoid overlapping cards
2907
+ var sideSign = dx > 0 ? 1 : -1;
2908
+ cp1x = sx + sideSign * cpLen; cp1y = sy + cpLen * 0.5;
2909
+ cp2x = tx - sideSign * cpLen; cp2y = ty - cpLen * 0.5;
2910
+ }
2911
+ var d = 'M ' + sx + ' ' + sy + ' C ' + cp1x + ' ' + cp1y + ', ' + cp2x + ' ' + cp2y + ', ' + tx + ' ' + ty;
2952
2912
  var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
2953
2913
  path.setAttribute('d', d);
2954
2914
  path.setAttribute('fill', 'none');
@@ -2999,6 +2959,7 @@ var LiveCard = (function () {
2999
2959
  node.card.view.layout.canvas.y = y;
3000
2960
  }
3001
2961
  engine.notify(node.id);
2962
+ _fitCanvasToContent();
3002
2963
  if (_edges.length) _repositionEdges();
3003
2964
  else _drawEdges();
3004
2965
  }, { signal });
@@ -3057,6 +3018,7 @@ var LiveCard = (function () {
3057
3018
  node.card.view.layout.canvas.h = sh;
3058
3019
  }
3059
3020
  engine.notify(node.id);
3021
+ _fitCanvasToContent();
3060
3022
  if (_edges.length) _repositionEdges();
3061
3023
  else _drawEdges();
3062
3024
  }, { signal });
@@ -3064,8 +3026,12 @@ var LiveCard = (function () {
3064
3026
 
3065
3027
  function _renderCanvas() {
3066
3028
  _destroyEdges();
3029
+ document.body.style.overflow = 'hidden';
3067
3030
  root.innerHTML = '';
3068
3031
  root.appendChild(canvasEl);
3032
+ // Fill remaining viewport height
3033
+ var top = canvasEl.getBoundingClientRect().top;
3034
+ canvasEl.style.height = co.height || ('calc(100vh - ' + top + 'px)');
3069
3035
  canvasInner.querySelectorAll('.lc-canvas-card,.lc-source-node').forEach(el => el.remove());
3070
3036
  svgEl.querySelectorAll('line,path').forEach(function(el) { el.remove(); });
3071
3037
  _initPositions();
@@ -3109,12 +3075,11 @@ var LiveCard = (function () {
3109
3075
 
3110
3076
  _updateTokenAvailability();
3111
3077
 
3112
- // Draw edges use rAF for LeaderLine so elements are laid out first
3113
- if (typeof LeaderLine !== 'undefined') {
3114
- requestAnimationFrame(function() { _drawEdges(); });
3115
- } else {
3078
+ // Fit canvas to content then draw edges
3079
+ requestAnimationFrame(function() {
3080
+ _fitCanvasToContent();
3116
3081
  _drawEdges();
3117
- }
3082
+ });
3118
3083
 
3119
3084
  // Reposition LeaderLine edges on scroll
3120
3085
  canvasEl.addEventListener('scroll', function() { _repositionEdges(); }, { signal, passive: true });
@@ -3226,7 +3191,41 @@ var LiveCard = (function () {
3226
3191
  _render();
3227
3192
  }
3228
3193
 
3229
- function refresh() { _render(); }
3194
+ /**
3195
+ * Per-node update: replace runtime fields on the existing node object in place
3196
+ * and re-render only that node's body. Outer wrapper is rebuilt to pick up
3197
+ * status/badges, but the surrounding column element is reused so layout is stable.
3198
+ * Editable element state is preserved via journal overlays keyed by nodeId:bindPath.
3199
+ */
3200
+ function updateNode(id, model) {
3201
+ const entry = nodeMap[id];
3202
+ if (!entry) throw new Error('updateNode: unknown node id ' + id);
3203
+ const node = entry.node;
3204
+ if (model && typeof model === 'object') {
3205
+ if (model.card !== undefined) node.card = model.card;
3206
+ if (model.card_data !== undefined) node.card_data = model.card_data;
3207
+ if (model.requires !== undefined) node.requires = model.requires;
3208
+ if (model.computed_values !== undefined) node.computed_values = model.computed_values;
3209
+ if (model.runtime_state !== undefined) node.runtime_state = model.runtime_state;
3210
+ }
3211
+ engine.destroy(id);
3212
+ if (mode.current === 'board') {
3213
+ const colEl = entry.colEl;
3214
+ colEl.innerHTML = '';
3215
+ const built = _buildCardWrapper(node);
3216
+ colEl.appendChild(built.wrap);
3217
+ nodeMap[id] = { node, colEl, bodyEl: built.body };
3218
+ engine.render(node, built.body, { showChat });
3219
+ } else {
3220
+ const el = entry.colEl;
3221
+ el.innerHTML = '';
3222
+ const built = _buildCardWrapper(node);
3223
+ while (built.wrap.firstChild) el.appendChild(built.wrap.firstChild);
3224
+ nodeMap[id] = { node, colEl: el, bodyEl: built.body };
3225
+ engine.render(node, built.body, { showChat: false });
3226
+ }
3227
+ _updateTokenAvailability();
3228
+ }
3230
3229
 
3231
3230
  function clear() {
3232
3231
  _destroyEdges();
@@ -3250,6 +3249,7 @@ var LiveCard = (function () {
3250
3249
 
3251
3250
  function destroy() {
3252
3251
  _destroyEdges();
3252
+ document.body.style.overflow = '';
3253
3253
  ac.abort();
3254
3254
  engine.destroyAll();
3255
3255
  nodeList.length = 0;
@@ -3268,7 +3268,7 @@ var LiveCard = (function () {
3268
3268
  add,
3269
3269
  remove,
3270
3270
  reorder,
3271
- refresh,
3271
+ updateNode,
3272
3272
  clear,
3273
3273
  setMode,
3274
3274
  setDevMode,
@@ -3281,9 +3281,120 @@ var LiveCard = (function () {
3281
3281
  };
3282
3282
  }
3283
3283
 
3284
+ // ===========================================================================
3285
+ // Board — reactive host. State in, view out. No destructive re-renders.
3286
+ // ===========================================================================
3287
+
3288
+ function Board(engine, containerEl, opts) {
3289
+ opts = opts || {};
3290
+ const initialState = opts.initialState;
3291
+ const getNodeIds = opts.getNodeIds;
3292
+ const selectNode = opts.selectNode;
3293
+ if (typeof getNodeIds !== 'function' || typeof selectNode !== 'function') {
3294
+ throw new Error('LiveCard.Board requires getNodeIds and selectNode functions');
3295
+ }
3296
+
3297
+ let state = initialState;
3298
+ const prevModelsById = {};
3299
+ const prevFingerprintsById = {};
3300
+
3301
+ function _stableStringify(v) {
3302
+ if (v == null || typeof v !== 'object') return JSON.stringify(v);
3303
+ if (Array.isArray(v)) return '[' + v.map(_stableStringify).join(',') + ']';
3304
+ const keys = Object.keys(v).sort();
3305
+ return '{' + keys.map(k => JSON.stringify(k) + ':' + _stableStringify(v[k])).join(',') + '}';
3306
+ }
3307
+
3308
+ function _modelFingerprint(model) {
3309
+ if (!model || typeof model !== 'object') return 'null';
3310
+ return _stableStringify({
3311
+ card: model.card,
3312
+ card_data: model.card_data,
3313
+ requires: model.requires,
3314
+ computed_values: model.computed_values,
3315
+ runtime_state: model.runtime_state,
3316
+ });
3317
+ }
3318
+
3319
+ const initialIds = getNodeIds(state);
3320
+ const initialNodes = initialIds.map(id => {
3321
+ const m = selectNode(state, id);
3322
+ prevModelsById[id] = m;
3323
+ prevFingerprintsById[id] = _modelFingerprint(m);
3324
+ return m;
3325
+ });
3326
+
3327
+ const coreOpts = {};
3328
+ Object.keys(opts).forEach(k => {
3329
+ if (k === 'initialState' || k === 'getNodeIds' || k === 'selectNode' || k === 'nodes') return;
3330
+ coreOpts[k] = opts[k];
3331
+ });
3332
+ coreOpts.nodes = initialNodes;
3333
+
3334
+ const core = BoardCore(engine, containerEl, coreOpts);
3335
+
3336
+ function _changed(prevFingerprint, nextFingerprint) {
3337
+ return prevFingerprint !== nextFingerprint;
3338
+ }
3339
+
3340
+ function setState(nextStateOrUpdater) {
3341
+ const nextState = (typeof nextStateOrUpdater === 'function')
3342
+ ? nextStateOrUpdater(state)
3343
+ : nextStateOrUpdater;
3344
+ if (nextState === undefined) return;
3345
+
3346
+ state = nextState;
3347
+ const nextIds = getNodeIds(state);
3348
+ const nextSet = new Set(nextIds);
3349
+
3350
+ // Removals
3351
+ Object.keys(prevModelsById).forEach(id => {
3352
+ if (!nextSet.has(id)) {
3353
+ core.remove(id);
3354
+ delete prevModelsById[id];
3355
+ delete prevFingerprintsById[id];
3356
+ }
3357
+ });
3358
+
3359
+ // Additions and per-node updates
3360
+ nextIds.forEach(id => {
3361
+ const next = selectNode(state, id);
3362
+ const prev = prevModelsById[id];
3363
+ const nextFingerprint = _modelFingerprint(next);
3364
+ const prevFingerprint = prevFingerprintsById[id];
3365
+ if (!prev) {
3366
+ core.add(next);
3367
+ } else if (_changed(prevFingerprint, nextFingerprint)) {
3368
+ core.updateNode(id, next);
3369
+ }
3370
+ prevModelsById[id] = next;
3371
+ prevFingerprintsById[id] = nextFingerprint;
3372
+ });
3373
+
3374
+ // Reorder if id sequence differs
3375
+ const currentOrder = core.nodes.map(n => n.id);
3376
+ const orderDiffers = nextIds.length !== currentOrder.length
3377
+ || nextIds.some((id, i) => id !== currentOrder[i]);
3378
+ if (orderDiffers) core.reorder(nextIds);
3379
+ }
3380
+
3381
+ function destroy() {
3382
+ Object.keys(prevModelsById).forEach(k => delete prevModelsById[k]);
3383
+ Object.keys(prevFingerprintsById).forEach(k => delete prevFingerprintsById[k]);
3384
+ core.destroy();
3385
+ }
3386
+
3387
+ return {
3388
+ setState,
3389
+ destroy,
3390
+ core,
3391
+ get state() { return state; },
3392
+ };
3393
+ }
3394
+
3284
3395
  // ===========================================================================
3285
3396
  // Module export
3286
3397
  // ===========================================================================
3287
3398
 
3288
- return { init, Board };
3399
+ return { init, Board, BoardCore };
3289
3400
  })();
package/card-store.js CHANGED
@@ -10,10 +10,7 @@ const distCli = path.join(__dirname, 'dist', 'cli', 'node', 'card-store-cli.js')
10
10
  const srcCli = path.join(__dirname, 'src', 'cli', 'node', 'card-store-cli.ts');
11
11
  const tsxCli = path.join(__dirname, 'node_modules', 'tsx', 'dist', 'cli.mjs');
12
12
 
13
- if (fs.existsSync(distCli)) {
14
- const { cli } = await import(pathToFileUrl(distCli).href);
15
- await cli(process.argv.slice(2));
16
- } else if (fs.existsSync(srcCli)) {
13
+ if (fs.existsSync(srcCli)) {
17
14
  const result = spawnSync(process.execPath, [tsxCli, srcCli, ...process.argv.slice(2)], {
18
15
  stdio: 'inherit',
19
16
  shell: false,
@@ -26,6 +23,9 @@ if (fs.existsSync(distCli)) {
26
23
  }
27
24
 
28
25
  process.exit(result.status ?? 0);
26
+ } else if (fs.existsSync(distCli)) {
27
+ const { cli } = await import(pathToFileUrl(distCli).href);
28
+ await cli(process.argv.slice(2));
29
29
  } else {
30
30
  console.error('[card-store] Could not find dist or src CLI entrypoint.');
31
31
  process.exit(1);