yaml-flow 7.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/browser/asset-integrity.json +1 -1
  2. package/browser/board-livecards-client.js +1 -1
  3. package/browser/board-livecards-client.js.map +1 -1
  4. package/browser/board-livecards-localstorage.js +5 -5
  5. package/browser/board-livecards-localstorage.js.map +1 -1
  6. package/browser/live-cards.js +3 -1
  7. package/dist/{board-live-cards-public-CW5074xr.d.cts → board-live-cards-public-5n1-syA3.d.cts} +1 -2
  8. package/dist/{board-live-cards-public-hnZo0mAf.d.ts → board-live-cards-public-CK_J8uv0.d.ts} +1 -2
  9. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -2
  10. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -1
  11. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +2 -2
  12. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +2 -2
  13. package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -2
  14. package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -1
  15. package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -1
  16. package/dist/cli/browser-api/card-store-browser-api.js.map +1 -1
  17. package/dist/cli/node/artifacts-store-cli.cjs +5 -5
  18. package/dist/cli/node/artifacts-store-cli.cjs.map +1 -1
  19. package/dist/cli/node/artifacts-store-cli.js +5 -5
  20. package/dist/cli/node/artifacts-store-cli.js.map +1 -1
  21. package/dist/cli/node/board-live-cards-cli.cjs +7 -7
  22. package/dist/cli/node/board-live-cards-cli.cjs.map +1 -1
  23. package/dist/cli/node/board-live-cards-cli.js +7 -7
  24. package/dist/cli/node/board-live-cards-cli.js.map +1 -1
  25. package/dist/cli/node/card-store-cli.cjs +4 -4
  26. package/dist/cli/node/card-store-cli.cjs.map +1 -1
  27. package/dist/cli/node/card-store-cli.js +4 -4
  28. package/dist/cli/node/card-store-cli.js.map +1 -1
  29. package/dist/cli/node/execution-adapter.cjs +1 -1
  30. package/dist/cli/node/execution-adapter.cjs.map +1 -1
  31. package/dist/cli/node/execution-adapter.js +1 -1
  32. package/dist/cli/node/execution-adapter.js.map +1 -1
  33. package/dist/cli/node/fs-board-adapter.cjs +7 -7
  34. package/dist/cli/node/fs-board-adapter.cjs.map +1 -1
  35. package/dist/cli/node/fs-board-adapter.d.cts +2 -2
  36. package/dist/cli/node/fs-board-adapter.d.ts +2 -2
  37. package/dist/cli/node/fs-board-adapter.js +7 -7
  38. package/dist/cli/node/fs-board-adapter.js.map +1 -1
  39. package/dist/cli/node/source-cli-task-executor.cjs +2 -2
  40. package/dist/cli/node/source-cli-task-executor.cjs.map +1 -1
  41. package/dist/cli/node/source-cli-task-executor.js +2 -2
  42. package/dist/cli/node/source-cli-task-executor.js.map +1 -1
  43. package/dist/execution-refs.cjs +2 -2
  44. package/dist/execution-refs.cjs.map +1 -1
  45. package/dist/execution-refs.d.cts +9 -4
  46. package/dist/execution-refs.d.ts +9 -4
  47. package/dist/execution-refs.js +2 -2
  48. package/dist/execution-refs.js.map +1 -1
  49. package/dist/server-runtime/index.cjs +4 -4
  50. package/dist/server-runtime/index.cjs.map +1 -1
  51. package/dist/server-runtime/index.d.cts +3 -3
  52. package/dist/server-runtime/index.d.ts +3 -3
  53. package/dist/server-runtime/index.js +4 -4
  54. package/dist/server-runtime/index.js.map +1 -1
  55. package/dist/step-machine-public/index.cjs +2 -1
  56. package/dist/step-machine-public/index.cjs.map +1 -1
  57. package/dist/step-machine-public/index.d.cts +7 -0
  58. package/dist/step-machine-public/index.d.ts +7 -0
  59. package/dist/step-machine-public/index.js +2 -1
  60. package/dist/step-machine-public/index.js.map +1 -1
  61. package/dist/storage-refs.cjs +2 -2
  62. package/dist/storage-refs.cjs.map +1 -1
  63. package/dist/storage-refs.d.cts +1 -2
  64. package/dist/storage-refs.d.ts +1 -2
  65. package/dist/storage-refs.js +2 -2
  66. package/dist/storage-refs.js.map +1 -1
  67. package/dist/{types-BxEFcVK9.d.cts → types-CU3DjTKL.d.cts} +1 -1
  68. package/dist/{types-B1ZRa4aI.d.ts → types-HGDTWIun.d.ts} +1 -1
  69. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.js +13 -0
  70. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.py +398 -0
  71. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +4 -4
  72. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +1 -1
  73. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +1 -1
  74. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +1 -1
  75. package/examples/example-board/agent-instructions.md +1 -1
  76. package/examples/example-board/cards/{card-market-prices.json → cardT-market-prices.json} +2 -2
  77. package/examples/example-board/cards/{card-portfolio.json → cardT-portfolio.json} +3 -13
  78. package/examples/example-board/demo-server-config.json +1 -1
  79. package/examples/example-board/demo-server.js +48 -25
  80. package/examples/example-board/demo-shell-localstorage.html +3 -3
  81. package/examples/example-board/demo-shell-with-server.html +2 -2
  82. package/examples/example-board/demo-task-executor.js +4 -8
  83. package/package.json +2 -2
  84. package/step-machine-cli.js +1 -1
  85. package/examples/example-board/cards/_index.json +0 -47
  86. /package/examples/example-board/cards/{card-portfolio-value.json → cardT-portfolio-value.json} +0 -0
@@ -164,20 +164,20 @@ function createNodeSpawnInvocationAdapter() {
164
164
  if (ref.howToRun !== 'local-node') {
165
165
  return { dispatched: false, error: `unsupported howToRun: ${ref.howToRun}` };
166
166
  }
167
- const whatToRun = String(ref.whatToRun || '');
167
+ const whatToRun = ref.whatToRun;
168
168
  let scriptPath = '';
169
- if (whatToRun.startsWith('b64:')) {
169
+ if (whatToRun && typeof whatToRun === 'object') {
170
+ if (whatToRun.kind === 'fs-path') scriptPath = whatToRun.value;
171
+ } else if (typeof whatToRun === 'string' && whatToRun.startsWith('b64:')) {
170
172
  try {
171
173
  const parsed = parseRef(whatToRun);
172
174
  if (parsed.kind === 'fs-path') scriptPath = parsed.value;
173
175
  } catch {
174
176
  scriptPath = '';
175
177
  }
176
- } else {
177
- scriptPath = whatToRun;
178
178
  }
179
179
  if (!scriptPath) {
180
- return { dispatched: false, error: `no script path in whatToRun: ${whatToRun}` };
180
+ return { dispatched: false, error: `no fs-path in whatToRun: ${JSON.stringify(whatToRun)}` };
181
181
  }
182
182
  // Resolve chatsKeyPrefix (blob key prefix) to absolute FS chatDir for handlers
183
183
  const finalArgs = { ...args };
@@ -203,17 +203,17 @@ function createNodeSpawnInvocationAdapter() {
203
203
  },
204
204
  async describe(ref) {
205
205
  if (ref.howToRun !== 'local-node') return null;
206
- const whatToRun = String(ref.whatToRun || '');
206
+ const whatToRun = ref.whatToRun;
207
207
  let scriptPath = '';
208
- if (whatToRun.startsWith('b64:')) {
208
+ if (whatToRun && typeof whatToRun === 'object') {
209
+ if (whatToRun.kind === 'fs-path') scriptPath = whatToRun.value;
210
+ } else if (typeof whatToRun === 'string' && whatToRun.startsWith('b64:')) {
209
211
  try {
210
212
  const parsed = parseRef(whatToRun);
211
213
  if (parsed.kind === 'fs-path') scriptPath = parsed.value;
212
214
  } catch {
213
215
  scriptPath = '';
214
216
  }
215
- } else {
216
- scriptPath = whatToRun;
217
217
  }
218
218
  if (!scriptPath) return null;
219
219
  try {
@@ -291,9 +291,17 @@ const logger = { info: console.log, warn: console.warn, error: console.error };
291
291
  // Track per-board host config for demo-setup (FS paths are host concerns, not runtime concerns)
292
292
  const boardHostConfig = new Map();
293
293
 
294
- function buildBoardContextConfig(label, boardDir, cardsDir, taskExecPath, chatHandlerPath, infAdapterPath, boardId) {
294
+ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerPath, infAdapterPath, boardId) {
295
295
  fs.mkdirSync(boardDir, { recursive: true });
296
296
 
297
+ // Runtime card store lives inside the board's setup dir, isolated from the source cards dir.
298
+ // Layout: boardDir/cards/store — KV card store
299
+ // boardDir/cards/chats — chat blobs
300
+ // boardDir/cards/files — file uploads
301
+ const runtimeCardsDir = path.join(boardDir, 'cards');
302
+ const runtimeCardStoreDir = path.join(runtimeCardsDir, 'store');
303
+ fs.mkdirSync(runtimeCardStoreDir, { recursive: true });
304
+
297
305
  const notifyChannel = `yaml-flow-server-${label}-${boardId}-${process.pid}`;
298
306
  const baseRef = parseRef(serializeRef({ kind: 'fs-path', value: boardDir }));
299
307
  const boardAdapter = createFsBoardPlatformAdapter(baseRef, YAML_FLOW_CLI_DIR, {
@@ -302,12 +310,11 @@ function buildBoardContextConfig(label, boardDir, cardsDir, taskExecPath, chatHa
302
310
  // In the server context the drain loop is driven in-process; suppress the
303
311
  // detached CLI spawn that the FS adapter would otherwise fire as a continuation.
304
312
  boardAdapter.requestProcessAccumulated = () => {};
305
- // Separate artifacts adapter rooted at cardsDir (preserves old FS layout where
306
- // chats/files live under cardsDir rather than boardDir)
307
- const artifactsRef = parseRef(serializeRef({ kind: 'fs-path', value: cardsDir }));
313
+ // Artifacts adapter rooted at runtimeCardsDir so chats/ and files/ are siblings of store/.
314
+ const artifactsRef = parseRef(serializeRef({ kind: 'fs-path', value: runtimeCardsDir }));
308
315
  const artifactsAdapter = createFsBoardPlatformAdapter(artifactsRef, YAML_FLOW_CLI_DIR, { suppressSpawn: true });
309
316
 
310
- const cardStoreRef = serializeRef({ kind: 'fs-path', value: cardsDir });
317
+ const cardStoreRef = serializeRef({ kind: 'fs-path', value: runtimeCardStoreDir });
311
318
 
312
319
  return {
313
320
  label,
@@ -328,7 +335,8 @@ const runtime = createMultiBoardServerRuntime({
328
335
  serverMetaStore,
329
336
  logger,
330
337
  boardRuntimeFactory: (boardId, entry) => {
331
- const cardsDir = typeof entry.cardsDir === 'string' ? path.resolve(entry.cardsDir) : defaultCardsDir;
338
+ // sourceCardsDir: read-only source used only for initial seeding.
339
+ const sourceCardsDir = typeof entry.cardsDir === 'string' ? path.resolve(entry.cardsDir) : defaultCardsDir;
332
340
  const boardRoot = path.join(setupDir, `board-${boardId}`);
333
341
  const boardDir = path.join(boardRoot, 'runtime');
334
342
 
@@ -337,29 +345,32 @@ const runtime = createMultiBoardServerRuntime({
337
345
  const infAdapterPath = typeof entry.inferenceAdapterPath === 'string' ? entry.inferenceAdapterPath : defaultInferenceAdapterPath;
338
346
  const stepMachinePath = typeof entry.stepMachineCliPath === 'string' ? entry.stepMachineCliPath : defaultStepMachineCliPath;
339
347
 
340
- const gandalfCardsDir_ = typeof entry.gandalfCardsDir === 'string' ? path.resolve(entry.gandalfCardsDir) : defaultGandalfCardsDir;
348
+ const sourceGandalfCardsDir = typeof entry.gandalfCardsDir === 'string' ? path.resolve(entry.gandalfCardsDir) : defaultGandalfCardsDir;
341
349
  const gandalfTaskExecPath = typeof entry.gandalfTaskExecutorPath === 'string' ? entry.gandalfTaskExecutorPath : defaultGandalfTaskExecutorPath;
342
350
  const gandalfChatPath = typeof entry.gandalfChatHandlerPath === 'string' ? entry.gandalfChatHandlerPath : defaultGandalfChatHandlerPath;
343
351
  const gandalfInfPath = typeof entry.gandalfInferenceAdapterPath === 'string' ? entry.gandalfInferenceAdapterPath : defaultGandalfInferenceAdapterPath;
344
352
 
345
- const baseCfg = buildBoardContextConfig('base', boardDir, cardsDir, taskExecPath, chatHandlerPath_, infAdapterPath, boardId);
353
+ const baseCfg = buildBoardContextConfig('base', boardDir, taskExecPath, chatHandlerPath_, infAdapterPath, boardId);
346
354
 
347
355
  const boards = [baseCfg];
348
- if (gandalfCardsDir_ && gandalfTaskExecPath) {
349
- const gandalfBoardDir = path.join(boardRoot, 'gandalf-runtime');
350
- const gandalfCfg = buildBoardContextConfig('gandalf', gandalfBoardDir, gandalfCardsDir_, gandalfTaskExecPath, gandalfChatPath, gandalfInfPath, boardId);
351
- // Fix gandalf outputsStoreRef
356
+ let gandalfBoardDir = null;
357
+ if (sourceGandalfCardsDir && gandalfTaskExecPath) {
358
+ gandalfBoardDir = path.join(boardRoot, 'gandalf-runtime');
359
+ const gandalfCfg = buildBoardContextConfig('gandalf', gandalfBoardDir, gandalfTaskExecPath, gandalfChatPath, gandalfInfPath, boardId);
352
360
  gandalfCfg.outputsStoreRef = serializeRef({ kind: 'fs-path', value: path.join(boardRoot, 'gandalf-runtime-out', '.outputs') });
353
361
  boards.push(gandalfCfg);
354
362
  }
355
363
 
356
364
  // Store host config for demo-setup (FS paths are host concerns)
357
- boardHostConfig.set(boardId, { cardsDir, gandalfCardsDir: gandalfCardsDir_, boardDir, boardRoot });
365
+ boardHostConfig.set(boardId, { cardsDir: sourceCardsDir, gandalfCardsDir: sourceGandalfCardsDir, boardDir, boardRoot });
358
366
 
359
367
  // Auto-run demo-setup (write copilot-instructions.md) at board init time,
360
368
  // so clients no longer need a separate /demo-setup request before bootstrapping.
361
369
  demoPrepSetup(boardId);
362
370
 
371
+ // runtimeCardsDir is where the live card store lives (inside setupDir).
372
+ const runtimeCardsDir = path.join(boardDir, 'cards');
373
+
363
374
  const singleBoardRuntime = createSingleBoardServerRuntime({
364
375
  apiBasePath: `${apiBasePath}/${boardId}`,
365
376
  boardId,
@@ -370,18 +381,30 @@ const runtime = createMultiBoardServerRuntime({
370
381
  serverUrl: `http://127.0.0.1:${PORT}`,
371
382
  executionExtra: {
372
383
  boardSetupRoot: boardRoot,
373
- chatsBlobBasePath: path.join(cardsDir, 'chats'),
384
+ chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
374
385
  ...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
375
386
  },
376
387
  });
377
388
 
378
- // Host concern (Part A): seed card store from FS source only if empty
389
+ // Host concern: seed card store from source cardsDir only if the runtime store is empty.
379
390
  const existing = singleBoardRuntime.cardStore.get({});
380
391
  const isEmpty = existing.status !== 'success' || !existing.data?.cards?.length;
381
392
  if (isEmpty) {
382
- const cards = createFsCardSource(cardsDir).listCards();
393
+ const cards = createFsCardSource(sourceCardsDir).listCards();
383
394
  if (cards.length) singleBoardRuntime.cardStore.set({ body: cards });
384
395
  }
396
+ // Seed gandalf board if present
397
+ if (gandalfBoardDir && sourceGandalfCardsDir) {
398
+ const gandalfRuntime = singleBoardRuntime.getBoardRuntime?.('gandalf');
399
+ if (gandalfRuntime) {
400
+ const gExisting = gandalfRuntime.cardStore.get({});
401
+ const gEmpty = gExisting.status !== 'success' || !gExisting.data?.cards?.length;
402
+ if (gEmpty) {
403
+ const gCards = createFsCardSource(sourceGandalfCardsDir).listCards();
404
+ if (gCards.length) gandalfRuntime.cardStore.set({ body: gCards });
405
+ }
406
+ }
407
+ }
385
408
 
386
409
  return singleBoardRuntime;
387
410
  },
@@ -6,13 +6,13 @@
6
6
  <title>Example Board Demo (LocalStorage Runtime)</title>
7
7
  <link rel="icon" type="image/svg+xml" href="favicon.svg" />
8
8
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
9
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.0.0/browser/compute-jsonata.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.1.0/browser/compute-jsonata.js"></script>
10
10
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
11
11
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
12
12
  <script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
13
13
  <script src="https://cdn.jsdelivr.net/npm/leader-line/leader-line.min.js"></script>
14
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.0.0/browser/live-cards.js"></script>
15
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.0.0/browser/board-livecards-localstorage.js"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.1.0/browser/live-cards.js"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.1.0/browser/board-livecards-localstorage.js"></script>
16
16
  </head>
17
17
  <body class="bg-light">
18
18
  <div class="container-fluid py-3">
@@ -19,8 +19,8 @@
19
19
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
20
20
  <script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
21
21
  <script src="https://cdn.jsdelivr.net/npm/leader-line/leader-line.min.js"></script>
22
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.0.0/browser/live-cards.js"></script>
23
- <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.0.0/browser/board-livecards-client.js"></script>
22
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.1.0/browser/live-cards.js"></script>
23
+ <script src="https://cdn.jsdelivr.net/npm/yaml-flow@7.1.0/browser/board-livecards-client.js"></script>
24
24
  </head>
25
25
  <body class="bg-light">
26
26
  <div class="container-fluid py-3">
@@ -265,12 +265,7 @@ async function executeStepMachineSourceFlow(context) {
265
265
  const flow = await loadStepFlow(flowPath);
266
266
 
267
267
  const invokeHttpRef = async (ref, args) => {
268
- let rawUrl = ref.whatToRun;
269
- try {
270
- rawUrl = parseRef(ref.whatToRun).value;
271
- } catch {
272
- // Keep raw value when whatToRun is already a URL.
273
- }
268
+ const rawUrl = typeof ref.whatToRun === 'object' ? ref.whatToRun.value : parseRef(ref.whatToRun).value;
274
269
 
275
270
  const base = String(args?.extra?.serverUrl || 'http://127.0.0.1:7799').replace(/\/$/, '');
276
271
  const resolvedUrl = /^https?:\/\//i.test(rawUrl)
@@ -326,10 +321,11 @@ async function executeStepMachineSourceFlow(context) {
326
321
  return invokeHttpRef(ref, args);
327
322
  }
328
323
  if (ref.howToRun === 'demo-local-module') {
329
- const modulePath = path.resolve(__dirname, ref.whatToRun);
324
+ const whatValue = typeof ref.whatToRun === 'object' ? ref.whatToRun.value : parseRef(ref.whatToRun).value;
325
+ const modulePath = path.resolve(__dirname, whatValue);
330
326
  const mod = await import(pathToFileURL(modulePath).href);
331
327
  if (typeof mod.execute !== 'function') {
332
- throw new Error(`Flow module ${ref.whatToRun} must export execute(context)`);
328
+ throw new Error(`Flow module ${JSON.stringify(ref.whatToRun)} must export execute(context)`);
333
329
  }
334
330
  return mod.execute(args);
335
331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml-flow",
3
- "version": "7.0.0",
3
+ "version": "7.1.0",
4
4
  "description": "Unified workflow engine: step-machine (sequential) + event-graph (stateless DAG) with pluggable storage",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -138,7 +138,7 @@
138
138
  "build:example": "node demo-src/example-board/build.js",
139
139
  "dev": "tsup --watch",
140
140
  "test": "npm run build:example && vitest",
141
- "test:run": "npm run build:example && vitest run",
141
+ "test:run": "npm run build && npm run build:example && vitest run",
142
142
  "test:cli-parity": "python scripts/cli_parity_check.py",
143
143
  "test:cli-parity:full": "python scripts/cli_parity_check.py --include-portfolio",
144
144
  "test:e2e": "python examples/browser/boards/portfolio-tracker/portfolio-tracker.py --run-pycli",
@@ -376,7 +376,7 @@ function normalizeExecutionRef(ref) {
376
376
  if (!decoded || typeof decoded !== 'object' || typeof decoded.value !== 'string') {
377
377
  return ref;
378
378
  }
379
- return { ...ref, whatToRun: decoded.value };
379
+ return { ...ref, whatToRun: decoded };
380
380
  } catch {
381
381
  return ref;
382
382
  }
@@ -1,47 +0,0 @@
1
- {
2
- "card-portfolio-intelligence": {
3
- "key": "card-portfolio-intelligence",
4
- "checksum": "card-portfolio-intelligence",
5
- "updatedAt": "2026-05-10T09:38:02.181Z"
6
- },
7
- "card-market-prices": {
8
- "key": "card-market-prices",
9
- "checksum": "card-market-prices",
10
- "updatedAt": "2026-05-10T09:38:02.161Z"
11
- },
12
- "card-my-identity": {
13
- "key": "card-my-identity",
14
- "checksum": "card-my-identity",
15
- "updatedAt": "2026-05-10T09:38:02.168Z"
16
- },
17
- "card-portfolio-action": {
18
- "key": "card-portfolio-action",
19
- "checksum": "card-portfolio-action",
20
- "updatedAt": "2026-05-10T09:38:02.174Z"
21
- },
22
- "card-portfolio-risks": {
23
- "key": "card-portfolio-risks",
24
- "checksum": "card-portfolio-risks",
25
- "updatedAt": "2026-05-10T09:38:02.187Z"
26
- },
27
- "card-portfolio-value": {
28
- "key": "card-portfolio-value",
29
- "checksum": "card-portfolio-value",
30
- "updatedAt": "2026-05-10T09:38:02.195Z"
31
- },
32
- "card-portfolio": {
33
- "key": "card-portfolio",
34
- "checksum": "card-portfolio",
35
- "updatedAt": "2026-05-10T09:38:02.201Z"
36
- },
37
- "card-rebalance-impact": {
38
- "key": "card-rebalance-impact",
39
- "checksum": "card-rebalance-impact",
40
- "updatedAt": "2026-05-10T09:38:02.208Z"
41
- },
42
- "card-rebalance-sim": {
43
- "key": "card-rebalance-sim",
44
- "checksum": "card-rebalance-sim",
45
- "updatedAt": "2026-05-10T09:38:02.214Z"
46
- }
47
- }