yaml-flow 3.1.1 → 5.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 (194) hide show
  1. package/README.md +81 -20
  2. package/board-live-cards-cli.js +37 -0
  3. package/browser/board-livegraph-runtime.js +1453 -0
  4. package/browser/board-livegraph-runtime.js.map +1 -0
  5. package/browser/card-compute.js +153 -433
  6. package/browser/live-cards.js +868 -115
  7. package/browser/live-cards.schema.json +90 -83
  8. package/dist/board-livegraph-runtime/index.cjs +1448 -0
  9. package/dist/board-livegraph-runtime/index.cjs.map +1 -0
  10. package/dist/board-livegraph-runtime/index.d.cts +101 -0
  11. package/dist/board-livegraph-runtime/index.d.ts +101 -0
  12. package/dist/board-livegraph-runtime/index.js +1441 -0
  13. package/dist/board-livegraph-runtime/index.js.map +1 -0
  14. package/dist/card-compute/index.cjs +266 -431
  15. package/dist/card-compute/index.cjs.map +1 -1
  16. package/dist/card-compute/index.d.cts +77 -49
  17. package/dist/card-compute/index.d.ts +77 -49
  18. package/dist/card-compute/index.js +263 -432
  19. package/dist/card-compute/index.js.map +1 -1
  20. package/dist/cli/board-live-cards-cli.cjs +2750 -0
  21. package/dist/cli/board-live-cards-cli.cjs.map +1 -0
  22. package/dist/cli/board-live-cards-cli.d.cts +205 -0
  23. package/dist/cli/board-live-cards-cli.d.ts +205 -0
  24. package/dist/cli/board-live-cards-cli.js +2702 -0
  25. package/dist/cli/board-live-cards-cli.js.map +1 -0
  26. package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
  27. package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
  28. package/dist/continuous-event-graph/index.cjs +258 -464
  29. package/dist/continuous-event-graph/index.cjs.map +1 -1
  30. package/dist/continuous-event-graph/index.d.cts +18 -358
  31. package/dist/continuous-event-graph/index.d.ts +18 -358
  32. package/dist/continuous-event-graph/index.js +255 -464
  33. package/dist/continuous-event-graph/index.js.map +1 -1
  34. package/dist/event-graph/index.cjs +4 -4
  35. package/dist/event-graph/index.cjs.map +1 -1
  36. package/dist/event-graph/index.d.cts +5 -5
  37. package/dist/event-graph/index.d.ts +5 -5
  38. package/dist/event-graph/index.js +4 -4
  39. package/dist/event-graph/index.js.map +1 -1
  40. package/dist/index.cjs +1684 -555
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +26 -7
  43. package/dist/index.d.ts +26 -7
  44. package/dist/index.js +1678 -555
  45. package/dist/index.js.map +1 -1
  46. package/dist/inference/index.cjs +138 -19
  47. package/dist/inference/index.cjs.map +1 -1
  48. package/dist/inference/index.d.cts +2 -2
  49. package/dist/inference/index.d.ts +2 -2
  50. package/dist/inference/index.js +138 -19
  51. package/dist/inference/index.js.map +1 -1
  52. package/dist/journal-DRfJiheM.d.cts +28 -0
  53. package/dist/journal-NLYuqege.d.ts +28 -0
  54. package/dist/live-cards-bridge-Or7fdEJV.d.ts +316 -0
  55. package/dist/live-cards-bridge-vGJ6tMzN.d.cts +316 -0
  56. package/dist/schedule-CMcZe5Ny.d.ts +21 -0
  57. package/dist/schedule-CiucyCan.d.cts +21 -0
  58. package/dist/step-machine/index.cjs +18 -1
  59. package/dist/step-machine/index.cjs.map +1 -1
  60. package/dist/step-machine/index.d.cts +2 -2
  61. package/dist/step-machine/index.d.ts +2 -2
  62. package/dist/step-machine/index.js +18 -1
  63. package/dist/step-machine/index.js.map +1 -1
  64. package/dist/stores/file.d.cts +1 -1
  65. package/dist/stores/file.d.ts +1 -1
  66. package/dist/stores/index.d.cts +1 -1
  67. package/dist/stores/index.d.ts +1 -1
  68. package/dist/stores/localStorage.d.cts +1 -1
  69. package/dist/stores/localStorage.d.ts +1 -1
  70. package/dist/stores/memory.d.cts +1 -1
  71. package/dist/stores/memory.d.ts +1 -1
  72. package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
  73. package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
  74. package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
  75. package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
  76. package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
  77. package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
  78. package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
  79. package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
  80. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
  81. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
  82. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
  83. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
  84. package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
  85. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  86. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
  87. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +217 -0
  88. package/examples/browser/livecards-browser/index.html +41 -0
  89. package/examples/browser/{index.html → step-machine-browser/index.html} +53 -53
  90. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  91. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  92. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  93. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  94. package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  95. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  96. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  97. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  98. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  99. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  100. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  101. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  102. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  103. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  104. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  105. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  106. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  107. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
  108. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
  109. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
  110. package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
  111. package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
  112. package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
  113. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
  114. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
  115. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
  116. package/examples/example-board/board.yaml +23 -0
  117. package/examples/example-board/bootstrap_payload.json +1 -0
  118. package/examples/example-board/cards/card-chain-region-alert.json +39 -0
  119. package/examples/example-board/cards/card-chain-region-totals.json +26 -0
  120. package/examples/example-board/cards/card-chain-top-region.json +24 -0
  121. package/examples/example-board/cards/card-ex-actions.json +32 -0
  122. package/examples/example-board/cards/card-ex-chart.json +30 -0
  123. package/examples/example-board/cards/card-ex-filter.json +36 -0
  124. package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
  125. package/examples/example-board/cards/card-ex-form.json +91 -0
  126. package/examples/example-board/cards/card-ex-list.json +22 -0
  127. package/examples/example-board/cards/card-ex-markdown.json +17 -0
  128. package/examples/example-board/cards/card-ex-metric.json +19 -0
  129. package/examples/example-board/cards/card-ex-narrative.json +36 -0
  130. package/examples/example-board/cards/card-ex-source-http.json +28 -0
  131. package/examples/example-board/cards/card-ex-source.json +21 -0
  132. package/examples/example-board/cards/card-ex-status.json +35 -0
  133. package/examples/example-board/cards/card-ex-table.json +30 -0
  134. package/examples/example-board/cards/card-ex-todo.json +29 -0
  135. package/examples/example-board/demo-chat-handler.js +69 -0
  136. package/examples/example-board/demo-server.js +87 -0
  137. package/examples/example-board/demo-shell-browser.html +806 -0
  138. package/examples/example-board/demo-shell-with-server.html +280 -0
  139. package/examples/example-board/demo-shell.html +62 -0
  140. package/examples/example-board/demo-task-executor.js +255 -0
  141. package/examples/example-board/mock.db +15 -0
  142. package/examples/example-board/reusable-board-runtime-client.js +265 -0
  143. package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
  144. package/examples/example-board/reusable-server-runtime.js +1284 -0
  145. package/examples/index.html +799 -0
  146. package/examples/{batch → npm-libs/batch}/batch-step-machine.ts +1 -1
  147. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-cards-board.ts +18 -18
  148. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-portfolio-dashboard.ts +24 -24
  149. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/portfolio-tracker.ts +1 -1
  150. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-monitoring.ts +1 -1
  151. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-pipeline.ts +1 -1
  152. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/soc-incident-board.ts +1 -1
  153. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/stock-dashboard.ts +1 -1
  154. package/examples/{event-graph → npm-libs/event-graph}/ci-cd-pipeline.ts +1 -1
  155. package/examples/{event-graph → npm-libs/event-graph}/executor-diamond.ts +1 -1
  156. package/examples/{event-graph → npm-libs/event-graph}/executor-pipeline.ts +1 -1
  157. package/examples/{event-graph → npm-libs/event-graph}/research-pipeline.ts +1 -1
  158. package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/multi-stage-etl.ts +1 -1
  159. package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/url-processing-pipeline.ts +1 -1
  160. package/examples/{inference → npm-libs/inference}/azure-deployment.ts +1 -1
  161. package/examples/{inference → npm-libs/inference}/copilot-cli.ts +1 -1
  162. package/examples/{inference → npm-libs/inference}/data-pipeline.ts +1 -1
  163. package/examples/{inference → npm-libs/inference}/pluggable-adapters.ts +1 -1
  164. package/examples/{node → npm-libs/node}/ai-conversation.ts +1 -1
  165. package/examples/{node → npm-libs/node}/simple-greeting.ts +2 -2
  166. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  167. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  168. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  169. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  170. package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  171. package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  172. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  173. package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  174. package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  175. package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  176. package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  177. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  178. package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  179. package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  180. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  181. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  182. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  183. package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  184. package/package.json +27 -2
  185. package/schema/board-status.schema.json +118 -0
  186. package/schema/card-runtime.schema.json +25 -0
  187. package/schema/flow.schema.json +5 -0
  188. package/schema/live-cards.schema.json +90 -83
  189. package/step-machine-cli.js +674 -0
  190. package/browser/ingest-board.js +0 -296
  191. package/examples/ingest.js +0 -733
  192. /package/examples/{flows → npm-libs/flows}/ai-conversation.yaml +0 -0
  193. /package/examples/{flows → npm-libs/flows}/order-processing.yaml +0 -0
  194. /package/examples/{flows → npm-libs/flows}/simple-greeting.yaml +0 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * portfolio-tracker.js
3
+ *
4
+ * Runs the full T0-T4 lifecycle of the portfolio board demo.
5
+ *
6
+ * This is a BLACK-BOX client of board-live-cards CLI.
7
+ * It only calls CLI commands and does NOT inspect board internals.
8
+ *
9
+ * Usage:
10
+ * node portfolio-tracker.js
11
+ * node portfolio-tracker.js --task-executor <path>
12
+ */
13
+
14
+ import * as fs from 'node:fs';
15
+ import * as os from 'node:os';
16
+ import * as path from 'node:path';
17
+ import { spawnSync } from 'node:child_process';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+
22
+ const CARDS_TEMPLATE = path.join(__dirname, 'cards');
23
+ const REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
24
+ const CLI_WRAPPER = path.join(REPO_ROOT, 'board-live-cards-cli.js');
25
+ const CLI_TS = path.join(REPO_ROOT, 'src', 'cli', 'board-live-cards-cli.ts');
26
+ const CLI_JS = path.join(REPO_ROOT, 'dist', 'cli', 'board-live-cards-cli.js');
27
+ const TSX_CLI = path.join(REPO_ROOT, 'node_modules', 'tsx', 'dist', 'cli.mjs');
28
+
29
+ // Keep runtime artifacts out of the repository.
30
+ const RUNTIME_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'portfolio-tracker-'));
31
+ const BOARD = path.join(RUNTIME_ROOT, 'board-runtime');
32
+ const CARDS = path.join(RUNTIME_ROOT, 'cards');
33
+ const TMP_FILE = path.join(BOARD, 'tmp_file1');
34
+
35
+ function parseArgs(argv) {
36
+ let taskExecutor;
37
+ for (let i = 0; i < argv.length; i++) {
38
+ const arg = argv[i];
39
+ if (arg === '--task-executor') {
40
+ const value = argv[i + 1];
41
+ if (!value || value.startsWith('--')) {
42
+ console.error('[ERROR] Missing value for --task-executor');
43
+ process.exit(1);
44
+ }
45
+ taskExecutor = value;
46
+ i += 1;
47
+ continue;
48
+ }
49
+ console.error(`[ERROR] Unknown argument: ${arg}`);
50
+ process.exit(1);
51
+ }
52
+ return { taskExecutor };
53
+ }
54
+
55
+ const options = parseArgs(process.argv.slice(2));
56
+
57
+ console.log(`Runtime root: ${RUNTIME_ROOT}`);
58
+
59
+ function sleep(ms) {
60
+ return new Promise(resolve => setTimeout(resolve, ms));
61
+ }
62
+
63
+ function cliCommand() {
64
+ // Prefer node+tsx CLI on Windows to avoid flashing transient cmd windows.
65
+ if (fs.existsSync(CLI_TS) && fs.existsSync(TSX_CLI)) {
66
+ return { cmd: process.execPath, prefixArgs: [TSX_CLI, CLI_TS] };
67
+ }
68
+ if (fs.existsSync(CLI_WRAPPER)) {
69
+ return { cmd: process.execPath, prefixArgs: [CLI_WRAPPER] };
70
+ }
71
+ if (fs.existsSync(CLI_JS)) {
72
+ return { cmd: process.execPath, prefixArgs: [CLI_JS] };
73
+ }
74
+ return { cmd: process.execPath, prefixArgs: [CLI_WRAPPER] };
75
+ }
76
+
77
+ function runCli(args, capture = false) {
78
+ const { cmd, prefixArgs } = cliCommand();
79
+ const result = spawnSync(cmd, [...prefixArgs, ...args], {
80
+ stdio: capture ? 'pipe' : 'inherit',
81
+ shell: false,
82
+ windowsHide: true,
83
+ env: {
84
+ ...process.env,
85
+ BOARD_LIVE_CARDS_NO_SPAWN: '1',
86
+ },
87
+ encoding: capture ? 'utf-8' : undefined,
88
+ });
89
+
90
+ if (result.error) {
91
+ console.error(`[ERROR] Failed to run CLI ${args[0]}: ${result.error.message}`);
92
+ process.exit(1);
93
+ }
94
+
95
+ if (result.status !== 0) {
96
+ if (capture && result.stdout) process.stdout.write(result.stdout);
97
+ if (capture && result.stderr) process.stderr.write(result.stderr);
98
+ console.error(`\n[ERROR] board-live-cards-cli ${args[0]} exited with status ${result.status}`);
99
+ process.exit(1);
100
+ }
101
+
102
+ return capture ? result.stdout : undefined;
103
+ }
104
+
105
+ function cli(...args) {
106
+ runCli(args, false);
107
+ }
108
+
109
+ function statusText() {
110
+ return runCli(['status', '--rg', BOARD], true) ?? '';
111
+ }
112
+
113
+ async function waitForAllCompleted(label, timeoutMs = 30000, pollMs = 500) {
114
+ const start = Date.now();
115
+ while (Date.now() - start < timeoutMs) {
116
+ const out = statusText();
117
+ const completed = [
118
+ /\bcompleted\s+portfolio-form\b/.test(out),
119
+ /\bcompleted\s+price-fetch\b/.test(out),
120
+ /\bcompleted\s+holdings-table\b/.test(out),
121
+ /\bcompleted\s+portfolio-value\b/.test(out),
122
+ ].every(Boolean);
123
+
124
+ if (completed) {
125
+ console.log(`${label}: all cards completed.`);
126
+ return;
127
+ }
128
+
129
+ await sleep(pollMs);
130
+ }
131
+
132
+ console.error(`[ERROR] ${label}: timed out waiting for all cards to complete.`);
133
+ console.error(statusText());
134
+ process.exit(1);
135
+ }
136
+
137
+ function writePrices(prices) {
138
+ if (!fs.existsSync(BOARD)) {
139
+ fs.mkdirSync(BOARD, { recursive: true });
140
+ }
141
+ fs.writeFileSync(TMP_FILE, JSON.stringify(prices), 'utf-8');
142
+ console.log(`Wrote prices: ${JSON.stringify(prices)}`);
143
+ }
144
+
145
+ function setupRuntimeCards() {
146
+ fs.rmSync(CARDS, { recursive: true, force: true });
147
+ fs.mkdirSync(RUNTIME_ROOT, { recursive: true });
148
+ fs.cpSync(CARDS_TEMPLATE, CARDS, { recursive: true });
149
+ fs.copyFileSync(path.join(__dirname, 'fetch-prices.js'), path.join(RUNTIME_ROOT, 'fetch-prices.js'));
150
+ }
151
+
152
+ (async () => {
153
+ setupRuntimeCards();
154
+
155
+ console.log('\n=== T0: Init board ===');
156
+ fs.rmSync(BOARD, { recursive: true, force: true });
157
+
158
+ if (options.taskExecutor) {
159
+ cli('init', BOARD, '--task-executor', options.taskExecutor);
160
+ } else {
161
+ cli('init', BOARD);
162
+ }
163
+ cli('add-cards', '--rg', BOARD, '--card-glob', path.join(CARDS, '*.json'));
164
+
165
+ console.log('\n--- T0 Status (after add-cards) ---');
166
+ process.stdout.write(statusText());
167
+
168
+ console.log('\n=== T1: Writing market prices ===');
169
+ writePrices({ AAPL: 198.50, MSFT: 425.30, GOOG: 178.90, AMZN: 192.40, TSLA: 168.75 });
170
+ await waitForAllCompleted('T1');
171
+
172
+ console.log('\n--- T1 Status ---');
173
+ process.stdout.write(statusText());
174
+
175
+ console.log('\n=== T2: Adding GOOG (100 shares) ===');
176
+ const portfolioFormPath = path.join(CARDS, 'portfolio-form.json');
177
+ const portfolioFormV2 = {
178
+ id: 'portfolio-form',
179
+ meta: { title: 'Portfolio Holdings Form' },
180
+ provides: [{ bindTo: 'holdings', src: 'card_data.holdings' }],
181
+ card_data: {
182
+ holdings: [
183
+ { symbol: 'AAPL', qty: 50 },
184
+ { symbol: 'MSFT', qty: 30 },
185
+ { symbol: 'GOOG', qty: 100 },
186
+ ],
187
+ },
188
+ view: {
189
+ elements: [
190
+ { kind: 'table', label: 'Holdings', data: { bind: 'card_data.holdings', columns: ['symbol', 'qty'] } },
191
+ ],
192
+ },
193
+ };
194
+ fs.writeFileSync(portfolioFormPath, JSON.stringify(portfolioFormV2, null, 2));
195
+
196
+ cli('update-card', '--rg', BOARD, '--card-id', 'portfolio-form', '--restart');
197
+ await sleep(500);
198
+ writePrices({ AAPL: 198.50, MSFT: 425.30, GOOG: 178.90, AMZN: 192.40, TSLA: 168.75 });
199
+ await waitForAllCompleted('T2');
200
+
201
+ console.log('\n--- T2 Status ---');
202
+ process.stdout.write(statusText());
203
+
204
+ console.log('\n=== T3: Force price refresh — AAPL now 205.00 ===');
205
+ cli('retrigger', '--rg', BOARD, '--task', 'price-fetch');
206
+ await sleep(500);
207
+ writePrices({ AAPL: 205.00, MSFT: 425.30, GOOG: 178.90, AMZN: 192.40, TSLA: 168.75 });
208
+ await waitForAllCompleted('T3');
209
+
210
+ console.log('\n--- T3 Status ---');
211
+ process.stdout.write(statusText());
212
+
213
+ console.log('\n=== T4: Final board status ===');
214
+ process.stdout.write(statusText());
215
+
216
+ console.log('\nPortfolio tracker completed successfully');
217
+ })();
@@ -0,0 +1,41 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>LiveCards Browser Demo Moved</title>
7
+ <meta http-equiv="refresh" content="0; url=../../example-board/demo-shell.html" />
8
+ <style>
9
+ body {
10
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
11
+ background: #f5f1e8;
12
+ color: #1f2937;
13
+ margin: 0;
14
+ min-height: 100vh;
15
+ display: grid;
16
+ place-items: center;
17
+ padding: 24px;
18
+ }
19
+
20
+ main {
21
+ max-width: 680px;
22
+ background: #fffdf8;
23
+ border: 1px solid #d6cdbd;
24
+ border-radius: 16px;
25
+ padding: 28px;
26
+ box-shadow: 0 20px 50px rgba(56, 46, 33, 0.08);
27
+ }
28
+
29
+ a {
30
+ color: #8a3b12;
31
+ }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <main>
36
+ <h1>LiveCards browser demo moved</h1>
37
+ <p>The maintained browser demo is now the example-board shell so there is a single LiveCards example path to keep current.</p>
38
+ <p><a href="../../example-board/demo-shell.html">Open the example-board demo shell</a></p>
39
+ </main>
40
+ </body>
41
+ </html>
@@ -1,58 +1,58 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>yaml-flow Browser Example</title>
7
- <style>
8
- * { box-sizing: border-box; }
9
- body {
10
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
- max-width: 800px;
12
- margin: 40px auto;
13
- padding: 20px;
14
- background: #f5f5f5;
15
- }
16
- h1 { color: #333; }
17
- .card {
18
- background: white;
19
- border-radius: 8px;
20
- padding: 20px;
21
- margin: 20px 0;
22
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
23
- }
24
- .step-list {
25
- display: flex;
26
- gap: 10px;
27
- flex-wrap: wrap;
28
- margin: 10px 0;
29
- }
30
- .step {
31
- padding: 8px 16px;
32
- border-radius: 20px;
33
- background: #e0e0e0;
34
- font-size: 14px;
35
- }
36
- .step.active {
37
- background: #2196F3;
38
- color: white;
39
- }
40
- .step.completed {
41
- background: #4CAF50;
42
- color: white;
43
- }
44
- button {
45
- background: #2196F3;
46
- color: white;
47
- border: none;
48
- padding: 12px 24px;
49
- border-radius: 6px;
50
- cursor: pointer;
51
- font-size: 16px;
52
- margin: 5px;
53
- }
54
- button:hover { background: #1976D2; }
55
- button:disabled { background: #ccc; cursor: not-allowed; }
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Step Machine Browser Example — yaml-flow</title>
7
+ <style>
8
+ * { box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ max-width: 800px;
12
+ margin: 40px auto;
13
+ padding: 20px;
14
+ background: #f5f5f5;
15
+ }
16
+ h1 { color: #333; }
17
+ .card {
18
+ background: white;
19
+ border-radius: 8px;
20
+ padding: 20px;
21
+ margin: 20px 0;
22
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
23
+ }
24
+ .step-list {
25
+ display: flex;
26
+ gap: 10px;
27
+ flex-wrap: wrap;
28
+ margin: 10px 0;
29
+ }
30
+ .step {
31
+ padding: 8px 16px;
32
+ border-radius: 20px;
33
+ background: #e0e0e0;
34
+ font-size: 14px;
35
+ }
36
+ .step.active {
37
+ background: #2196F3;
38
+ color: white;
39
+ }
40
+ .step.completed {
41
+ background: #4CAF50;
42
+ color: white;
43
+ }
44
+ button {
45
+ background: #2196F3;
46
+ color: white;
47
+ border: none;
48
+ padding: 12px 24px;
49
+ border-radius: 6px;
50
+ cursor: pointer;
51
+ font-size: 16px;
52
+ margin: 5px;
53
+ }
54
+ button:hover { background: #1976D2; }
55
+ button:disabled { background: #ccc; cursor: not-allowed; }
56
56
  .log {
57
57
  background: #263238;
58
58
  color: #aed581;
@@ -83,7 +83,7 @@
83
83
  </style>
84
84
  </head>
85
85
  <body>
86
- <h1>🔄 yaml-flow Browser Demo</h1>
86
+ <h1>yaml-flow Step Machine Browser Example</h1>
87
87
 
88
88
  <div class="card">
89
89
  <h2>Simple Greeting Flow</h2>
@@ -0,0 +1,22 @@
1
+ {
2
+ "id": "holdings-table",
3
+ "meta": { "title": "Holdings Table" },
4
+ "requires": ["holdings", "prices"],
5
+ "provides": [{ "bindTo": "table", "src": "computed_values.table" }],
6
+ "card_data": {},
7
+ "compute": [
8
+ {
9
+ "bindTo": "table",
10
+ "expr": "{ \"rows\": $map(requires.holdings, function($h) { { \"symbol\": $h.symbol, \"qty\": $h.qty, \"price\": $lookup(requires.prices, $h.symbol), \"value\": $h.qty * $lookup(requires.prices, $h.symbol) } }) }"
11
+ }
12
+ ],
13
+ "view": {
14
+ "elements": [
15
+ {
16
+ "kind": "table",
17
+ "label": "Portfolio Positions",
18
+ "data": { "bind": "computed_values.table.rows", "columns": ["symbol", "qty", "price", "value"] }
19
+ }
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "id": "portfolio-form",
3
+ "meta": {
4
+ "title": "Portfolio Holdings Form"
5
+ },
6
+ "provides": [
7
+ {
8
+ "bindTo": "holdings",
9
+ "src": "card_data.holdings"
10
+ }
11
+ ],
12
+ "card_data": {
13
+ "holdings": [
14
+ {
15
+ "symbol": "AAPL",
16
+ "qty": 50
17
+ },
18
+ {
19
+ "symbol": "MSFT",
20
+ "qty": 30
21
+ },
22
+ {
23
+ "symbol": "GOOG",
24
+ "qty": 100
25
+ }
26
+ ]
27
+ },
28
+ "view": {
29
+ "elements": [
30
+ {
31
+ "kind": "table",
32
+ "label": "Holdings",
33
+ "data": {
34
+ "bind": "card_data.holdings",
35
+ "columns": [
36
+ "symbol",
37
+ "qty"
38
+ ]
39
+ }
40
+ }
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "portfolio-value",
3
+ "meta": { "title": "Portfolio Total Value" },
4
+ "requires": ["table"],
5
+ "provides": [{ "bindTo": "totalValue", "src": "computed_values.totalValue" }],
6
+ "card_data": {},
7
+ "compute": [
8
+ { "bindTo": "totalValue", "expr": "$sum(requires.table.rows.value)" }
9
+ ],
10
+ "view": {
11
+ "elements": [
12
+ { "kind": "metric", "label": "Total Portfolio Value", "data": { "bind": "computed_values.totalValue" } }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "price-fetch",
3
+ "meta": { "title": "Fetch Market Prices" },
4
+ "requires": ["holdings"],
5
+ "provides": [{ "bindTo": "prices", "src": "fetched_sources.prices" }],
6
+ "card_data": {},
7
+ "sources": [
8
+ { "cli": "node ../fetch-prices.js --tmp-file-name tmp_file1", "bindTo": "prices", "outputFile": "prices.json" }
9
+ ],
10
+ "view": {
11
+ "elements": [
12
+ { "kind": "table", "label": "Market Prices", "data": { "bind": "fetched_sources.prices" } }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * fetch-prices.js
3
+ * Polls for tmp_file1 payload and outputs JSON to stdout once available.
4
+ */
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+
8
+ function getArgValue(flagName) {
9
+ const idx = process.argv.indexOf(flagName);
10
+ if (idx < 0) return undefined;
11
+ return process.argv[idx + 1];
12
+ }
13
+
14
+ const envBoardDir = (process.env.BOARD_DIR ?? '').trim();
15
+ const tmpFileName = String(getArgValue('--tmp-file-name') ?? process.env.TMP_FILE_NAME ?? 'tmp_file1').trim();
16
+ const tmpFilePath = envBoardDir ? path.join(envBoardDir, tmpFileName) : '';
17
+
18
+ if (!tmpFilePath) {
19
+ console.error('BOARD_DIR environment variable is required for fetch-prices.js');
20
+ process.exit(1);
21
+ }
22
+
23
+ function getReadableTmpFile() {
24
+ if (!fs.existsSync(tmpFilePath)) return undefined;
25
+ const content = fs.readFileSync(tmpFilePath, 'utf-8').trim();
26
+ if (!content) return undefined;
27
+ return { tmpFile: tmpFilePath, content };
28
+ }
29
+
30
+ function waitForFile(timeoutMs = 120000) {
31
+ const started = Date.now();
32
+ const interval = setInterval(() => {
33
+ if (Date.now() - started > timeoutMs) {
34
+ clearInterval(interval);
35
+ console.error('Timed out waiting for tmp_file1 market prices input.');
36
+ process.exit(1);
37
+ }
38
+
39
+ const ready = getReadableTmpFile();
40
+ if (!ready) return;
41
+
42
+ clearInterval(interval);
43
+ fs.writeFileSync(ready.tmpFile, '', 'utf-8');
44
+ process.stdout.write(`${ready.content}\n`);
45
+ }, 250);
46
+ }
47
+
48
+ waitForFile();
@@ -0,0 +1,58 @@
1
+ import * as path from 'node:path';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const repoRoot = path.resolve(__dirname, '..', '..', '..', '..', '..');
8
+ const boardCliPath = path.join(repoRoot, 'board-live-cards-cli.js');
9
+
10
+ export function runBoardCli(args, options = {}) {
11
+ const { capture = false, cwd = process.cwd() } = options;
12
+ const result = spawnSync(process.execPath, [boardCliPath, ...args], {
13
+ cwd,
14
+ encoding: 'utf-8',
15
+ windowsHide: true,
16
+ stdio: capture ? 'pipe' : 'pipe',
17
+ env: {
18
+ ...process.env,
19
+ BOARD_LIVE_CARDS_NO_SPAWN: process.env.BOARD_LIVE_CARDS_NO_SPAWN ?? '1',
20
+ BOARD_DIR: process.env.BOARD_DIR ?? '',
21
+ },
22
+ });
23
+
24
+ if (result.error) {
25
+ throw new Error(`Failed to launch board-live-cards-cli: ${result.error.message}`);
26
+ }
27
+
28
+ if ((result.status ?? 1) !== 0) {
29
+ const stderr = (result.stderr ?? '').trim();
30
+ const stdout = (result.stdout ?? '').trim();
31
+ throw new Error(`board-live-cards-cli failed (${result.status}): ${stderr || stdout || 'no output'}`);
32
+ }
33
+
34
+ return capture ? (result.stdout ?? '') : '';
35
+ }
36
+
37
+ export async function readStdinJson() {
38
+ let raw = '';
39
+ process.stdin.setEncoding('utf-8');
40
+
41
+ for await (const chunk of process.stdin) {
42
+ raw += chunk;
43
+ }
44
+
45
+ if (!raw.trim()) {
46
+ return {};
47
+ }
48
+
49
+ return JSON.parse(raw);
50
+ }
51
+
52
+ export function writeResult(payload) {
53
+ process.stdout.write(JSON.stringify(payload));
54
+ }
55
+
56
+ export function writeFailure(message) {
57
+ writeResult({ result: 'failure', error: message });
58
+ }
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
4
+
5
+ try {
6
+ const input = await readStdinJson();
7
+ const boardDir = String(input.BOARD_DIR ?? '').trim();
8
+ const cardsGlob = String(input.CARDS_GLOB ?? '').trim();
9
+
10
+ if (!boardDir || !cardsGlob) {
11
+ writeFailure('BOARD_DIR and CARDS_GLOB are required');
12
+ process.exit(0);
13
+ }
14
+
15
+ runBoardCli(['add-cards', '--rg', boardDir, '--card-glob', cardsGlob]);
16
+
17
+ writeResult({
18
+ result: 'success',
19
+ data: {
20
+ board_dir: boardDir,
21
+ cards_glob: cardsGlob,
22
+ },
23
+ });
24
+ } catch (error) {
25
+ const message = error instanceof Error ? error.message : String(error);
26
+ writeFailure(message);
27
+ }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
4
+
5
+ try {
6
+ const input = await readStdinJson();
7
+ const boardDir = String(input.BOARD_DIR ?? '').trim();
8
+
9
+ if (!boardDir) {
10
+ writeFailure('BOARD_DIR is required');
11
+ process.exit(0);
12
+ }
13
+
14
+ runBoardCli(['init', boardDir]);
15
+ writeResult({
16
+ result: 'success',
17
+ data: {
18
+ board_dir: boardDir,
19
+ message: `initialized ${boardDir}`,
20
+ },
21
+ });
22
+ } catch (error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ writeFailure(message);
25
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as fs from 'node:fs';
4
+ import * as path from 'node:path';
5
+ import { readStdinJson, writeFailure, writeResult } from './_board-cli.js';
6
+
7
+ try {
8
+ const input = await readStdinJson();
9
+ const boardDirInput = String(input.BOARD_DIR ?? '').trim();
10
+
11
+ if (!boardDirInput) {
12
+ writeFailure('BOARD_DIR is required');
13
+ process.exit(0);
14
+ }
15
+
16
+ const boardDir = path.resolve(boardDirInput);
17
+ fs.rmSync(boardDir, { recursive: true, force: true });
18
+
19
+ writeResult({
20
+ result: 'success',
21
+ data: {
22
+ board_dir: boardDir,
23
+ reset: true,
24
+ },
25
+ });
26
+ } catch (error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ writeFailure(message);
29
+ }