yaml-flow 3.1.0 → 4.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 (149) hide show
  1. package/README.md +81 -20
  2. package/board-live-cards-cli.js +37 -0
  3. package/browser/card-compute.js +132 -431
  4. package/browser/live-cards.js +41 -27
  5. package/browser/live-cards.schema.json +59 -77
  6. package/dist/card-compute/index.cjs +135 -415
  7. package/dist/card-compute/index.cjs.map +1 -1
  8. package/dist/card-compute/index.d.cts +52 -49
  9. package/dist/card-compute/index.d.ts +52 -49
  10. package/dist/card-compute/index.js +134 -415
  11. package/dist/card-compute/index.js.map +1 -1
  12. package/dist/cli/board-live-cards-cli.cjs +2379 -0
  13. package/dist/cli/board-live-cards-cli.cjs.map +1 -0
  14. package/dist/cli/board-live-cards-cli.d.cts +213 -0
  15. package/dist/cli/board-live-cards-cli.d.ts +213 -0
  16. package/dist/cli/board-live-cards-cli.js +2332 -0
  17. package/dist/cli/board-live-cards-cli.js.map +1 -0
  18. package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
  19. package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
  20. package/dist/continuous-event-graph/index.cjs +201 -448
  21. package/dist/continuous-event-graph/index.cjs.map +1 -1
  22. package/dist/continuous-event-graph/index.d.cts +16 -340
  23. package/dist/continuous-event-graph/index.d.ts +16 -340
  24. package/dist/continuous-event-graph/index.js +198 -448
  25. package/dist/continuous-event-graph/index.js.map +1 -1
  26. package/dist/event-graph/index.cjs +4 -4
  27. package/dist/event-graph/index.cjs.map +1 -1
  28. package/dist/event-graph/index.d.cts +5 -5
  29. package/dist/event-graph/index.d.ts +5 -5
  30. package/dist/event-graph/index.js +4 -4
  31. package/dist/event-graph/index.js.map +1 -1
  32. package/dist/index.cjs +278 -533
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +8 -7
  35. package/dist/index.d.ts +8 -7
  36. package/dist/index.js +278 -533
  37. package/dist/index.js.map +1 -1
  38. package/dist/inference/index.cjs +138 -19
  39. package/dist/inference/index.cjs.map +1 -1
  40. package/dist/inference/index.d.cts +2 -2
  41. package/dist/inference/index.d.ts +2 -2
  42. package/dist/inference/index.js +138 -19
  43. package/dist/inference/index.js.map +1 -1
  44. package/dist/journal-BJDjWb5Q.d.cts +343 -0
  45. package/dist/journal-B_2JnBMF.d.ts +343 -0
  46. package/dist/step-machine/index.cjs +18 -1
  47. package/dist/step-machine/index.cjs.map +1 -1
  48. package/dist/step-machine/index.d.cts +2 -2
  49. package/dist/step-machine/index.d.ts +2 -2
  50. package/dist/step-machine/index.js +18 -1
  51. package/dist/step-machine/index.js.map +1 -1
  52. package/dist/stores/file.d.cts +1 -1
  53. package/dist/stores/file.d.ts +1 -1
  54. package/dist/stores/index.d.cts +1 -1
  55. package/dist/stores/index.d.ts +1 -1
  56. package/dist/stores/localStorage.d.cts +1 -1
  57. package/dist/stores/localStorage.d.ts +1 -1
  58. package/dist/stores/memory.d.cts +1 -1
  59. package/dist/stores/memory.d.ts +1 -1
  60. package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
  61. package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
  62. package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
  63. package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
  64. package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
  65. package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
  66. package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
  67. package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
  68. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
  69. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
  70. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
  71. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
  72. package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
  73. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
  74. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +189 -0
  75. package/examples/browser/livecards-browser/index.html +688 -0
  76. package/examples/browser/step-machine-browser/index.html +367 -0
  77. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  78. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  79. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  80. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  81. package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  82. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  83. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  84. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  85. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  86. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  87. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  88. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  89. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  90. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  91. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  92. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  93. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  94. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
  95. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
  96. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
  97. package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
  98. package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
  99. package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
  100. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
  101. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
  102. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
  103. package/examples/index.html +792 -0
  104. package/examples/ingest.js +733 -0
  105. package/examples/npm-libs/batch/batch-step-machine.ts +121 -0
  106. package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +215 -0
  107. package/examples/npm-libs/continuous-event-graph/live-portfolio-dashboard.ts +555 -0
  108. package/examples/npm-libs/continuous-event-graph/portfolio-tracker.ts +287 -0
  109. package/examples/npm-libs/continuous-event-graph/reactive-monitoring.ts +265 -0
  110. package/examples/npm-libs/continuous-event-graph/reactive-pipeline.ts +168 -0
  111. package/examples/npm-libs/continuous-event-graph/soc-incident-board.ts +287 -0
  112. package/examples/npm-libs/continuous-event-graph/stock-dashboard.ts +229 -0
  113. package/examples/npm-libs/event-graph/ci-cd-pipeline.ts +243 -0
  114. package/examples/npm-libs/event-graph/executor-diamond.ts +165 -0
  115. package/examples/npm-libs/event-graph/executor-pipeline.ts +161 -0
  116. package/examples/npm-libs/event-graph/research-pipeline.ts +137 -0
  117. package/examples/npm-libs/flows/ai-conversation.yaml +116 -0
  118. package/examples/npm-libs/flows/order-processing.yaml +143 -0
  119. package/examples/npm-libs/flows/simple-greeting.yaml +54 -0
  120. package/examples/npm-libs/graph-of-graphs/multi-stage-etl.ts +307 -0
  121. package/examples/npm-libs/graph-of-graphs/url-processing-pipeline.ts +254 -0
  122. package/examples/npm-libs/inference/azure-deployment.ts +149 -0
  123. package/examples/npm-libs/inference/copilot-cli.ts +138 -0
  124. package/examples/npm-libs/inference/data-pipeline.ts +145 -0
  125. package/examples/npm-libs/inference/pluggable-adapters.ts +254 -0
  126. package/examples/npm-libs/node/ai-conversation.ts +195 -0
  127. package/examples/npm-libs/node/simple-greeting.ts +101 -0
  128. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  129. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  130. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  131. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  132. package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  133. package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  134. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  135. package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  136. package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  137. package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  138. package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  139. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  140. package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  141. package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  142. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  143. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  144. package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  145. package/package.json +14 -2
  146. package/schema/board-status.schema.json +118 -0
  147. package/schema/flow.schema.json +5 -0
  148. package/schema/live-cards.schema.json +59 -77
  149. package/step-machine-cli.js +674 -0
@@ -1,8 +1,14 @@
1
1
  // live-cards.js — LiveCards v3: Node-based Board/Canvas engine
2
2
  //
3
- // Schema: Each node has { id, type, meta, data, view?, source?, state, compute? }
4
- // type "card" — renderable node with view.elements[]
5
- // type "source" data-only node (no view, shown as pill in canvas)
3
+ // Schema: Each node has { id, state } required; all else optional.
4
+ // id, meta, state, requires, provides, sources, compute, view
5
+ // Nodes with view render as cards; nodes with sources 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
+ // sources[] — 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
11
+ // provides[] — [{ bindTo, src }] explicit downstream token bindings
6
12
  //
7
13
  // Uses Bootstrap 5 for layout/forms, optional Chart.js for charts.
8
14
  // Uses CardCompute (card-compute.js) for declarative compute expressions.
@@ -85,9 +91,15 @@ var LiveCard = (function () {
85
91
  return String(str).replace(/[&<>"']/g, ch => _escMap[ch]);
86
92
  }
87
93
 
94
+ function _pathParts(path) {
95
+ if (!path || typeof path !== 'string') return [];
96
+ // Support both dot notation (a.b.c) and bracket notation (a.b[0].c).
97
+ return path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(Boolean);
98
+ }
99
+
88
100
  function _deepGet(obj, path) {
89
101
  if (!path || !obj) return undefined;
90
- const parts = path.split('.');
102
+ const parts = _pathParts(path);
91
103
  let cur = obj;
92
104
  for (let i = 0; i < parts.length; i++) {
93
105
  if (cur == null) return undefined;
@@ -97,7 +109,8 @@ var LiveCard = (function () {
97
109
  }
98
110
 
99
111
  function _deepSet(obj, path, value) {
100
- const parts = path.split('.');
112
+ const parts = _pathParts(path);
113
+ if (!parts.length) return;
101
114
  let cur = obj;
102
115
  for (let i = 0; i < parts.length - 1; i++) {
103
116
  if (cur[parts[i]] == null || typeof cur[parts[i]] !== 'object') cur[parts[i]] = {};
@@ -187,11 +200,11 @@ var LiveCard = (function () {
187
200
  }
188
201
 
189
202
  function _runCompute(node) {
190
- if (!node.compute) return;
191
- if (typeof CardCompute !== 'undefined') {
192
- try { CardCompute.run(node); }
193
- catch (e) { console.error('LiveCard compute error', node.id, e); }
194
- }
203
+ if (!node.compute || !node.compute.length) return Promise.resolve();
204
+ if (typeof CardCompute === 'undefined') return Promise.resolve();
205
+ return CardCompute.run(node).catch(function (e) {
206
+ console.error('LiveCard compute error', node.id, e);
207
+ });
195
208
  }
196
209
 
197
210
  function _resolveBind(node, bind) {
@@ -213,7 +226,7 @@ var LiveCard = (function () {
213
226
  }
214
227
 
215
228
  function _autoSubscribe(node) {
216
- const requires = (node.data && node.data.requires) || [];
229
+ const requires = node.requires || [];
217
230
  if (!requires.length) return;
218
231
  const cleanup = _getCleanup(node.id);
219
232
  cleanup.unsubs = requires.map(upId => subscribe(upId, () => {
@@ -221,9 +234,10 @@ var LiveCard = (function () {
221
234
  if (!info || !info.resultEl) return;
222
235
  const updated = cfg.resolve(node.id);
223
236
  if (!updated) return;
224
- _runCompute(updated);
225
- _renderElements(updated, info.resultEl);
226
- notify(node.id);
237
+ _runCompute(updated).then(function () {
238
+ _renderElements(updated, info.resultEl);
239
+ notify(node.id);
240
+ });
227
241
  }));
228
242
  }
229
243
 
@@ -1042,8 +1056,8 @@ var LiveCard = (function () {
1042
1056
  const uid = 'lc-' + (node.id || 'x');
1043
1057
  const features = (node.view && node.view.features) || {};
1044
1058
 
1045
- // Run compute before render
1046
- _runCompute(node);
1059
+ // Run compute async before populating elements
1060
+ // (compute is triggered in the else branch below after DOM is ready)
1047
1061
 
1048
1062
  let h = `<div class="lc-card" id="${uid}">`;
1049
1063
 
@@ -1094,7 +1108,7 @@ var LiveCard = (function () {
1094
1108
  } else if (node.state && node.state.status === 'error' && node.state.error) {
1095
1109
  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.state.error)}</pre>`;
1096
1110
  } else {
1097
- _renderElements(node, resultEl);
1111
+ _runCompute(node).then(function () { _renderElements(node, resultEl); });
1098
1112
  }
1099
1113
 
1100
1114
  // ---- Wire refresh ----
@@ -1181,8 +1195,7 @@ var LiveCard = (function () {
1181
1195
  } else if (node.state.status === 'error' && node.state.error) {
1182
1196
  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.state.error)}</pre>`;
1183
1197
  } else {
1184
- _runCompute(node);
1185
- _renderElements(node, info.resultEl);
1198
+ _runCompute(node).then(function () { _renderElements(node, info.resultEl); });
1186
1199
  }
1187
1200
  }
1188
1201
 
@@ -1350,7 +1363,7 @@ var LiveCard = (function () {
1350
1363
  }
1351
1364
 
1352
1365
  function _getRequires(node) {
1353
- return (node.data && node.data.requires) || [];
1366
+ return node.requires || [];
1354
1367
  }
1355
1368
 
1356
1369
  function _buildCardWrapper(node) {
@@ -1361,8 +1374,9 @@ var LiveCard = (function () {
1361
1374
  const title = (node.meta && node.meta.title) || node.id;
1362
1375
  const tags = (node.meta && node.meta.tags) || [];
1363
1376
  let badgeHtml = '';
1364
- if (node.type === 'source' && node.source) {
1365
- badgeHtml = '<span class="badge bg-info text-dark ms-auto">' + _esc(node.source.kind || 'source') + '</span>';
1377
+ if ((node.sources && node.sources.length) && !node.view) {
1378
+ var src = node.sources[0] || {};
1379
+ badgeHtml = '<span class="badge bg-info text-dark ms-auto">' + _esc(src.kind || 'source') + '</span>';
1366
1380
  } else if (tags.length) {
1367
1381
  badgeHtml = tags.map(t => '<span class="badge bg-secondary ms-1">' + _esc(t) + '</span>').join('');
1368
1382
  }
@@ -1379,7 +1393,7 @@ var LiveCard = (function () {
1379
1393
  el.className = 'lc-source-node';
1380
1394
  const status = (node.state && node.state.status) || 'fresh';
1381
1395
  const title = (node.meta && node.meta.title) || node.id;
1382
- const kind = (node.source && node.source.kind) || 'source';
1396
+ const kind = (node.sources && node.sources[0] && node.sources[0].kind) || 'source';
1383
1397
  el.innerHTML = `<div class="lc-source-pill shadow-sm">
1384
1398
  ${_statusDot(status)}
1385
1399
  <span class="fw-medium">${_esc(title)}</span>
@@ -1396,7 +1410,7 @@ var LiveCard = (function () {
1396
1410
  gridEl.innerHTML = '';
1397
1411
 
1398
1412
  // Only card nodes in board mode, sorted by order
1399
- const cards = nodeList.filter(n => n.type === 'card').slice();
1413
+ const cards = nodeList.filter(n => n.view).slice();
1400
1414
  cards.sort((a, b) => {
1401
1415
  const ao = (a.view && a.view.layout && a.view.layout.board && a.view.layout.board.order) || 0;
1402
1416
  const bo = (b.view && b.view.layout && b.view.layout.board && b.view.layout.board.order) || 0;
@@ -1477,7 +1491,7 @@ var LiveCard = (function () {
1477
1491
  el.style.left = x + 'px'; el.style.top = y + 'px';
1478
1492
  // Persist
1479
1493
  _positions[node.id] = Object.assign(_positions[node.id] || {}, { x, y });
1480
- if (node.type === 'card' && node.view) {
1494
+ if (node.view) {
1481
1495
  if (!node.view.layout) node.view.layout = {};
1482
1496
  if (!node.view.layout.canvas) node.view.layout.canvas = {};
1483
1497
  node.view.layout.canvas.x = x;
@@ -1499,7 +1513,7 @@ var LiveCard = (function () {
1499
1513
  nodeList.forEach(node => {
1500
1514
  const pos = _positions[node.id] || { x: 0, y: 0 };
1501
1515
 
1502
- if (node.type === 'source') {
1516
+ if (!node.view && (node.sources && node.sources.length)) {
1503
1517
  const el = _buildSourcePill(node);
1504
1518
  el.dataset.nodeId = node.id;
1505
1519
  el.style.left = pos.x + 'px';
@@ -1596,7 +1610,7 @@ var LiveCard = (function () {
1596
1610
  w: (_positions[n.id] && _positions[n.id].w) || cvs.defaultW,
1597
1611
  };
1598
1612
  // Sync to card nodes
1599
- if (n.type === 'card' && n.view) {
1613
+ if (n.view) {
1600
1614
  if (!n.view.layout) n.view.layout = {};
1601
1615
  n.view.layout.canvas = Object.assign({}, _positions[n.id]);
1602
1616
  }
@@ -7,9 +7,9 @@
7
7
  "definitions": {
8
8
 
9
9
  "bind_ref": {
10
- "description": "A state path reference, e.g. 'state.raw_quotes' or 'state.compute_vars.total'",
10
+ "description": "A state path reference, e.g. 'state.raw_quotes' or 'requires.upstream'",
11
11
  "type": "string",
12
- "pattern": "^state\\."
12
+ "pattern": "^(state|requires|computed_values)\\."
13
13
  },
14
14
 
15
15
  "bind_or_literal": {
@@ -72,39 +72,46 @@
72
72
  }
73
73
  },
74
74
 
75
- "data": {
75
+ "requires": {
76
+ "type": "array",
77
+ "items": { "type": "string" },
78
+ "description": "IDs of upstream nodes this node depends on"
79
+ },
80
+
81
+ "provides": {
82
+ "type": "array",
83
+ "items": {
84
+ "type": "object",
85
+ "required": ["bindTo", "src"],
86
+ "properties": {
87
+ "bindTo": { "type": "string", "description": "Token name published downstream" },
88
+ "src": { "type": "string", "description": "Path to read value from (state.*, requires.*, computed_values.*)" }
89
+ }
90
+ },
91
+ "description": "Explicit bindings exposing computed/state values downstream as named tokens"
92
+ },
93
+
94
+ "compute_step": {
95
+ "description": "A single ordered compute step: reads state.*/requires.*/computed_values.*, writes to computed_values[bindTo]",
76
96
  "type": "object",
97
+ "required": ["bindTo", "expr"],
77
98
  "properties": {
78
- "requires": {
79
- "type": "array",
80
- "items": { "type": "string" },
81
- "description": "IDs of upstream nodes this node depends on"
82
- },
83
- "provides": {
84
- "type": "object",
85
- "description": "Subset of state exposed downstream. Keys are published names, values are bind refs.",
86
- "additionalProperties": { "$ref": "#/definitions/bind_or_literal" }
87
- }
99
+ "bindTo": { "type": "string", "description": "Key in computed_values to write result" },
100
+ "expr": { "type": "string", "description": "JSONata expression evaluated against { state, requires, sources, computed_values }" }
88
101
  }
89
102
  },
90
103
 
91
104
  "source_def": {
105
+ "description": "One source entry. The engine only cares about 'bindTo' (compute namespace key) and 'outputFile' (delivery signal). Every other property is yours — add whatever your task-executor needs: kind, url, headers, mailbox, channel, model, query, etc. The full object is passed verbatim as the --in JSON to the executor.",
92
106
  "type": "object",
93
- "required": ["kind", "bindTo"],
107
+ "required": ["bindTo"],
108
+ "additionalProperties": true,
94
109
  "properties": {
95
- "kind": { "enum": ["api", "websocket", "static", "llm"] },
96
- "bindTo": { "$ref": "#/definitions/bind_ref", "description": "state path to write fetched data into" },
97
- "method": { "enum": ["GET", "POST", "PUT", "DELETE"], "default": "GET" },
98
- "url_template": { "type": "string", "description": "URL with {{var}} placeholders" },
99
- "headers": { "type": "object", "additionalProperties": { "$ref": "#/definitions/bind_or_literal" } },
100
- "body_template": { "type": "object", "description": "Request body with {{var}} placeholders or bind refs" },
101
- "template_vars": {
102
- "type": "object",
103
- "additionalProperties": { "$ref": "#/definitions/bind_or_literal" },
104
- "description": "Variables for url/body templates — static values or bind refs"
105
- },
106
- "poll_interval": { "type": "integer", "minimum": 0, "description": "Auto-refresh in seconds (0 = manual)" },
107
- "transform": { "type": "string", "description": "Dot-path to extract from response, e.g. 'data.items'" }
110
+ "bindTo": { "type": "string", "description": "Key under sources.* available in compute expressions" },
111
+ "outputFile": { "type": "string", "description": "Board-relative path the executor writes its JSON result to. Presence of this file signals delivery." },
112
+ "optionalForCompletionGating": { "type": "boolean", "default": false, "description": "When true this source does not gate card completion. Default false when absent, so sources are completion-gating by default." },
113
+ "timeout": { "type": "integer", "minimum": 0, "default": 120000, "description": "Executor/script timeout in ms. Default: 120 000 (2 min)." },
114
+ "script": { "type": "string", "description": "Legacy direct-run: shell command executed when no .task-executor is registered. stdout is captured as the result." }
108
115
  }
109
116
  },
110
117
 
@@ -209,58 +216,33 @@
209
216
  }
210
217
  },
211
218
 
212
- "oneOf": [
213
- {
214
- "title": "Card",
215
- "description": "A renderable card node with view elements",
216
- "type": "object",
217
- "required": ["id", "type", "view", "state"],
218
- "additionalProperties": false,
219
+ "title": "LiveCard",
220
+ "description": "A unified card node. Behavior depends on which sections are present (sources, compute, view, etc.)",
221
+ "type": "object",
222
+ "required": ["id"],
223
+ "additionalProperties": false,
224
+ "properties": {
225
+ "id": { "type": "string" },
226
+ "requires": { "$ref": "#/definitions/requires" },
227
+ "provides": { "$ref": "#/definitions/provides" },
228
+ "meta": { "$ref": "#/definitions/meta" },
229
+ "view": { "$ref": "#/definitions/view" },
230
+ "state": { "type": "object", "additionalProperties": true,
219
231
  "properties": {
220
- "id": { "type": "string" },
221
- "type": { "const": "card" },
222
- "meta": { "$ref": "#/definitions/meta" },
223
- "data": { "$ref": "#/definitions/data" },
224
- "view": { "$ref": "#/definitions/view" },
225
- "state": { "type": "object", "additionalProperties": true,
226
- "properties": {
227
- "status": { "enum": ["fresh", "stale", "loading", "error"] },
228
- "lastRun": { "type": "string", "format": "date-time" },
229
- "error": { "type": "string" }
230
- }
231
- },
232
- "compute": {
233
- "type": "object",
234
- "description": "Derived state: key = state path to write, value = compute_expr",
235
- "additionalProperties": { "$ref": "#/definitions/compute_expr" }
236
- }
232
+ "status": { "enum": ["fresh", "stale", "loading", "error"] },
233
+ "lastRun": { "type": "string", "format": "date-time" },
234
+ "error": { "type": "string" }
237
235
  }
238
236
  },
239
- {
240
- "title": "ExternalSource",
241
- "description": "A data-only node that fetches from external systems (no view)",
242
- "type": "object",
243
- "required": ["id", "type", "source", "state"],
244
- "additionalProperties": false,
245
- "properties": {
246
- "id": { "type": "string" },
247
- "type": { "const": "source" },
248
- "meta": { "$ref": "#/definitions/meta" },
249
- "data": { "$ref": "#/definitions/data" },
250
- "source": { "$ref": "#/definitions/source_def" },
251
- "state": { "type": "object", "additionalProperties": true,
252
- "properties": {
253
- "status": { "enum": ["fresh", "stale", "loading", "error"] },
254
- "lastRun": { "type": "string", "format": "date-time" },
255
- "error": { "type": "string" }
256
- }
257
- },
258
- "compute": {
259
- "type": "object",
260
- "description": "Derived state: key = state path to write, value = compute_expr",
261
- "additionalProperties": { "$ref": "#/definitions/compute_expr" }
262
- }
263
- }
237
+ "sources": {
238
+ "type": "array",
239
+ "description": "Source entries. Each entry is passed verbatim to the board's .task-executor (registered via init --task-executor) as the --in JSON file. The executor fetches/generates the data and writes JSON to --out. If no executor is registered, the built-in executor runs the entry's 'cli' command directly. Sources gate completion by default. Set optionalForCompletionGating: true for enrichment-only sources that should not block task-completed.",
240
+ "items": { "$ref": "#/definitions/source_def" }
241
+ },
242
+ "compute": {
243
+ "type": "array",
244
+ "description": "Ordered array of compute steps. Each reads state.*/requires.*/sources.*/computed_values.* and writes to ephemeral computed_values[bindTo].",
245
+ "items": { "$ref": "#/definitions/compute_step" }
264
246
  }
265
- ]
247
+ }
266
248
  }