yaml-flow 8.2.4 → 8.3.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 (77) 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 +4 -4
  4. package/browser/live-cards.js +19 -19
  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/card-store-browser-api.d.ts +1 -1
  8. package/cli/browser-api/card-store-browser-api.js +1 -1
  9. package/cli/{execution-interface-ftO1W7Po.d.ts → execution-interface-CrG5gzAx.d.ts} +116 -2
  10. package/cli/node/batch-runner-cli.d.ts +3 -0
  11. package/cli/node/batch-runner-cli.js +2 -1
  12. package/cli/node/board-live-cards-cli.js +7 -7
  13. package/cli/node/card-store-cli.js +5 -5
  14. package/cli/node/chat-store-cli.d.ts +23 -0
  15. package/cli/node/chat-store-cli.js +8 -0
  16. package/cli/node/execution-adapter.d.ts +4 -2
  17. package/cli/node/execution-adapter.js +2 -2
  18. package/cli/node/fs-board-adapter.d.ts +7 -6
  19. package/cli/node/fs-board-adapter.js +8 -8
  20. package/cli/node/source-cli-task-executor.js +4 -4
  21. package/cli/node/step-machine-cli.js +3 -3
  22. package/cli/{types--rXGWbSR.d.ts → types-PUfPBxc_.d.ts} +4 -109
  23. package/examples/board/demo-shell-with-server.html +3 -3
  24. package/examples/board/doc.html +465 -0
  25. package/examples/board/server/board-server.js +20 -81
  26. package/examples/board/server/board-worker/source_def_flows.json +2 -2
  27. package/examples/board/server/chat-flow/copilot-chat/assistant.js +44 -185
  28. package/examples/board/server/chat-flow/copilot-chat/copilot_wrapper.bat +157 -0
  29. package/examples/board/server/chat-flow/copilot-chat/copilot_wrapper_helper.ps1 +190 -0
  30. package/examples/board/server/chat-flow/flow-steps.json +122 -56
  31. package/examples/board/test/server-http-test.js +252 -220
  32. package/examples/board-local/demo-shell-localstorage.html +3 -3
  33. package/lib/{artifacts-store-lib-public-C5UL5tyG.d.cts → artifacts-store-lib-public-Cz8-kdXG.d.cts} +1 -1
  34. package/lib/{artifacts-store-lib-public-GD4H-fFp.d.ts → artifacts-store-lib-public-ksGIExhc.d.ts} +1 -1
  35. package/lib/artifacts-store-public.d.cts +2 -2
  36. package/lib/artifacts-store-public.d.ts +2 -2
  37. package/lib/board-live-cards-node.cjs +8 -8
  38. package/lib/board-live-cards-node.d.cts +26 -6
  39. package/lib/board-live-cards-node.d.ts +26 -6
  40. package/lib/board-live-cards-node.js +8 -8
  41. package/lib/{board-live-cards-public-BLXbcBNk.d.cts → board-live-cards-public-BwZYGAsF.d.cts} +1 -1
  42. package/lib/{board-live-cards-public-BZaNb2mi.d.ts → board-live-cards-public-DWpZVDXN.d.ts} +1 -1
  43. package/lib/board-live-cards-public.cjs +2 -2
  44. package/lib/board-live-cards-public.d.cts +1 -1
  45. package/lib/board-live-cards-public.d.ts +1 -1
  46. package/lib/board-live-cards-public.js +2 -2
  47. package/lib/board-live-cards-server-runtime.cjs +3 -3
  48. package/lib/board-live-cards-server-runtime.d.cts +3 -2
  49. package/lib/board-live-cards-server-runtime.d.ts +3 -2
  50. package/lib/board-live-cards-server-runtime.js +3 -3
  51. package/lib/board-worker-adapter.cjs +2 -2
  52. package/lib/board-worker-adapter.js +2 -2
  53. package/lib/card-store-public.d.cts +1 -1
  54. package/lib/card-store-public.d.ts +1 -1
  55. package/lib/chat-storage-lib-BK5Njslc.d.ts +53 -0
  56. package/lib/chat-storage-lib-C5bQ7bGe.d.cts +53 -0
  57. package/lib/chat-store-public.cjs +2 -0
  58. package/lib/chat-store-public.d.cts +128 -0
  59. package/lib/chat-store-public.d.ts +128 -0
  60. package/lib/chat-store-public.js +2 -0
  61. package/lib/execution-refs.d.cts +10 -1
  62. package/lib/execution-refs.d.ts +10 -1
  63. package/lib/server-runtime/index.cjs +3 -3
  64. package/lib/server-runtime/index.d.cts +4 -3
  65. package/lib/server-runtime/index.d.ts +4 -3
  66. package/lib/server-runtime/index.js +3 -3
  67. package/lib/{types-Bztd1KoK.d.cts → types-D9B0Vrwg.d.cts} +4 -53
  68. package/lib/{types-D-xVWPdY.d.ts → types-DNYhCFNJ.d.ts} +4 -53
  69. package/package.json +8 -2
  70. package/examples/board/.board-ws/cards/store/_index.json +0 -17
  71. package/examples/board/.board-ws/cards/store/card-market-prices.json +0 -80
  72. package/examples/board/.board-ws/cards/store/card-portfolio-value.json +0 -90
  73. package/examples/board/.board-ws/cards/store/card-portfolio.json +0 -78
  74. package/examples/board/server/chat-flow/chat-clear-processing.js +0 -41
  75. package/examples/board/server/chat-flow/chat-open-turn.js +0 -144
  76. package/examples/board/server/chat-flow/chat-write-assistant.js +0 -44
  77. 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.3.0/browser/live-cards.js"></script>
41
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@8.3.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';
@@ -315,7 +318,7 @@ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow,
315
318
 
316
319
  // Pre-register configured boards in the server meta store
317
320
  const persistedBoardsConfigText = serverMetaStore.getText('boards-config.json');
318
- let persistedBoardsConfig = { boards: [{ id: 'default', label: 'Default Board' }] };
321
+ let persistedBoardsConfig = { boards: [] };
319
322
  if (persistedBoardsConfigText) {
320
323
  try {
321
324
  const parsedBoardsConfig = JSON.parse(persistedBoardsConfigText);
@@ -323,7 +326,7 @@ if (persistedBoardsConfigText) {
323
326
  persistedBoardsConfig = parsedBoardsConfig;
324
327
  }
325
328
  } catch {
326
- persistedBoardsConfig = { boards: [{ id: 'default', label: 'Default Board' }] };
329
+ persistedBoardsConfig = { boards: [] };
327
330
  }
328
331
  }
329
332
 
@@ -350,82 +353,15 @@ serverMetaStore.putText(
350
353
  );
351
354
 
352
355
  /**
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).
356
+ * Thin wrapper around the shared execution-ref invoker that pins the board
357
+ * server's cliDir/cwd/label defaults for chat-flow steps.
356
358
  */
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
- }
359
+ function invokeExecutionRefAsync(ref, args, opts) {
360
+ return invokeExecutionRef(ref, args, {
361
+ cliDir: opts?.cliDir || BOARD_ROOT,
362
+ cwd: opts?.cwd || BOARD_ROOT,
363
+ timeoutMs: typeof opts?.timeoutMs === 'number' ? opts.timeoutMs : undefined,
364
+ label: 'board-server-chat-flow',
429
365
  });
430
366
  }
431
367
 
@@ -469,7 +405,7 @@ const runtime = createMultiBoardServerRuntime({
469
405
  const boardDir = path.join(boardRoot, 'runtime');
470
406
  const runtimeCardsDir = path.join(boardRoot, 'cards');
471
407
  const flowRunner = createStepMachineChatFlowRunner({
472
- invokeRef: (ref, stepArgs) => invokeLocalNodeRefAsync(ref, stepArgs, {
408
+ invokeRef: (ref, stepArgs) => invokeExecutionRefAsync(ref, stepArgs, {
473
409
  cliDir: BOARD_ROOT,
474
410
  cwd: BOARD_ROOT,
475
411
  timeoutMs: chatInvokeRefTimeoutMs,
@@ -477,10 +413,12 @@ const runtime = createMultiBoardServerRuntime({
477
413
  });
478
414
  const baseExecutionExtra = {
479
415
  boardSetupRoot: boardRoot,
416
+ boardRuntimeDir: 'runtime',
417
+ runtimeStatusDir: 'runtime-out',
418
+ cardsDir: 'cards',
480
419
  projectRoot: BOARD_ROOT,
481
420
  chatFlowRoot,
482
421
  apiBasePath: `${apiBasePath}/${boardId}`,
483
- chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
484
422
  serverUrl: `http://127.0.0.1:${PORT}`,
485
423
  chatCopilotTimeoutMs,
486
424
  ...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
@@ -505,10 +443,11 @@ const runtime = createMultiBoardServerRuntime({
505
443
  serverUrl: `http://127.0.0.1:${PORT}`,
506
444
  executionExtra: {
507
445
  boardSetupRoot: boardRoot,
446
+ boardRuntimeDir: 'runtime',
447
+ runtimeStatusDir: 'runtime-out',
448
+ cardsDir: 'cards',
508
449
  projectRoot: BOARD_ROOT,
509
450
  chatFlowRoot,
510
- apiBasePath: `${apiBasePath}/${boardId}`,
511
- chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
512
451
  chatCopilotTimeoutMs,
513
452
  ...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
514
453
  },
@@ -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
  },