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.
- package/browser/asset-integrity.json +3 -3
- package/browser/board-livecards-client.js +1 -1
- package/browser/board-livecards-localstorage.js +4 -4
- package/browser/live-cards.js +19 -19
- package/cli/{board-live-cards-lib-Iq_XAC09.d.ts → board-live-cards-lib-tjYsPt5U.d.ts} +1 -1
- package/cli/browser-api/board-live-cards-browser-adapter.d.ts +3 -3
- package/cli/browser-api/card-store-browser-api.d.ts +1 -1
- package/cli/browser-api/card-store-browser-api.js +1 -1
- package/cli/{execution-interface-ftO1W7Po.d.ts → execution-interface-CrG5gzAx.d.ts} +116 -2
- package/cli/node/batch-runner-cli.d.ts +3 -0
- package/cli/node/batch-runner-cli.js +2 -1
- package/cli/node/board-live-cards-cli.js +7 -7
- package/cli/node/card-store-cli.js +5 -5
- package/cli/node/chat-store-cli.d.ts +23 -0
- package/cli/node/chat-store-cli.js +8 -0
- package/cli/node/execution-adapter.d.ts +4 -2
- package/cli/node/execution-adapter.js +2 -2
- package/cli/node/fs-board-adapter.d.ts +7 -6
- package/cli/node/fs-board-adapter.js +8 -8
- package/cli/node/source-cli-task-executor.js +4 -4
- package/cli/node/step-machine-cli.js +3 -3
- package/cli/{types--rXGWbSR.d.ts → types-PUfPBxc_.d.ts} +4 -109
- package/examples/board/demo-shell-with-server.html +3 -3
- package/examples/board/doc.html +465 -0
- package/examples/board/server/board-server.js +20 -81
- package/examples/board/server/board-worker/source_def_flows.json +2 -2
- package/examples/board/server/chat-flow/copilot-chat/assistant.js +44 -185
- package/examples/board/server/chat-flow/copilot-chat/copilot_wrapper.bat +157 -0
- package/examples/board/server/chat-flow/copilot-chat/copilot_wrapper_helper.ps1 +190 -0
- package/examples/board/server/chat-flow/flow-steps.json +122 -56
- package/examples/board/test/server-http-test.js +252 -220
- package/examples/board-local/demo-shell-localstorage.html +3 -3
- package/lib/{artifacts-store-lib-public-C5UL5tyG.d.cts → artifacts-store-lib-public-Cz8-kdXG.d.cts} +1 -1
- package/lib/{artifacts-store-lib-public-GD4H-fFp.d.ts → artifacts-store-lib-public-ksGIExhc.d.ts} +1 -1
- package/lib/artifacts-store-public.d.cts +2 -2
- package/lib/artifacts-store-public.d.ts +2 -2
- package/lib/board-live-cards-node.cjs +8 -8
- package/lib/board-live-cards-node.d.cts +26 -6
- package/lib/board-live-cards-node.d.ts +26 -6
- package/lib/board-live-cards-node.js +8 -8
- package/lib/{board-live-cards-public-BLXbcBNk.d.cts → board-live-cards-public-BwZYGAsF.d.cts} +1 -1
- package/lib/{board-live-cards-public-BZaNb2mi.d.ts → board-live-cards-public-DWpZVDXN.d.ts} +1 -1
- package/lib/board-live-cards-public.cjs +2 -2
- package/lib/board-live-cards-public.d.cts +1 -1
- package/lib/board-live-cards-public.d.ts +1 -1
- package/lib/board-live-cards-public.js +2 -2
- package/lib/board-live-cards-server-runtime.cjs +3 -3
- package/lib/board-live-cards-server-runtime.d.cts +3 -2
- package/lib/board-live-cards-server-runtime.d.ts +3 -2
- package/lib/board-live-cards-server-runtime.js +3 -3
- package/lib/board-worker-adapter.cjs +2 -2
- package/lib/board-worker-adapter.js +2 -2
- package/lib/card-store-public.d.cts +1 -1
- package/lib/card-store-public.d.ts +1 -1
- package/lib/chat-storage-lib-BK5Njslc.d.ts +53 -0
- package/lib/chat-storage-lib-C5bQ7bGe.d.cts +53 -0
- package/lib/chat-store-public.cjs +2 -0
- package/lib/chat-store-public.d.cts +128 -0
- package/lib/chat-store-public.d.ts +128 -0
- package/lib/chat-store-public.js +2 -0
- package/lib/execution-refs.d.cts +10 -1
- package/lib/execution-refs.d.ts +10 -1
- package/lib/server-runtime/index.cjs +3 -3
- package/lib/server-runtime/index.d.cts +4 -3
- package/lib/server-runtime/index.d.ts +4 -3
- package/lib/server-runtime/index.js +3 -3
- package/lib/{types-Bztd1KoK.d.cts → types-D9B0Vrwg.d.cts} +4 -53
- package/lib/{types-D-xVWPdY.d.ts → types-DNYhCFNJ.d.ts} +4 -53
- package/package.json +8 -2
- package/examples/board/.board-ws/cards/store/_index.json +0 -17
- package/examples/board/.board-ws/cards/store/card-market-prices.json +0 -80
- package/examples/board/.board-ws/cards/store/card-portfolio-value.json +0 -90
- package/examples/board/.board-ws/cards/store/card-portfolio.json +0 -78
- package/examples/board/server/chat-flow/chat-clear-processing.js +0 -41
- package/examples/board/server/chat-flow/chat-open-turn.js +0 -144
- package/examples/board/server/chat-flow/chat-write-assistant.js +0 -44
- 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) => 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) => 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
|
+
() => 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: [
|
|
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: [
|
|
329
|
+
persistedBoardsConfig = { boards: [] };
|
|
327
330
|
}
|
|
328
331
|
}
|
|
329
332
|
|
|
@@ -350,82 +353,15 @@ serverMetaStore.putText(
|
|
|
350
353
|
);
|
|
351
354
|
|
|
352
355
|
/**
|
|
353
|
-
*
|
|
354
|
-
*
|
|
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
|
|
358
|
-
return
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
},
|