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.
- package/browser/asset-integrity.json +3 -3
- package/browser/board-livecards-client.js +1 -1
- package/browser/board-livecards-localstorage.js +6 -4
- package/browser/live-cards.js +19 -25
- 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/board-live-cards-browser-adapter.js +3 -2
- 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-DCFBy4L8.d.ts} +151 -2
- package/cli/node/artifacts-store-cli.js +5 -5
- 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 +14 -8
- package/cli/node/card-store-cli.js +6 -6
- 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 +75 -75
- package/cli/node/fs-board-adapter.js +14 -10
- 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-Dc2VRMUw.d.ts} +27 -124
- package/examples/board/demo-shell-with-server.html +3 -196
- package/examples/board/doc.html +465 -0
- package/examples/board/server/board-server.js +30 -82
- 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-GD4H-fFp.d.ts → artifacts-store-lib-454TAuov.d.ts} +2 -32
- package/lib/{artifacts-store-lib-public-C5UL5tyG.d.cts → artifacts-store-lib-zsGFbBV8.d.cts} +2 -32
- package/lib/artifacts-store-public.d.cts +34 -3
- package/lib/artifacts-store-public.d.ts +34 -3
- package/lib/board-live-cards-node.cjs +14 -10
- package/lib/board-live-cards-node.d.cts +42 -21
- package/lib/board-live-cards-node.d.ts +42 -21
- package/lib/board-live-cards-node.js +14 -10
- package/lib/{board-live-cards-public-BLXbcBNk.d.cts → board-live-cards-public-BM6jCEIa.d.cts} +25 -5
- package/lib/{board-live-cards-public-BZaNb2mi.d.ts → board-live-cards-public-Bz07XKRK.d.ts} +25 -5
- package/lib/board-live-cards-public.cjs +2 -2
- package/lib/board-live-cards-public.d.cts +2 -2
- package/lib/board-live-cards-public.d.ts +2 -2
- package/lib/board-live-cards-public.js +2 -2
- package/lib/board-live-cards-server-runtime.cjs +4 -4
- package/lib/board-live-cards-server-runtime.d.cts +5 -3
- package/lib/board-live-cards-server-runtime.d.ts +5 -3
- package/lib/board-live-cards-server-runtime.js +4 -4
- package/lib/board-worker-adapter.cjs +2 -2
- package/lib/board-worker-adapter.js +2 -2
- package/lib/card-store-public.d.cts +2 -2
- package/lib/card-store-public.d.ts +2 -2
- package/lib/chat-storage-lib-DGaKrjVe.d.ts +53 -0
- package/lib/chat-storage-lib-OX0Q_Ttf.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.cjs +1 -1
- package/lib/execution-refs.d.cts +10 -1
- package/lib/execution-refs.d.ts +10 -1
- package/lib/execution-refs.js +1 -1
- package/lib/index.d.cts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/server-runtime/index.cjs +4 -4
- package/lib/server-runtime/index.d.cts +6 -4
- package/lib/server-runtime/index.d.ts +6 -4
- package/lib/server-runtime/index.js +4 -4
- package/lib/step-machine-public/index.cjs +3 -3
- package/lib/step-machine-public/index.d.cts +1 -1
- package/lib/step-machine-public/index.d.ts +1 -1
- package/lib/step-machine-public/index.js +3 -3
- package/lib/{storage-interface-B6ecOulj.d.ts → storage-interface-B-7pDHwD.d.cts} +36 -1
- package/lib/{storage-interface-B6ecOulj.d.cts → storage-interface-B-7pDHwD.d.ts} +36 -1
- package/lib/stores/index.d.cts +1 -1
- package/lib/stores/index.d.ts +1 -1
- package/lib/stores/kv.d.cts +1 -1
- package/lib/stores/kv.d.ts +1 -1
- package/lib/{types-D-xVWPdY.d.ts → types-Cn0b8G-i.d.ts} +16 -65
- package/lib/{types-Bztd1KoK.d.cts → types-Dszjwfud.d.cts} +16 -65
- 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.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) => 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';
|
|
@@ -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:
|
|
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: [
|
|
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: [
|
|
338
|
+
persistedBoardsConfig = { boards: [] };
|
|
327
339
|
}
|
|
328
340
|
}
|
|
329
341
|
|
|
@@ -350,82 +362,15 @@ serverMetaStore.putText(
|
|
|
350
362
|
);
|
|
351
363
|
|
|
352
364
|
/**
|
|
353
|
-
*
|
|
354
|
-
*
|
|
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
|
|
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
|
-
}
|
|
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) =>
|
|
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
|
|
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
|
},
|