yaml-flow 8.2.5 → 8.4.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 (93) hide show
  1. package/browser/asset-integrity.json +3 -3
  2. package/browser/board-livecards-client.js +1 -1
  3. package/browser/board-livecards-localstorage.js +6 -4
  4. package/browser/live-cards.js +19 -25
  5. package/cli/{board-live-cards-lib-Iq_XAC09.d.ts → board-live-cards-lib-tjYsPt5U.d.ts} +1 -1
  6. package/cli/browser-api/board-live-cards-browser-adapter.d.ts +3 -3
  7. package/cli/browser-api/board-live-cards-browser-adapter.js +3 -2
  8. package/cli/browser-api/card-store-browser-api.d.ts +1 -1
  9. package/cli/browser-api/card-store-browser-api.js +1 -1
  10. package/cli/{execution-interface-ftO1W7Po.d.ts → execution-interface-DCFBy4L8.d.ts} +151 -2
  11. package/cli/node/artifacts-store-cli.js +5 -5
  12. package/cli/node/batch-runner-cli.d.ts +3 -0
  13. package/cli/node/batch-runner-cli.js +2 -1
  14. package/cli/node/board-live-cards-cli.js +14 -8
  15. package/cli/node/card-store-cli.js +6 -6
  16. package/cli/node/chat-store-cli.d.ts +23 -0
  17. package/cli/node/chat-store-cli.js +8 -0
  18. package/cli/node/execution-adapter.d.ts +4 -2
  19. package/cli/node/execution-adapter.js +2 -2
  20. package/cli/node/fs-board-adapter.d.ts +75 -75
  21. package/cli/node/fs-board-adapter.js +14 -10
  22. package/cli/node/source-cli-task-executor.js +4 -4
  23. package/cli/node/step-machine-cli.js +3 -3
  24. package/cli/{types--rXGWbSR.d.ts → types-Dc2VRMUw.d.ts} +27 -124
  25. package/examples/board/demo-shell-with-server.html +3 -196
  26. package/examples/board/doc.html +465 -0
  27. package/examples/board/server/board-server.js +30 -82
  28. package/examples/board/server/board-worker/source_def_flows.json +2 -2
  29. package/examples/board/server/chat-flow/copilot-chat/assistant.js +44 -185
  30. package/examples/board/server/chat-flow/copilot-chat/copilot_wrapper.bat +157 -0
  31. package/examples/board/server/chat-flow/copilot-chat/copilot_wrapper_helper.ps1 +190 -0
  32. package/examples/board/server/chat-flow/flow-steps.json +122 -56
  33. package/examples/board/test/server-http-test.js +252 -220
  34. package/examples/board-local/demo-shell-localstorage.html +3 -3
  35. package/lib/{artifacts-store-lib-public-GD4H-fFp.d.ts → artifacts-store-lib-454TAuov.d.ts} +2 -32
  36. package/lib/{artifacts-store-lib-public-C5UL5tyG.d.cts → artifacts-store-lib-zsGFbBV8.d.cts} +2 -32
  37. package/lib/artifacts-store-public.d.cts +34 -3
  38. package/lib/artifacts-store-public.d.ts +34 -3
  39. package/lib/board-live-cards-node.cjs +14 -10
  40. package/lib/board-live-cards-node.d.cts +42 -21
  41. package/lib/board-live-cards-node.d.ts +42 -21
  42. package/lib/board-live-cards-node.js +14 -10
  43. package/lib/{board-live-cards-public-BLXbcBNk.d.cts → board-live-cards-public-BM6jCEIa.d.cts} +25 -5
  44. package/lib/{board-live-cards-public-BZaNb2mi.d.ts → board-live-cards-public-Bz07XKRK.d.ts} +25 -5
  45. package/lib/board-live-cards-public.cjs +2 -2
  46. package/lib/board-live-cards-public.d.cts +2 -2
  47. package/lib/board-live-cards-public.d.ts +2 -2
  48. package/lib/board-live-cards-public.js +2 -2
  49. package/lib/board-live-cards-server-runtime.cjs +4 -4
  50. package/lib/board-live-cards-server-runtime.d.cts +5 -3
  51. package/lib/board-live-cards-server-runtime.d.ts +5 -3
  52. package/lib/board-live-cards-server-runtime.js +4 -4
  53. package/lib/board-worker-adapter.cjs +2 -2
  54. package/lib/board-worker-adapter.js +2 -2
  55. package/lib/card-store-public.d.cts +2 -2
  56. package/lib/card-store-public.d.ts +2 -2
  57. package/lib/chat-storage-lib-DGaKrjVe.d.ts +53 -0
  58. package/lib/chat-storage-lib-OX0Q_Ttf.d.cts +53 -0
  59. package/lib/chat-store-public.cjs +2 -0
  60. package/lib/chat-store-public.d.cts +128 -0
  61. package/lib/chat-store-public.d.ts +128 -0
  62. package/lib/chat-store-public.js +2 -0
  63. package/lib/execution-refs.cjs +1 -1
  64. package/lib/execution-refs.d.cts +10 -1
  65. package/lib/execution-refs.d.ts +10 -1
  66. package/lib/execution-refs.js +1 -1
  67. package/lib/index.d.cts +1 -1
  68. package/lib/index.d.ts +1 -1
  69. package/lib/server-runtime/index.cjs +4 -4
  70. package/lib/server-runtime/index.d.cts +6 -4
  71. package/lib/server-runtime/index.d.ts +6 -4
  72. package/lib/server-runtime/index.js +4 -4
  73. package/lib/step-machine-public/index.cjs +3 -3
  74. package/lib/step-machine-public/index.d.cts +1 -1
  75. package/lib/step-machine-public/index.d.ts +1 -1
  76. package/lib/step-machine-public/index.js +3 -3
  77. package/lib/{storage-interface-B6ecOulj.d.ts → storage-interface-B-7pDHwD.d.cts} +36 -1
  78. package/lib/{storage-interface-B6ecOulj.d.cts → storage-interface-B-7pDHwD.d.ts} +36 -1
  79. package/lib/stores/index.d.cts +1 -1
  80. package/lib/stores/index.d.ts +1 -1
  81. package/lib/stores/kv.d.cts +1 -1
  82. package/lib/stores/kv.d.ts +1 -1
  83. package/lib/{types-D-xVWPdY.d.ts → types-Cn0b8G-i.d.ts} +16 -65
  84. package/lib/{types-Bztd1KoK.d.cts → types-Dszjwfud.d.cts} +16 -65
  85. package/package.json +8 -2
  86. package/examples/board/.board-ws/cards/store/_index.json +0 -17
  87. package/examples/board/.board-ws/cards/store/card-market-prices.json +0 -80
  88. package/examples/board/.board-ws/cards/store/card-portfolio-value.json +0 -90
  89. package/examples/board/.board-ws/cards/store/card-portfolio.json +0 -78
  90. package/examples/board/server/chat-flow/chat-clear-processing.js +0 -41
  91. package/examples/board/server/chat-flow/chat-open-turn.js +0 -144
  92. package/examples/board/server/chat-flow/chat-write-assistant.js +0 -44
  93. package/examples/board/server/chat-flow/echo-probe/assistant.js +0 -28
@@ -0,0 +1,465 @@
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>Example Board Demo (Server Runtime)</title>
7
+ <link rel="icon" type="image/svg+xml" href="../../browser/favicon.svg" />
8
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
9
+ <style>
10
+ /* Scale down markdown headings inside cards to stay in proportion with card body text */
11
+ .lc-result h1 { font-size: 1rem; font-weight: 700; margin: 0.5rem 0 0.25rem; }
12
+ .lc-result h2 { font-size: 0.9rem; font-weight: 700; margin: 0.5rem 0 0.25rem; border-bottom: 1px solid #dee2e6; padding-bottom: 0.1rem; }
13
+ .lc-result h3 { font-size: 0.85rem; font-weight: 600; margin: 0.4rem 0 0.2rem; }
14
+ .lc-result p { font-size: 0.8rem; margin: 0 0 0.25rem; }
15
+ .lc-result ul, .lc-result ol { font-size: 0.8rem; margin: 0 0 0.25rem; padding-left: 1.2rem; }
16
+ .lc-result li { margin-bottom: 0.15rem; }
17
+ .demo-docs pre {
18
+ background: #0f172a;
19
+ border-radius: 0.75rem;
20
+ color: #e2e8f0;
21
+ font-size: 0.8rem;
22
+ margin: 0;
23
+ overflow-x: auto;
24
+ padding: 0.9rem 1rem;
25
+ white-space: pre-wrap;
26
+ }
27
+ .demo-docs code {
28
+ color: inherit;
29
+ font-size: inherit;
30
+ }
31
+ .demo-docs .list-inline-item {
32
+ margin-right: 0.35rem;
33
+ margin-bottom: 0.35rem;
34
+ }
35
+ </style>
36
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
37
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
38
+ <script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
39
+ <script src="https://cdn.jsdelivr.net/npm/leader-line/leader-line.min.js"></script>
40
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.4.0/browser/live-cards.js"></script>
41
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.4.0/browser/board-livecards-client.js"></script>
42
+ </head>
43
+ <body class="bg-light">
44
+ <div class="container-fluid py-3">
45
+ <div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-3">
46
+ <div>
47
+ <h1 class="h4 mb-0" id="boardTitle">Example Board (Server Runtime)</h1>
48
+ <div class="small text-muted" id="boardDesc"></div>
49
+ </div>
50
+ <div class="d-flex align-items-center gap-2">
51
+ <select class="form-select form-select-sm" id="boardSelector" style="max-width:180px" title="Active board"></select>
52
+ <button class="btn btn-sm btn-outline-success" id="addBoardBtn">+ Board</button>
53
+ <div id="addBoardForm" class="d-flex align-items-center gap-1" style="display:none!important">
54
+ <input class="form-control form-control-sm" id="newBoardId" placeholder="board-id" style="max-width:110px" />
55
+ <input class="form-control form-control-sm" id="newBoardLabel" placeholder="Label (optional)" style="max-width:150px" />
56
+ <button class="btn btn-sm btn-success" id="addBoardSubmit">Add</button>
57
+ <button class="btn btn-sm btn-secondary" id="addBoardCancel">Cancel</button>
58
+ </div>
59
+ <button class="btn btn-sm btn-outline-primary" id="modeBoard">Board</button>
60
+ <button class="btn btn-sm btn-outline-primary" id="modeCanvas">Canvas</button>
61
+ <button class="btn btn-sm btn-outline-secondary" id="autoLayout">Auto Layout</button>
62
+ <button class="btn btn-sm btn-outline-dark" id="refreshAll" style="display:none">Refresh All</button>
63
+ <div class="form-check ms-2">
64
+ <input class="form-check-input" type="checkbox" id="devModeToggle" />
65
+ <label class="form-check-label" for="devModeToggle">Dev Mode</label>
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ <div class="alert alert-info small py-2 mb-3">
71
+ Server runtime mode: board initializes on first connect and streams updates via SSE.
72
+ Source execution and event processing happen on the server side.
73
+ </div>
74
+
75
+ <div class="card shadow-sm border-0 mb-3 demo-docs">
76
+ <div class="card-body">
77
+ <div class="d-flex flex-wrap justify-content-between gap-3 mb-3">
78
+ <div>
79
+ <h2 class="h6 mb-1">Advanced Composition Model</h2>
80
+ <p class="small text-muted mb-0">
81
+ This demo still mounts the full board, but the same client now exposes the shared runtime session
82
+ needed for filtered or projected live views. Host applications do not need to reimplement SSE,
83
+ client identity, notification reduction, or card chat subscribe or unsubscribe.
84
+ </p>
85
+ </div>
86
+ <div class="small text-muted text-end">
87
+ <div><strong>Active board:</strong> <span id="docActiveBoard">default</span></div>
88
+ <div><strong>Shared client id:</strong> <span id="docClientId">pending</span></div>
89
+ <div><strong>Session state:</strong> <span id="docSessionState">not connected</span></div>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="row g-3">
94
+ <div class="col-lg-4">
95
+ <div class="border rounded-3 p-3 h-100 bg-white">
96
+ <h3 class="h6 mb-2">What is public now</h3>
97
+ <ul class="list-inline small mb-0">
98
+ <li class="list-inline-item badge text-bg-light">createBoardRuntimeSession()</li>
99
+ <li class="list-inline-item badge text-bg-light">createDerivedBoardRuntime()</li>
100
+ <li class="list-inline-item badge text-bg-light">applyBoardNotifications()</li>
101
+ <li class="list-inline-item badge text-bg-light">serverPayloadToBoardState()</li>
102
+ <li class="list-inline-item badge text-bg-light">pickBoardState()</li>
103
+ <li class="list-inline-item badge text-bg-light">subtractBoardState()</li>
104
+ <li class="list-inline-item badge text-bg-light">deriveBoardState()</li>
105
+ <li class="list-inline-item badge text-bg-light">session.attachProvidedState()</li>
106
+ <li class="list-inline-item badge text-bg-light">session.subscribeCardChats()</li>
107
+ <li class="list-inline-item badge text-bg-light">session.unsubscribeCardChats()</li>
108
+ </ul>
109
+ </div>
110
+ </div>
111
+ <div class="col-lg-4">
112
+ <div class="border rounded-3 p-3 h-100 bg-white">
113
+ <h3 class="h6 mb-2">Recommended shape</h3>
114
+ <ol class="small mb-0 ps-3">
115
+ <li>Create one shared runtime session per logical board.</li>
116
+ <li>Mount the full board or any number of derived views from that same session.</li>
117
+ <li>Reuse the shared client id for all chat subscribe or unsubscribe calls.</li>
118
+ <li>Use the public reducer helpers instead of mutating projected state by hand.</li>
119
+ </ol>
120
+ </div>
121
+ </div>
122
+ <div class="col-lg-4">
123
+ <div class="border rounded-3 p-3 h-100 bg-white">
124
+ <h3 class="h6 mb-2">When to use this</h3>
125
+ <ul class="small mb-0 ps-3">
126
+ <li>split views over one board</li>
127
+ <li>plugin or secondary mounts</li>
128
+ <li>filtered card subsets</li>
129
+ <li>provided-state boot with live SSE attached afterward</li>
130
+ </ul>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <div class="row g-3 mt-1">
136
+ <div class="col-xl-4">
137
+ <div class="border rounded-3 overflow-hidden bg-white h-100">
138
+ <div class="px-3 py-2 border-bottom small fw-semibold">1. Share one session</div>
139
+ <pre><code>const client = BoardLiveCardsClient.createBoardRuntimeClient({
140
+ fetchServer,
141
+ boardPaths,
142
+ getServerOrigin,
143
+ });
144
+
145
+ await client.bootstrapBoard({
146
+ boardId: 'default',
147
+ rootElement: boardRoot,
148
+ });
149
+
150
+ const session = client.getRuntimeSession();</code></pre>
151
+ </div>
152
+ </div>
153
+ <div class="col-xl-4">
154
+ <div class="border rounded-3 overflow-hidden bg-white h-100">
155
+ <div class="px-3 py-2 border-bottom small fw-semibold">2. Mount derived live views</div>
156
+ <pre><code>const alertsView = client.createDerivedRuntime({
157
+ includeCard: (model) =&gt; model.id.startsWith('alert-'),
158
+ });
159
+
160
+ alertsView.mountBoard({
161
+ rootElement: alertsRoot,
162
+ mode: 'board',
163
+ });
164
+
165
+ // shares SSE + chat session with the main board</code></pre>
166
+ </div>
167
+ </div>
168
+ <div class="col-xl-4">
169
+ <div class="border rounded-3 overflow-hidden bg-white h-100">
170
+ <div class="px-3 py-2 border-bottom small fw-semibold">3. Start from provided state</div>
171
+ <pre><code>const session = BoardLiveCardsClient.createBoardRuntimeSession({
172
+ fetchServer,
173
+ boardPaths,
174
+ getServerOrigin,
175
+ });
176
+
177
+ session.attachProvidedState({ boardId, payload });
178
+ await session.bootstrap({ boardId, skipInitBoard: true });
179
+
180
+ const view = BoardLiveCardsClient.createDerivedBoardRuntime({
181
+ session,
182
+ includeCard: (model) =&gt; model.id !== 'hidden-card',
183
+ });</code></pre>
184
+ </div>
185
+ </div>
186
+ </div>
187
+
188
+ <div class="row g-3 mt-1">
189
+ <div class="col-xl-6">
190
+ <div class="border rounded-3 overflow-hidden bg-white h-100">
191
+ <div class="px-3 py-2 border-bottom small fw-semibold">Reducer helpers for host-managed state</div>
192
+ <pre><code>const state = BoardLiveCardsClient.serverPayloadToBoardState(payload);
193
+
194
+ const nextState = BoardLiveCardsClient.applyBoardNotifications(
195
+ state,
196
+ notifications,
197
+ () =&gt; payload,
198
+ );
199
+
200
+ const leftPane = BoardLiveCardsClient.pickBoardState(nextState, ['card-a']);
201
+ const rightPane = BoardLiveCardsClient.subtractBoardState(nextState, ['card-a']);</code></pre>
202
+ </div>
203
+ </div>
204
+ <div class="col-xl-6">
205
+ <div class="border rounded-3 overflow-hidden bg-white h-100">
206
+ <div class="px-3 py-2 border-bottom small fw-semibold">Chat lifecycle stays with the session</div>
207
+ <pre><code>const session = client.getRuntimeSession();
208
+
209
+ await session.subscribeCardChats(cardId);
210
+ await session.unsubscribeCardChats(cardId);
211
+
212
+ // same shared client id is reused automatically
213
+ console.log(session.getClientId());
214
+
215
+ // incoming notifications keep mounted chat UIs synchronized</code></pre>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <div id="boardRoot">
223
+ <div class="d-flex align-items-center justify-content-center" style="height: 72vh;">
224
+ <div class="text-center">
225
+ <div class="spinner-border mb-3" role="status">
226
+ <span class="visually-hidden">Loading...</span>
227
+ </div>
228
+ <p class="text-muted">Initializing board...</p>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ <script>
235
+ (function () {
236
+ const createBoardRuntimeClient = window.BoardLiveCardsClient && window.BoardLiveCardsClient.createBoardRuntimeClient;
237
+ const defaultBoardPaths = window.BoardLiveCardsClient && window.BoardLiveCardsClient.defaultBoardPaths;
238
+ if (!createBoardRuntimeClient || !defaultBoardPaths) {
239
+ document.getElementById('boardRoot').innerHTML =
240
+ '<div class="alert alert-danger">board-livecards-client.js not loaded. Run <code>npm run build:browser</code> first.</div>';
241
+ throw new Error('BoardLiveCardsClient not loaded');
242
+ }
243
+ let currentMode = 'canvas';
244
+ let bootstrapCompleted = false;
245
+ let activeBoardId = 'default';
246
+ let runtimeClient = null;
247
+
248
+ function refreshDocsRuntimeState() {
249
+ const boardEl = document.getElementById('docActiveBoard');
250
+ const clientIdEl = document.getElementById('docClientId');
251
+ const sessionStateEl = document.getElementById('docSessionState');
252
+ const session = runtimeClient && typeof runtimeClient.getRuntimeSession === 'function'
253
+ ? runtimeClient.getRuntimeSession()
254
+ : null;
255
+
256
+ if (boardEl) boardEl.textContent = activeBoardId || 'default';
257
+ if (!session) {
258
+ if (clientIdEl) clientIdEl.textContent = 'unavailable';
259
+ if (sessionStateEl) sessionStateEl.textContent = 'not connected';
260
+ return;
261
+ }
262
+
263
+ if (clientIdEl) clientIdEl.textContent = session.getClientId();
264
+ if (sessionStateEl) {
265
+ const state = session.getState();
266
+ const status = session.isConnected()
267
+ ? `connected, ${Array.isArray(state && state.cardIds) ? state.cardIds.length : 0} cards in session state`
268
+ : 'not connected';
269
+ sessionStateEl.textContent = status;
270
+ }
271
+ }
272
+
273
+ function boardPaths(boardId) {
274
+ return defaultBoardPaths(boardId || activeBoardId || 'default');
275
+ }
276
+
277
+ function localServerOrigins() {
278
+ const params = new URLSearchParams(window.location.search || '');
279
+ const fromQuery = params.get('demoServerOrigin');
280
+ const fromWindow = window.DEMO_SERVER_ORIGIN;
281
+ const hostPref = window.location.hostname === 'localhost' ? 'localhost' : '127.0.0.1';
282
+ const hostAlt = hostPref === 'localhost' ? '127.0.0.1' : 'localhost';
283
+ const defaults = [
284
+ `http://${hostPref}:7799`,
285
+ `http://${hostAlt}:7799`,
286
+ ];
287
+
288
+ const unique = [];
289
+ const push = (v) => {
290
+ if (!v || typeof v !== 'string') return;
291
+ if (unique.includes(v)) return;
292
+ unique.push(v);
293
+ };
294
+
295
+ push(fromWindow);
296
+ push(fromQuery);
297
+ defaults.forEach(push);
298
+ return unique;
299
+ }
300
+
301
+ let activeServerOrigin = null;
302
+
303
+ async function fetchServer(path, init) {
304
+ const origins = activeServerOrigin ? [activeServerOrigin] : localServerOrigins();
305
+ const failures = [];
306
+
307
+ for (const origin of origins) {
308
+ const url = `${origin}${path}`;
309
+ try {
310
+ const res = await fetch(url, init);
311
+ activeServerOrigin = origin;
312
+ return res;
313
+ } catch (err) {
314
+ failures.push(`${url} -> ${String(err && err.message || err)}`);
315
+ }
316
+ }
317
+
318
+ const tip = 'Start the server from the yaml-flow folder: node public-examples/board/server/board-server.js';
319
+ throw new Error(`Unable to reach example-board demo server. Tried: ${failures.join(' | ')}. ${tip}`);
320
+ }
321
+
322
+ runtimeClient = createBoardRuntimeClient({
323
+ fetchServer,
324
+ boardPaths,
325
+ getServerOrigin: function () { return activeServerOrigin; },
326
+ initialMode: currentMode,
327
+ canvas: { height: '72vh', overflow: 'auto' },
328
+ });
329
+
330
+ async function loadBoardList() {
331
+ try {
332
+ const res = await fetchServer('/api/boards');
333
+ if (!res.ok) return;
334
+ const data = await res.json();
335
+ const boards = Array.isArray(data && data.boards) ? data.boards : [];
336
+ const sel = document.getElementById('boardSelector');
337
+ const prev = sel.value;
338
+ sel.innerHTML = boards.map((b) =>
339
+ `<option value="${b.id}"${b.id === (prev || activeBoardId) ? ' selected' : ''}>${b.label || b.id}</option>`
340
+ ).join('');
341
+ if (boards.length && !boards.some((b) => b.id === activeBoardId)) {
342
+ activeBoardId = boards[0].id;
343
+ sel.value = activeBoardId;
344
+ }
345
+ } catch (err) {
346
+ console.warn('Could not load board list', err);
347
+ }
348
+ }
349
+
350
+ async function switchBoard(boardId) {
351
+ if (boardId === activeBoardId && bootstrapCompleted) return;
352
+ runtimeClient.dispose();
353
+ bootstrapCompleted = false;
354
+ activeBoardId = boardId;
355
+ refreshDocsRuntimeState();
356
+ document.getElementById('boardRoot').innerHTML =
357
+ '<div class="d-flex align-items-center justify-content-center" style="height:72vh">'
358
+ + '<div class="text-center"><div class="spinner-border mb-3" role="status">'
359
+ + '<span class="visually-hidden">Loading...</span></div>'
360
+ + '<p class="text-muted">Loading board...</p></div></div>';
361
+ await bootstrap(boardId);
362
+ }
363
+
364
+ async function bootstrap(boardId) {
365
+ const bid = boardId || activeBoardId || 'default';
366
+
367
+ // Board title from registry label or fallback
368
+ document.getElementById('boardTitle').textContent = bid + ' (Server Runtime)';
369
+ document.getElementById('boardDesc').textContent = '';
370
+
371
+ // SSE-first bootstrap: init-board initializes the board server-side,
372
+ // then /sse sends the full payload as the first frame. No separate
373
+ // demo-setup step needed — the server runs it automatically at init time.
374
+ await runtimeClient.bootstrapBoard({
375
+ boardId: bid,
376
+ rootElement: document.getElementById('boardRoot'),
377
+ mode: currentMode,
378
+ });
379
+
380
+ bootstrapCompleted = true;
381
+ refreshDocsRuntimeState();
382
+ }
383
+
384
+ document.getElementById('modeBoard').addEventListener('click', function () {
385
+ currentMode = 'board';
386
+ runtimeClient.setMode('board');
387
+ });
388
+
389
+ document.getElementById('modeCanvas').addEventListener('click', function () {
390
+ currentMode = 'canvas';
391
+ runtimeClient.setMode('canvas');
392
+ });
393
+
394
+ document.getElementById('autoLayout').addEventListener('click', function () {
395
+ currentMode = 'canvas';
396
+ runtimeClient.autoLayout();
397
+ });
398
+
399
+ document.getElementById('devModeToggle').addEventListener('change', function () {
400
+ runtimeClient.setDevMode(this.checked);
401
+ });
402
+
403
+ document.getElementById('boardSelector').addEventListener('change', function () {
404
+ switchBoard(this.value).catch(function (err) {
405
+ console.error(err);
406
+ document.getElementById('boardRoot').innerHTML =
407
+ `<div class="alert alert-danger">Failed to switch board: ${String(err && err.message || err)}</div>`;
408
+ });
409
+ });
410
+
411
+ document.getElementById('addBoardBtn').addEventListener('click', function () {
412
+ document.getElementById('addBoardForm').style.removeProperty('display');
413
+ document.getElementById('newBoardId').focus();
414
+ });
415
+
416
+ document.getElementById('addBoardCancel').addEventListener('click', function () {
417
+ document.getElementById('addBoardForm').style.display = 'none';
418
+ document.getElementById('newBoardId').value = '';
419
+ document.getElementById('newBoardLabel').value = '';
420
+ });
421
+
422
+ document.getElementById('addBoardSubmit').addEventListener('click', async function () {
423
+ const id = document.getElementById('newBoardId').value.trim();
424
+ const label = document.getElementById('newBoardLabel').value.trim();
425
+ if (!id) {
426
+ alert('Board id is required');
427
+ return;
428
+ }
429
+ try {
430
+ const res = await fetchServer('/api/boards', {
431
+ method: 'POST',
432
+ headers: { 'content-type': 'application/json' },
433
+ body: JSON.stringify({ id, label: label || id }),
434
+ });
435
+ const data = await res.json();
436
+ if (!res.ok) {
437
+ alert(data.error || 'Failed to add board');
438
+ return;
439
+ }
440
+ document.getElementById('addBoardForm').style.display = 'none';
441
+ document.getElementById('newBoardId').value = '';
442
+ document.getElementById('newBoardLabel').value = '';
443
+ await loadBoardList();
444
+ await switchBoard(id);
445
+ } catch (err) {
446
+ alert('Error adding board: ' + String((err && err.message) || err));
447
+ }
448
+ });
449
+
450
+ loadBoardList().then(function () {
451
+ refreshDocsRuntimeState();
452
+ return bootstrap(activeBoardId);
453
+ }).catch(function (err) {
454
+ console.error(err);
455
+ document.getElementById('boardRoot').innerHTML =
456
+ `<div class="alert alert-danger">Failed to start server runtime demo: ${String(err && err.message || err)}</div>`;
457
+ });
458
+
459
+ window.addEventListener('beforeunload', function () {
460
+ runtimeClient.dispose();
461
+ });
462
+ })();
463
+ </script>
464
+ </body>
465
+ </html>
@@ -14,10 +14,13 @@ import {
14
14
  } from 'yaml-flow/board-live-cards-server-runtime';
15
15
 
16
16
  import {
17
+ buildLocalBaseSpec,
17
18
  createFsBoardPlatformAdapter,
18
19
  createFsBoardChatStorage,
19
20
  createNodeSpawnInvocationAdapter,
20
21
  createArtifactsStore,
22
+ evaluateArgsMassaging,
23
+ invokeExecutionRef,
21
24
  parseRef,
22
25
  serializeRef,
23
26
  } from 'yaml-flow/board-live-cards-node';
@@ -289,6 +292,11 @@ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow,
289
292
  const runtimeCardsDir = path.join(path.dirname(boardDir), 'cards');
290
293
  const runtimeCardStoreDir = path.join(runtimeCardsDir, 'store');
291
294
  fs.mkdirSync(runtimeCardStoreDir, { recursive: true });
295
+ const runtimeOutDir = path.join(path.dirname(boardDir), 'runtime-out');
296
+ const scratchDir = path.join(runtimeOutDir, '.tmp');
297
+ const archiveDir = path.join(runtimeOutDir, 'archive');
298
+ fs.mkdirSync(scratchDir, { recursive: true });
299
+ fs.mkdirSync(archiveDir, { recursive: true });
292
300
 
293
301
  const notifyChannel = `yaml-flow-server-${label}-${boardId}-${process.pid}`;
294
302
  const baseRef = parseRef(serializeRef({ kind: 'fs-path', value: boardDir }));
@@ -298,6 +306,8 @@ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow,
298
306
  const artifactsRef = parseRef(serializeRef({ kind: 'fs-path', value: runtimeCardsDir }));
299
307
  const artifactsAdapter = createFsBoardPlatformAdapter(artifactsRef, { suppressSpawn: true });
300
308
  const cardStoreRef = serializeRef({ kind: 'fs-path', value: runtimeCardStoreDir });
309
+ const scratchStoreRef = serializeRef({ kind: 'fs-path', value: scratchDir });
310
+ const archiveStoreRef = serializeRef({ kind: 'fs-path', value: archiveDir });
301
311
 
302
312
  return {
303
313
  label,
@@ -305,7 +315,9 @@ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow,
305
315
  artifactsAdapter,
306
316
  baseRef,
307
317
  cardStoreRef,
308
- outputsStoreRef: serializeRef({ kind: 'fs-path', value: path.join(path.dirname(boardDir), 'runtime-out', '.outputs') }),
318
+ outputsStoreRef: serializeRef({ kind: 'fs-path', value: runtimeOutDir }),
319
+ scratchStoreRef,
320
+ archiveStoreRef,
309
321
  notifyRef: { kind: 'named-pipe', value: namedPipePath(notifyChannel) },
310
322
  taskExecutorRef: makeExecutionRef(taskExecPath, executionExtra),
311
323
  chatHandlerFlow,
@@ -315,7 +327,7 @@ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow,
315
327
 
316
328
  // Pre-register configured boards in the server meta store
317
329
  const persistedBoardsConfigText = serverMetaStore.getText('boards-config.json');
318
- let persistedBoardsConfig = { boards: [{ id: 'default', label: 'Default Board' }] };
330
+ let persistedBoardsConfig = { boards: [] };
319
331
  if (persistedBoardsConfigText) {
320
332
  try {
321
333
  const parsedBoardsConfig = JSON.parse(persistedBoardsConfigText);
@@ -323,7 +335,7 @@ if (persistedBoardsConfigText) {
323
335
  persistedBoardsConfig = parsedBoardsConfig;
324
336
  }
325
337
  } catch {
326
- persistedBoardsConfig = { boards: [{ id: 'default', label: 'Default Board' }] };
338
+ persistedBoardsConfig = { boards: [] };
327
339
  }
328
340
  }
329
341
 
@@ -350,82 +362,15 @@ serverMetaStore.putText(
350
362
  );
351
363
 
352
364
  /**
353
- * Async local-node ref invoker uses spawn() instead of spawnSync() so the
354
- * Node.js event loop is never blocked. Required for chat-flow handlers that
355
- * must call back to the same server process (avoids a deadlock).
365
+ * Thin wrapper around the shared execution-ref invoker that pins the board
366
+ * server's cliDir/cwd/label defaults for chat-flow steps.
356
367
  */
357
- function invokeLocalNodeRefAsync(ref, args, opts) {
358
- return new Promise((resolve) => {
359
- const whatToRun = ref.whatToRun;
360
- let scriptPath = '';
361
- if (whatToRun && typeof whatToRun === 'object' && whatToRun.kind === 'fs-path') {
362
- scriptPath = String(whatToRun.value || '');
363
- } else if (typeof whatToRun === 'string') {
364
- if (whatToRun.startsWith('b64:')) {
365
- try {
366
- const parsed = parseRef(whatToRun);
367
- if (parsed.kind === 'fs-path') scriptPath = String(parsed.value || '');
368
- } catch { /* fall through */ }
369
- } else {
370
- scriptPath = whatToRun;
371
- }
372
- }
373
- if (!scriptPath) {
374
- return resolve({ result: 'failure', data: { error: 'cannot resolve script path from ref' } });
375
- }
376
-
377
- const cliDir = opts?.cliDir || BOARD_ROOT;
378
- const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(cliDir, scriptPath);
379
- const stdinData = JSON.stringify(args);
380
- const timeoutMs = typeof opts?.timeoutMs === 'number' && opts.timeoutMs > 0 ? opts.timeoutMs : 30_000;
381
-
382
- let stdout = '';
383
- let stderr = '';
384
- let settled = false;
385
- const settle = (val) => {
386
- if (settled) return;
387
- settled = true;
388
- clearTimeout(timer);
389
- resolve(val);
390
- };
391
-
392
- const child = spawn(process.execPath, [resolved], {
393
- stdio: ['pipe', 'pipe', 'pipe'],
394
- windowsHide: true,
395
- cwd: opts?.cwd || cliDir,
396
- });
397
-
398
- const timer = setTimeout(() => {
399
- try { child.kill(); } catch { /* best-effort */ }
400
- settle({ result: 'failure', data: { error: `timeout after ${timeoutMs}ms` } });
401
- }, timeoutMs);
402
-
403
- child.stdout.on('data', (d) => { stdout += d; });
404
- child.stderr.on('data', (d) => { stderr += d; });
405
-
406
- child.on('close', (code) => {
407
- if (settled) return;
408
- if (code !== 0) {
409
- settle({ result: 'failure', data: { error: stderr.trim() || `exit code ${code}` } });
410
- return;
411
- }
412
- try {
413
- const parsed = JSON.parse(stdout.trim());
414
- settle(typeof parsed?.result === 'string' ? parsed : { result: 'success', data: parsed });
415
- } catch {
416
- settle({ result: 'success', data: { stdout: stdout.trim() } });
417
- }
418
- });
419
-
420
- child.on('error', (err) => {
421
- settle({ result: 'failure', data: { error: err.message } });
422
- });
423
-
424
- try {
425
- child.stdin.end(stdinData);
426
- } catch (err) {
427
- settle({ result: 'failure', data: { error: `stdin write failed: ${err.message}` } });
428
- }
368
+ function invokeExecutionRefAsync(ref, args, opts) {
369
+ return invokeExecutionRef(ref, args, {
370
+ cliDir: opts?.cliDir || BOARD_ROOT,
371
+ cwd: opts?.cwd || BOARD_ROOT,
372
+ timeoutMs: typeof opts?.timeoutMs === 'number' ? opts.timeoutMs : undefined,
373
+ label: 'board-server-chat-flow',
429
374
  });
430
375
  }
431
376
 
@@ -469,7 +414,7 @@ const runtime = createMultiBoardServerRuntime({
469
414
  const boardDir = path.join(boardRoot, 'runtime');
470
415
  const runtimeCardsDir = path.join(boardRoot, 'cards');
471
416
  const flowRunner = createStepMachineChatFlowRunner({
472
- invokeRef: (ref, stepArgs) => invokeLocalNodeRefAsync(ref, stepArgs, {
417
+ invokeRef: (ref, stepArgs) => invokeExecutionRefAsync(ref, stepArgs, {
473
418
  cliDir: BOARD_ROOT,
474
419
  cwd: BOARD_ROOT,
475
420
  timeoutMs: chatInvokeRefTimeoutMs,
@@ -477,10 +422,12 @@ const runtime = createMultiBoardServerRuntime({
477
422
  });
478
423
  const baseExecutionExtra = {
479
424
  boardSetupRoot: boardRoot,
425
+ boardRuntimeDir: 'runtime',
426
+ runtimeStatusDir: 'runtime-out',
427
+ cardsDir: 'cards',
480
428
  projectRoot: BOARD_ROOT,
481
429
  chatFlowRoot,
482
430
  apiBasePath: `${apiBasePath}/${boardId}`,
483
- chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
484
431
  serverUrl: `http://127.0.0.1:${PORT}`,
485
432
  chatCopilotTimeoutMs,
486
433
  ...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
@@ -505,10 +452,11 @@ const runtime = createMultiBoardServerRuntime({
505
452
  serverUrl: `http://127.0.0.1:${PORT}`,
506
453
  executionExtra: {
507
454
  boardSetupRoot: boardRoot,
455
+ boardRuntimeDir: 'runtime',
456
+ runtimeStatusDir: 'runtime-out',
457
+ cardsDir: 'cards',
508
458
  projectRoot: BOARD_ROOT,
509
459
  chatFlowRoot,
510
- apiBasePath: `${apiBasePath}/${boardId}`,
511
- chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
512
460
  chatCopilotTimeoutMs,
513
461
  ...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
514
462
  },
@@ -38,11 +38,11 @@
38
38
  },
39
39
  "boardLiveCardsCliJs": {
40
40
  "type": "string",
41
- "description": "Absolute path to board-live-cards-cli.js when configured by the runtime."
41
+ "description": "Absolute path to board-live-cards-cli.js when injected by the runtime; for authored ExecutionRefs that target the packaged CLI, prefer kind 'yaml-flow-cli' with value 'board-live-cards-cli.js'."
42
42
  },
43
43
  "stepMachineCliPath": {
44
44
  "type": "string",
45
- "description": "Absolute path to step-machine-cli.js when configured by the runtime."
45
+ "description": "Absolute path to step-machine-cli.js when injected by the runtime; for authored ExecutionRefs that target the packaged CLI, prefer kind 'yaml-flow-cli' with value 'step-machine-cli.js'."
46
46
  }
47
47
  }
48
48
  },