yaml-flow 5.2.6 → 5.2.8

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 (84) hide show
  1. package/README.md +6 -6
  2. package/board-livecards-server-runtime.js +260 -35
  3. package/browser/board-livegraph-engine.js +57 -32
  4. package/browser/board-livegraph-engine.js.map +1 -1
  5. package/browser/card-compute.js +17 -17
  6. package/browser/live-cards.js +139 -12
  7. package/browser/live-cards.schema.json +14 -9
  8. package/dist/board-livegraph-runtime/index.cjs +57 -32
  9. package/dist/board-livegraph-runtime/index.cjs.map +1 -1
  10. package/dist/board-livegraph-runtime/index.d.cts +1 -1
  11. package/dist/board-livegraph-runtime/index.d.ts +1 -1
  12. package/dist/board-livegraph-runtime/index.js +57 -32
  13. package/dist/board-livegraph-runtime/index.js.map +1 -1
  14. package/dist/card-compute/index.cjs +96 -38
  15. package/dist/card-compute/index.cjs.map +1 -1
  16. package/dist/card-compute/index.d.cts +13 -8
  17. package/dist/card-compute/index.d.ts +13 -8
  18. package/dist/card-compute/index.js +96 -38
  19. package/dist/card-compute/index.js.map +1 -1
  20. package/dist/cli/board-live-cards-cli.cjs +7200 -201
  21. package/dist/cli/board-live-cards-cli.cjs.map +1 -1
  22. package/dist/cli/board-live-cards-cli.d.cts +6 -6
  23. package/dist/cli/board-live-cards-cli.d.ts +6 -6
  24. package/dist/cli/board-live-cards-cli.js +7199 -201
  25. package/dist/cli/board-live-cards-cli.js.map +1 -1
  26. package/dist/continuous-event-graph/index.cjs +55 -30
  27. package/dist/continuous-event-graph/index.cjs.map +1 -1
  28. package/dist/continuous-event-graph/index.d.cts +2 -2
  29. package/dist/continuous-event-graph/index.d.ts +2 -2
  30. package/dist/continuous-event-graph/index.js +55 -30
  31. package/dist/continuous-event-graph/index.js.map +1 -1
  32. package/dist/index.cjs +121 -53
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.js +121 -53
  37. package/dist/index.js.map +1 -1
  38. package/dist/{live-cards-bridge-CeNxiVcm.d.ts → live-cards-bridge-EQjytzI_.d.ts} +10 -5
  39. package/dist/{live-cards-bridge-z_rJCSbi.d.cts → live-cards-bridge-x5XREkXm.d.cts} +10 -5
  40. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +1 -1
  41. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +1 -1
  42. package/examples/browser/boards/portfolio-tracker/cards/portfolio-risk-assessment.json +1 -1
  43. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +1 -1
  44. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +2 -2
  45. package/examples/browser/boards/portfolio-tracker/cards/rebalancing-strategy.json +1 -1
  46. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +10 -10
  47. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
  48. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +1 -1
  49. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
  50. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +2 -2
  51. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +1 -1
  52. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +1 -1
  53. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +1 -1
  54. package/examples/example-board/agent-instructions-cardlayout.md +1 -1
  55. package/examples/example-board/agent-instructions.md +271 -45
  56. package/examples/example-board/cards/card-concentration.json +8 -5
  57. package/examples/example-board/cards/card-market-prices.json +14 -9
  58. package/examples/example-board/cards/card-my-identity.json +28 -0
  59. package/examples/example-board/cards/card-portfolio-value.json +1 -1
  60. package/examples/example-board/cards/card-portfolio.json +1 -1
  61. package/examples/example-board/cards/card-rebalance-impact.json +65 -0
  62. package/examples/example-board/cards/card-rebalance-sim.json +57 -0
  63. package/examples/example-board/demo-chat-handler.js +2 -1
  64. package/examples/example-board/demo-server-config.json +6 -1
  65. package/examples/example-board/demo-server.js +79 -8
  66. package/examples/example-board/demo-shell-browser.html +6 -6
  67. package/examples/example-board/demo-shell-with-server.html +4 -4
  68. package/examples/example-board/demo-task-executor.js +436 -246
  69. package/examples/example-board/scripts/copilot_wrapper.bat +16 -0
  70. package/examples/example-board/scripts/copilot_wrapper_helper.ps1 +19 -10
  71. package/examples/example-board/scripts/workiq_wrapper.mjs +66 -0
  72. package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +5 -5
  73. package/examples/npm-libs/continuous-event-graph/soc-incident-board.ts +3 -3
  74. package/examples/npm-libs/event-graph/research-pipeline.ts +5 -5
  75. package/examples/npm-libs/graph-of-graphs/multi-stage-etl.ts +9 -9
  76. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +1 -1
  77. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +1 -1
  78. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +1 -1
  79. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +3 -3
  80. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +1 -1
  81. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +1 -1
  82. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +1 -1
  83. package/package.json +2 -2
  84. package/schema/live-cards.schema.json +14 -9
@@ -0,0 +1,65 @@
1
+ {
2
+ "id": "card-rebalance-impact",
3
+ "meta": {
4
+ "title": "Rebalance Impact",
5
+ "tags": ["portfolio", "simulation"],
6
+ "simulation": true,
7
+ "desc": "Before/after impact summary of the proposed rebalance scenario."
8
+ },
9
+ "requires": ["positions", "proposed_trades"],
10
+ "compute": [
11
+ {
12
+ "bindTo": "total_current_value",
13
+ "expr": "$round($sum(requires.positions.value), 2)"
14
+ },
15
+ {
16
+ "bindTo": "trade_count",
17
+ "expr": "$count(requires.proposed_trades)"
18
+ },
19
+ {
20
+ "bindTo": "total_trade_value",
21
+ "expr": "$round($sum(requires.proposed_trades.trade_value), 2)"
22
+ },
23
+ {
24
+ "bindTo": "buys",
25
+ "expr": "$count($filter(requires.proposed_trades, function($t){ $t.direction = 'increase' }))"
26
+ },
27
+ {
28
+ "bindTo": "sells",
29
+ "expr": "$count($filter(requires.proposed_trades, function($t){ $t.direction = 'reduce' }))"
30
+ },
31
+ {
32
+ "bindTo": "max_weight_before",
33
+ "expr": "($tv := $sum(requires.positions.value); $round($max(requires.positions.value) / $tv * 100, 1))"
34
+ },
35
+ {
36
+ "bindTo": "post_positions",
37
+ "expr": "($tMap := $merge(requires.proposed_trades.{ticker: $}); $map(requires.positions, function($p) { ($t := $lookup($tMap, $p.ticker); $newQty := $t ? $t.proposed_qty : $p.quantity; {\"ticker\": $p.ticker, \"value\": $round($newQty * $p.price, 2)}) }))"
38
+ },
39
+ {
40
+ "bindTo": "max_weight_after",
41
+ "expr": "($tv := $sum(computed_values.post_positions.value); $round($max(computed_values.post_positions.value) / $tv * 100, 1))"
42
+ },
43
+ {
44
+ "bindTo": "est_tax_impact",
45
+ "expr": "$round($sum($filter(requires.proposed_trades, function($t){ $t.direction = 'reduce' }).trade_value) * 0.15, 2)"
46
+ },
47
+ {
48
+ "bindTo": "summary_md",
49
+ "expr": "'| Metric | Before | After |\\n|---|---|---|\\n| Top holding weight | ' & $string(computed_values.max_weight_before) & '% | ' & $string(computed_values.max_weight_after) & '% |\\n| Portfolio value | $' & $string(computed_values.total_current_value) & ' | ~$' & $string($round(computed_values.total_current_value - computed_values.est_tax_impact, 2)) & ' |\\n\\n**Trades:** ' & $string(computed_values.trade_count) & ' (' & $string(computed_values.buys) & ' buys, ' & $string(computed_values.sells) & ' sells) \\n**Total trade value:** $' & $string(computed_values.total_trade_value) & ' \\n**Est. tax impact:** ~$' & $string(computed_values.est_tax_impact)"
50
+ }
51
+ ],
52
+ "view": {
53
+ "elements": [
54
+ {
55
+ "kind": "markdown",
56
+ "data": { "bind": "computed_values.summary_md" }
57
+ }
58
+ ],
59
+ "layout": {
60
+ "board": { "col": 6, "order": 8 },
61
+ "canvas": { "x": 1360, "y": 480, "w": 400, "h": 280 }
62
+ }
63
+ },
64
+ "card_data": {}
65
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "id": "card-rebalance-sim",
3
+ "meta": {
4
+ "title": "Rebalance Proposal",
5
+ "tags": ["portfolio", "simulation"],
6
+ "simulation": true,
7
+ "desc": "AI-proposed rebalance trades based on portfolio intelligence. Edit quantities before pinning."
8
+ },
9
+ "requires": ["holdings", "portfolio_mix", "portfolio_risks", "portfolio_action", "positions"],
10
+ "source_defs": [
11
+ {
12
+ "bindTo": "rebalance",
13
+ "outputFile": "card-rebalance-proposal.json",
14
+ "projections": {
15
+ "holdings": "requires.holdings",
16
+ "positions": "requires.positions",
17
+ "portfolio_mix": "requires.portfolio_mix",
18
+ "portfolio_risks": "requires.portfolio_risks",
19
+ "portfolio_action": "requires.portfolio_action"
20
+ },
21
+ "copilot": {
22
+ "prompt_template": "You are a portfolio rebalance advisor. Using the intelligence analysis and current holdings below, propose specific trades.\n\nCurrent holdings:\n{{holdings}}\nFields: ticker, quantity, cost_basis ($).\n\nPositions (with live prices):\n{{positions}}\nFields: ticker, quantity, cost_basis, price ($), value ($), gain_$, gain_%, chg_$, chg_pct.\n\nPortfolio intelligence:\n- Mix: {{portfolio_mix}}\n- Risks: {{portfolio_risks}}\n- Action signal: {{portfolio_action}}\n\nIMPORTANT OUTPUT RULES:\n- Return ONLY a valid JSON object. No markdown, no preamble, no trailing text.\n- Start with { and end with }.\n- proposed_trades must be an array of objects with EXACTLY these fields per entry:\n ticker (string), direction (\"reduce\" or \"increase\"), current_qty (number, current shares held), change (number, shares to trade), proposed_qty (number, resulting shares after trade), price (number, current price per share), trade_value (number, change * price rounded to 2 decimals), reason (string, one short sentence).\n- Include one entry per ticker you recommend changing. Do not include tickers with no change.\n\n{\n \"proposed_trades\": [\n {\"ticker\": \"AAPL\", \"direction\": \"reduce\", \"current_qty\": 15, \"change\": 5, \"proposed_qty\": 10, \"price\": 210.50, \"trade_value\": 1052.50, \"reason\": \"overweight at 41%, trim before earnings\"}\n ]\n}"
23
+ }
24
+ }
25
+ ],
26
+ "provides": [
27
+ { "bindTo": "proposed_trades", "ref": "fetched_sources.rebalance.proposed_trades" }
28
+ ],
29
+ "view": {
30
+ "elements": [
31
+ {
32
+ "kind": "editable-table",
33
+ "label": "Proposed Trades",
34
+ "data": {
35
+ "bind": "fetched_sources.rebalance.proposed_trades",
36
+ "writeTo": "card_data.proposed_trades",
37
+ "columns": ["ticker", "direction", "current_qty", "change", "proposed_qty", "price", "trade_value", "reason"],
38
+ "schema": {
39
+ "properties": {
40
+ "current_qty": { "type": "number" },
41
+ "change": { "type": "number" },
42
+ "proposed_qty": { "type": "number" },
43
+ "price": { "type": "number" },
44
+ "trade_value": { "type": "number" }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ],
50
+ "layout": {
51
+ "board": { "col": 6, "order": 7 },
52
+ "canvas": { "x": 840, "y": 480, "w": 500, "h": 280 }
53
+ },
54
+ "features": { "refresh": true, "notes": true }
55
+ },
56
+ "card_data": {}
57
+ }
@@ -11,6 +11,7 @@
11
11
  * cardsDir — relative subdir: 'surface/tmp-cards'
12
12
  * chatDir — absolute path to the card's chats directory
13
13
  * lastChatFile — filename of the just-written user message, e.g. '001_user.txt'
14
+ * serverUrl — base URL of hosting server (e.g. http://127.0.0.1:7799), optional
14
15
  *
15
16
  * Invokes copilot_wrapper.bat with a prompt built from conversation history.
16
17
  * Session dir is per-card: os.tmpdir()/demo-chat-handler-sessions/<boardId>_<cardId>
@@ -42,7 +43,7 @@ let extra = {};
42
43
  try { extra = JSON.parse(Buffer.from(extraStr, 'base64').toString('utf-8')); }
43
44
  catch { console.error('[demo-chat-handler] bad --extraEncJson'); process.exit(0); }
44
45
 
45
- const { boardSetupRoot, boardRuntimeDir, runtimeStatusDir, cardsDir, chatDir, lastChatFile } = extra;
46
+ const { boardSetupRoot, boardRuntimeDir, runtimeStatusDir, cardsDir, chatDir, lastChatFile, serverUrl } = extra;
46
47
  if (!boardSetupRoot || !chatDir || !lastChatFile) {
47
48
  console.error('[demo-chat-handler] missing boardSetupRoot/chatDir/lastChatFile');
48
49
  process.exit(0);
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "port": 7799,
3
+ "cardsDir": "./cards",
3
4
  "taskExecutorPath": "./demo-task-executor.js",
4
- "chatHandlerPath": "./demo-chat-handler.js"
5
+ "chatHandlerPath": "./demo-chat-handler.js",
6
+ "chatSessionsDir": "",
7
+ "gandalfCardsDir": "./gandalf-cards",
8
+ "gandalfTaskExecutorPath": "./demo-task-executor.js",
9
+ "gandalfChatHandlerPath": "./demo-chat-handler.js"
5
10
  }
@@ -4,6 +4,7 @@ import http from 'node:http';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import os from 'node:os';
7
+ import { spawnSync, spawn } from 'node:child_process';
7
8
  import { fileURLToPath } from 'node:url';
8
9
  import { createRequire } from 'node:module';
9
10
 
@@ -48,10 +49,15 @@ function resolveFromConfig(configValue) {
48
49
 
49
50
  const serverConfig = loadServerConfig();
50
51
  const configuredCliJs = resolveFromConfig(serverConfig.boardLiveCardsCliJs) || _pkgCliJs;
52
+ const configuredCardsDir = resolveFromConfig(serverConfig.cardsDir);
51
53
  const configuredTaskExecutorPath = resolveFromConfig(serverConfig.taskExecutorPath || serverConfig.demoTaskExecutorPath);
52
54
  const configuredStepMachineCliPath = resolveFromConfig(serverConfig.stepMachineCliPath) || _pkgStepMachineCli;
53
55
  const configuredChatHandlerPath = resolveFromConfig(serverConfig.chatHandlerPath);
54
56
  const configuredInferenceAdapterPath = resolveFromConfig(serverConfig.inferenceAdapterPath);
57
+ const configuredGandalfCardsDir = resolveFromConfig(serverConfig.gandalfCardsDir);
58
+ const configuredGandalfTaskExecutorPath = resolveFromConfig(serverConfig.gandalfTaskExecutorPath);
59
+ const configuredGandalfChatHandlerPath = resolveFromConfig(serverConfig.gandalfChatHandlerPath);
60
+ const configuredGandalfInferenceAdapterPath = resolveFromConfig(serverConfig.gandalfInferenceAdapterPath);
55
61
 
56
62
  if (!process.env.BOARD_LIVE_CARDS_CLI_JS && configuredCliJs) {
57
63
  process.env.BOARD_LIVE_CARDS_CLI_JS = configuredCliJs;
@@ -77,11 +83,16 @@ const CORS_HEADERS = {
77
83
 
78
84
  const runtime = createMultiBoardServerRuntime({
79
85
  apiBasePath: '/api/boards',
80
- defaultCardsDir: path.join(__dirname, 'cards'),
81
- defaultTaskExecutorPath: process.env.DEMO_TASK_EXECUTOR_PATH || configuredTaskExecutorPath || path.join(__dirname, 'demo-task-executor.js'),
86
+ serverUrl: `http://127.0.0.1:${PORT}`,
87
+ defaultCardsDir: process.env.DEMO_CARDS_DIR || configuredCardsDir || null,
88
+ defaultTaskExecutorPath: process.env.DEMO_TASK_EXECUTOR_PATH || configuredTaskExecutorPath || null,
82
89
  defaultStepMachineCliPath: process.env.DEMO_STEP_MACHINE_CLI_PATH || configuredStepMachineCliPath,
83
- defaultChatHandlerPath: process.env.DEMO_CHAT_HANDLER_PATH || configuredChatHandlerPath || path.join(__dirname, 'demo-chat-handler.js'),
90
+ defaultChatHandlerPath: process.env.DEMO_CHAT_HANDLER_PATH || configuredChatHandlerPath || null,
84
91
  defaultInferenceAdapterPath: process.env.DEMO_INFERENCE_ADAPTER_PATH || configuredInferenceAdapterPath || null,
92
+ defaultGandalfCardsDir: process.env.DEMO_GANDALF_CARDS_DIR || configuredGandalfCardsDir || null,
93
+ defaultGandalfTaskExecutorPath: process.env.DEMO_GANDALF_TASK_EXECUTOR_PATH || configuredGandalfTaskExecutorPath || null,
94
+ defaultGandalfChatHandlerPath: process.env.DEMO_GANDALF_CHAT_HANDLER_PATH || configuredGandalfChatHandlerPath || null,
95
+ defaultGandalfInferenceAdapterPath: process.env.DEMO_GANDALF_INFERENCE_ADAPTER_PATH || configuredGandalfInferenceAdapterPath || null,
85
96
  boardLiveCardsCliJs: process.env.BOARD_LIVE_CARDS_CLI_JS || configuredCliJs,
86
97
  });
87
98
 
@@ -91,10 +102,12 @@ function resetRuntime() {
91
102
  fs.rmSync(setupDir, { recursive: true, force: true });
92
103
  console.log(`[demo-server] reset: wiped ${setupDir}`);
93
104
  }
94
- const chatSessions = path.join(os.tmpdir(), 'demo-chat-handler-sessions');
95
- if (fs.existsSync(chatSessions)) {
96
- fs.rmSync(chatSessions, { recursive: true, force: true });
97
- console.log(`[demo-server] reset: wiped ${chatSessions}`);
105
+ const chatSessionsDir = serverConfig.chatSessionsDir
106
+ ? path.resolve(__dirname, serverConfig.chatSessionsDir)
107
+ : path.join(os.tmpdir(), 'demo-chat-handler-sessions');
108
+ if (fs.existsSync(chatSessionsDir)) {
109
+ fs.rmSync(chatSessionsDir, { recursive: true, force: true });
110
+ console.log(`[demo-server] reset: wiped ${chatSessionsDir}`);
98
111
  }
99
112
  }
100
113
 
@@ -118,7 +131,7 @@ async function handleDemoSetup(req, res, boardId) {
118
131
  const { service, boardRoot } = runtime.requireBoardService(boardId);
119
132
  let setupPerformed = false;
120
133
 
121
- if (!fs.existsSync(path.join(boardRoot, 'surface', 'tmp-cards'))) {
134
+ if (!service.isDemoSetupDone()) {
122
135
  service.ensureDemoSetup();
123
136
  setupPerformed = true;
124
137
  }
@@ -129,6 +142,57 @@ async function handleDemoSetup(req, res, boardId) {
129
142
  }
130
143
  }
131
144
 
145
+ async function handleWorkiqAsk(req, res) {
146
+ let body = '';
147
+ for await (const chunk of req) body += chunk;
148
+ let query;
149
+ try {
150
+ query = JSON.parse(body).query;
151
+ } catch {
152
+ return jsonReply(res, 400, { error: 'Invalid JSON body' });
153
+ }
154
+ if (!query || typeof query !== 'string') {
155
+ return jsonReply(res, 400, { error: '{ query } string is required' });
156
+ }
157
+
158
+ const workiqJs = path.join(
159
+ process.env.APPDATA || os.homedir(),
160
+ 'npm', 'node_modules', '@microsoft', 'workiq', 'bin', 'workiq.js'
161
+ );
162
+ if (!fs.existsSync(workiqJs)) {
163
+ return jsonReply(res, 503, { error: `WorkIQ CLI not found at: ${workiqJs}` });
164
+ }
165
+
166
+ // Server has TTY on stdin — workiq can produce output.
167
+ // Use async spawn (not spawnSync) to avoid blocking the event loop during the call.
168
+ await new Promise((resolve) => {
169
+ let stdout = '';
170
+ let stderr = '';
171
+ const child = spawn(process.execPath, [workiqJs, 'ask', '-q', query], {
172
+ stdio: ['inherit', 'pipe', 'pipe'],
173
+ });
174
+ child.stdout.on('data', chunk => { stdout += chunk; });
175
+ child.stderr.on('data', chunk => { stderr += chunk; });
176
+ child.on('error', (err) => {
177
+ jsonReply(res, 500, { error: `workiq spawn error: ${err.message}` });
178
+ resolve();
179
+ });
180
+ child.on('close', (code) => {
181
+ if (code !== 0) {
182
+ jsonReply(res, 500, { error: `workiq exited ${code}`, stderr });
183
+ } else {
184
+ jsonReply(res, 200, { response: stdout });
185
+ }
186
+ resolve();
187
+ });
188
+ setTimeout(() => {
189
+ child.kill();
190
+ jsonReply(res, 504, { error: 'workiq timed out after 60s' });
191
+ resolve();
192
+ }, 60_000);
193
+ });
194
+ }
195
+
132
196
  const server = http.createServer((req, res) => {
133
197
  const method = req.method || 'GET';
134
198
  const pathname = new URL(req.url || '/', 'http://localhost').pathname;
@@ -139,6 +203,12 @@ const server = http.createServer((req, res) => {
139
203
  return;
140
204
  }
141
205
 
206
+ // Route: POST /api/workiq/ask — proxy to WorkIQ (M365 Copilot) from server TTY
207
+ if (method === 'POST' && pathname === '/api/workiq/ask') {
208
+ void handleWorkiqAsk(req, res);
209
+ return;
210
+ }
211
+
142
212
  // Route: demo-setup is handled here in demo-server (host concern)
143
213
  const boardSegMatch = pathname.match(BOARD_SEG_RE);
144
214
  if (boardSegMatch && boardSegMatch[2] === 'demo-setup') {
@@ -166,4 +236,5 @@ server.listen(PORT, '127.0.0.1', () => {
166
236
  console.log(` POST ${runtime.apiBasePath}/:boardId/cards/:id/files`);
167
237
  console.log(` GET ${runtime.apiBasePath}/:boardId/cards/:id/files/:idx`);
168
238
  console.log(` GET ${runtime.apiBasePath}/:boardId/cards/:id/chats`);
239
+ console.log(` POST /api/workiq/ask {query} <- WorkIQ (M365 Copilot) proxy`);
169
240
  });
@@ -6,9 +6,9 @@
6
6
  <title>Example Board Demo (Browser Runtime)</title>
7
7
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
8
8
  <script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
9
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/card-compute.js"></script>
10
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/live-cards.js"></script>
11
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/board-livegraph-engine.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/card-compute.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/live-cards.js"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/board-livegraph-engine.js"></script>
12
12
  </head>
13
13
  <body class="bg-light">
14
14
  <div class="container-fluid py-3">
@@ -31,7 +31,7 @@
31
31
  </div>
32
32
 
33
33
  <div class="alert alert-info small py-2 mb-3">
34
- Browser runtime mode: sources are executed in-browser through an opaque task executor.
34
+ Browser runtime mode: source_defs are executed in-browser through an opaque task executor.
35
35
  Card source definitions are treated as task-executor-owned metadata.
36
36
  </div>
37
37
 
@@ -341,7 +341,7 @@
341
341
  return cards.every((card) => {
342
342
  const artifact = cardRuntimeById[card.id];
343
343
  if (!artifact || typeof artifact !== 'object') return false;
344
- if (card.sources && card.sources.length > 0) {
344
+ if (card.source_defs && card.source_defs.length > 0) {
345
345
  if (!(artifact.fetched_sources && typeof artifact.fetched_sources === 'object' && !Array.isArray(artifact.fetched_sources))) return false;
346
346
  }
347
347
  if (card.requires && card.requires.length > 0) {
@@ -517,7 +517,7 @@
517
517
  runtime = createBoardLiveGraphRuntime(loaded.cards, {
518
518
  taskExecutor: async function ({ card }) {
519
519
  const out = {};
520
- for (const sourceDef of (card.sources || [])) {
520
+ for (const sourceDef of (card.source_defs || [])) {
521
521
  const payload = await mockServer.fetchSource(card, sourceDef);
522
522
  if (payload !== null && payload !== undefined) out[sourceDef.bindTo] = payload;
523
523
  }
@@ -16,10 +16,10 @@
16
16
  </style>
17
17
  <script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
18
18
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
19
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/card-compute.js"></script>
20
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/live-cards.js"></script>
21
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/board-livegraph-engine.js"></script>
22
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.6/browser/board-livecards-runtime-client.js"></script>
19
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/card-compute.js"></script>
20
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/live-cards.js"></script>
21
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/board-livegraph-engine.js"></script>
22
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@5.2.8/browser/board-livecards-runtime-client.js"></script>
23
23
  </head>
24
24
  <body class="bg-light">
25
25
  <div class="container-fluid py-3">