yaml-flow 8.6.1 → 8.6.2

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 (96) hide show
  1. package/browser/asset-integrity.json +3 -3
  2. package/examples/board/demo-shell-with-server.html +2 -2
  3. package/examples/board/doc.html +2 -2
  4. package/examples/board/server/board-server.js +544 -45
  5. package/examples/board/server-config.json +2 -1
  6. package/examples/board/test/server-http-test.js +12 -4
  7. package/examples/board-local/demo-shell-localstorage.html +3 -3
  8. package/lib/{artifacts-store-lib-CLOtsiav.d.cts → artifacts-store-lib-BR-Samty.d.cts} +1 -1
  9. package/lib/{artifacts-store-lib-C1rtrkxm.d.ts → artifacts-store-lib-DT7XlWUL.d.ts} +1 -1
  10. package/lib/artifacts-store-public.d.cts +3 -3
  11. package/lib/artifacts-store-public.d.ts +3 -3
  12. package/lib/board-live-cards-mcp.cjs +1 -1
  13. package/lib/board-live-cards-mcp.d.cts +56 -23
  14. package/lib/board-live-cards-mcp.d.ts +56 -23
  15. package/lib/board-live-cards-mcp.js +1 -1
  16. package/lib/board-live-cards-node.cjs +8 -8
  17. package/lib/board-live-cards-node.d.cts +39 -21
  18. package/lib/board-live-cards-node.d.ts +39 -21
  19. package/lib/board-live-cards-node.js +8 -8
  20. package/lib/{board-live-cards-public-CBVjm327.d.ts → board-live-cards-public-BMUIPOrc.d.ts} +67 -42
  21. package/lib/board-live-cards-public-async-DKZqbJVU.d.ts +256 -0
  22. package/lib/board-live-cards-public-async-dMWNbWq6.d.cts +256 -0
  23. package/lib/{board-live-cards-public-CPJy-aGW.d.cts → board-live-cards-public-wkNmBIRC.d.cts} +67 -42
  24. package/lib/board-live-cards-public.cjs +1 -1
  25. package/lib/board-live-cards-public.d.cts +2 -2
  26. package/lib/board-live-cards-public.d.ts +2 -2
  27. package/lib/board-live-cards-public.js +1 -1
  28. package/lib/board-live-cards-server-runtime.cjs +1 -1
  29. package/lib/board-live-cards-server-runtime.d.cts +7 -6
  30. package/lib/board-live-cards-server-runtime.d.ts +7 -6
  31. package/lib/board-live-cards-server-runtime.js +1 -1
  32. package/lib/board-worker-adapter.cjs +4 -3
  33. package/lib/board-worker-adapter.d.cts +24 -14
  34. package/lib/board-worker-adapter.d.ts +24 -14
  35. package/lib/board-worker-adapter.js +4 -3
  36. package/lib/card-store-public.d.cts +2 -2
  37. package/lib/card-store-public.d.ts +2 -2
  38. package/lib/{chat-storage-lib-CKylihjm.d.cts → chat-storage-lib-BIUbE-fM.d.cts} +1 -1
  39. package/lib/{chat-storage-lib-Bce-xx6l.d.ts → chat-storage-lib-BlG-sobS.d.ts} +1 -1
  40. package/lib/chat-store-public.d.cts +3 -3
  41. package/lib/chat-store-public.d.ts +3 -3
  42. package/lib/{chunk-CWREBRXS.js → chunk-BQS3EIEK.js} +3 -3
  43. package/lib/{chunk-LDAP75GN.js → chunk-CIAJNUR4.js} +2 -2
  44. package/lib/chunk-GJJMEAVN.cjs +2 -0
  45. package/lib/chunk-H5HBXPOI.cjs +3 -0
  46. package/lib/chunk-HEEDJEKM.js +2 -0
  47. package/lib/chunk-KQX6R4PV.cjs +8 -0
  48. package/lib/chunk-MLVTJASJ.js +2 -0
  49. package/lib/chunk-N6P2JW4W.js +3 -0
  50. package/lib/chunk-OEFTOO47.cjs +3 -0
  51. package/lib/chunk-OSWJKJLB.js +8 -0
  52. package/lib/chunk-PBCDDO4V.cjs +2 -0
  53. package/lib/{chunk-I4WH5U5D.cjs → chunk-PMUSJQSR.cjs} +2 -2
  54. package/lib/chunk-SCWHDI3I.js +2 -0
  55. package/lib/{chunk-UVE65IPR.cjs → chunk-SFVO2LB2.cjs} +3 -3
  56. package/lib/{chunk-5DB54ZX2.cjs → chunk-U2N6MCD5.cjs} +2 -2
  57. package/lib/chunk-VMW4Z6EF.js +3 -0
  58. package/lib/chunk-WOALA3V5.cjs +2 -0
  59. package/lib/{chunk-LBMEVV4U.js → chunk-XQRNDX4Q.js} +2 -2
  60. package/lib/cloud-storage.cjs +2 -0
  61. package/lib/cloud-storage.d.cts +177 -0
  62. package/lib/cloud-storage.d.ts +177 -0
  63. package/lib/cloud-storage.js +2 -0
  64. package/lib/execution-refs.cjs +1 -1
  65. package/lib/execution-refs.js +1 -1
  66. package/lib/index.cjs +2 -2
  67. package/lib/index.d.cts +1 -1
  68. package/lib/index.d.ts +1 -1
  69. package/lib/index.js +1 -1
  70. package/lib/{types-DRl0Hy_p.d.cts → queue-lane-registry-BPKWWgd4.d.cts} +66 -14
  71. package/lib/{types-BuK2UMxk.d.ts → queue-lane-registry-Be6c0ftj.d.ts} +66 -14
  72. package/lib/server-runtime/index.cjs +1 -1
  73. package/lib/server-runtime/index.d.cts +18 -7
  74. package/lib/server-runtime/index.d.ts +18 -7
  75. package/lib/server-runtime/index.js +1 -1
  76. package/lib/step-machine-public/index.cjs +1 -1
  77. package/lib/step-machine-public/index.d.cts +1 -1
  78. package/lib/step-machine-public/index.d.ts +1 -1
  79. package/lib/step-machine-public/index.js +1 -1
  80. package/lib/{storage-interface-Ct-C4tlz.d.cts → storage-interface-BFiD3kyB.d.cts} +11 -1
  81. package/lib/{storage-interface-Ct-C4tlz.d.ts → storage-interface-BFiD3kyB.d.ts} +11 -1
  82. package/lib/stores/index.cjs +1 -1
  83. package/lib/stores/index.d.cts +1 -1
  84. package/lib/stores/index.d.ts +1 -1
  85. package/lib/stores/index.js +1 -1
  86. package/lib/stores/kv.d.cts +1 -1
  87. package/lib/stores/kv.d.ts +1 -1
  88. package/package.json +6 -1
  89. package/lib/chunk-6APH25VI.js +0 -2
  90. package/lib/chunk-KNFFDVLD.cjs +0 -2
  91. package/lib/chunk-LVNQCE5X.cjs +0 -3
  92. package/lib/chunk-M7EQRS6W.js +0 -3
  93. package/lib/chunk-NJJ7WEDT.cjs +0 -2
  94. package/lib/chunk-P64UKI3L.cjs +0 -8
  95. package/lib/chunk-Q6VSL327.js +0 -8
  96. package/lib/chunk-VCCTAUIG.js +0 -2
@@ -11,12 +11,18 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
11
11
  import {
12
12
  createMultiBoardServerRuntime,
13
13
  createSingleBoardServerRuntime,
14
+ createHostedBoardQueueLaneRegistry,
14
15
  } from 'yaml-flow/board-live-cards-server-runtime';
16
+ import {
17
+ createHostedAsyncBoardPlatformAdapter,
18
+ } from 'yaml-flow/cloud-storage';
15
19
 
16
20
  import {
17
21
  buildLocalBaseSpec,
22
+ createHttpBoardCallbackTransport,
18
23
  createFsBoardPlatformAdapter,
19
24
  createFsBoardNonCorePlatformAdapter,
25
+ createInProcessBoardCallbackTransport,
20
26
  createFsBoardChatStorage,
21
27
  createNodeSpawnInvocationAdapter,
22
28
  createArtifactsStore,
@@ -24,8 +30,9 @@ import {
24
30
  invokeExecutionRef,
25
31
  parseRef,
26
32
  registerInProcessExecutionHandler,
27
- startBoardWorkerQueueRunner,
33
+ startQueueLaneRunners,
28
34
  serializeRef,
35
+ serializeExecutionRef,
29
36
  } from 'yaml-flow/board-live-cards-node';
30
37
  import { registerInProcessBoardWorkerCallback } from 'yaml-flow/board-worker-adapter';
31
38
  import {
@@ -144,6 +151,12 @@ const configuredChatFlowTimeoutMs = normalizeTimeoutMs(serverConfig.chatFlowTime
144
151
  const configuredInvokeRefTimeoutMs = normalizeTimeoutMs(serverConfig.chatInvokeRefTimeoutMs, 300000);
145
152
  const configuredCopilotTimeoutMs = normalizeTimeoutMs(serverConfig.chatCopilotTimeoutMs, 300000);
146
153
 
154
+ function normalizeRuntimeMode(value) {
155
+ const normalized = String(value || '').trim().toLowerCase();
156
+ if (normalized === 'sync' || normalized === 'fs') return 'sync';
157
+ return 'cloud';
158
+ }
159
+
147
160
  function normalizeBoardWorkerTransport(value) {
148
161
  const normalized = String(value || '').trim().toLowerCase();
149
162
  if (normalized === 'http') return 'http';
@@ -154,6 +167,9 @@ function normalizeBoardWorkerTransport(value) {
154
167
  const configuredBoardWorkerTransport = normalizeBoardWorkerTransport(
155
168
  process.env.DEMO_TASK_EXECUTOR_TRANSPORT || serverConfig.taskExecutorTransport || 'in-process-loop',
156
169
  );
170
+ const configuredRuntimeMode = normalizeRuntimeMode(
171
+ process.env.DEMO_SERVER_MODE || serverConfig.mode || 'cloud',
172
+ );
157
173
 
158
174
  // Resolve top-level config defaults (used as fallbacks for per-board config)
159
175
  const configuredTaskExecutorPath = resolveFromConfig(serverConfig.taskExecutorPath);
@@ -293,25 +309,14 @@ function makeHostedBoardWorkerRef(boardId, taskExecPath, transport, executionExt
293
309
  throw new Error(`Unsupported board-worker transport for demo host: ${transport}`);
294
310
  }
295
311
 
296
- function makeBoardWorkerCallbackSelfRef(serverUrl, boardApiBasePath, transport, boardId) {
297
- if (transport === 'in-process-loop' || transport === 'queue') {
298
- return {
299
- meta: 'board-live-cards',
300
- howToRun: 'in-process-loop',
301
- whatToRun: serializeRef({ kind: 'in-process-loop', value: `board:${boardId}:board-worker-callback` }),
302
- };
312
+ function makeBoardWorkerCallbackTransport(serverUrl, boardApiBasePath, transport, boardId) {
313
+ if (transport === 'in-process-loop' || transport === 'queue' || transport === 'http') {
314
+ return createInProcessBoardCallbackTransport(`board:${boardId}:board-worker-callback`);
303
315
  }
304
316
  const normalizedServerUrl = typeof serverUrl === 'string' ? serverUrl.trim().replace(/\/+$/, '') : '';
305
317
  const normalizedApiBasePath = typeof boardApiBasePath === 'string' ? boardApiBasePath.trim().replace(/\/+$/, '') : '';
306
318
  if (!normalizedServerUrl || !normalizedApiBasePath) return undefined;
307
- return {
308
- meta: 'board-live-cards',
309
- howToRun: 'http:post',
310
- whatToRun: serializeRef({
311
- kind: 'http-url',
312
- value: `${normalizedServerUrl}${normalizedApiBasePath}/callback/board-worker`,
313
- }),
314
- };
319
+ return createHttpBoardCallbackTransport(`${normalizedServerUrl}${normalizedApiBasePath}/callback/board-worker`);
315
320
  }
316
321
 
317
322
  async function readJsonRequest(req) {
@@ -381,6 +386,437 @@ function createNamedPipeNotificationTransport() {
381
386
  };
382
387
  }
383
388
 
389
+ function createInMemoryNotificationTransport() {
390
+ const subscribers = new Map();
391
+
392
+ return {
393
+ publish(channel, notifications) {
394
+ const channelSubscribers = subscribers.get(channel);
395
+ if (!channelSubscribers?.size) return;
396
+ const event = notifications.length === 1
397
+ ? notifications[0]
398
+ : { kind: 'notification-batch', notifications };
399
+ for (const onEvent of channelSubscribers) {
400
+ try { onEvent(event); } catch { /* */ }
401
+ }
402
+ },
403
+
404
+ async subscribe(ref, onEvent) {
405
+ if (ref.kind !== 'in-memory-notify') return () => {};
406
+ const channel = String(ref.value || '');
407
+ const channelSubscribers = subscribers.get(channel) || new Set();
408
+ channelSubscribers.add(onEvent);
409
+ subscribers.set(channel, channelSubscribers);
410
+ return () => {
411
+ channelSubscribers.delete(onEvent);
412
+ if (!channelSubscribers.size) subscribers.delete(channel);
413
+ };
414
+ },
415
+ };
416
+ }
417
+
418
+ function createNotificationTransport() {
419
+ const namedPipeTransport = createNamedPipeNotificationTransport();
420
+ const inMemoryTransport = createInMemoryNotificationTransport();
421
+
422
+ return {
423
+ publish: inMemoryTransport.publish,
424
+ async subscribe(ref, onEvent) {
425
+ if (ref.kind === 'in-memory-notify') return inMemoryTransport.subscribe(ref, onEvent);
426
+ return namedPipeTransport.subscribe(ref, onEvent);
427
+ },
428
+ };
429
+ }
430
+
431
+ class MemoryAsyncKVStorage {
432
+ constructor() {
433
+ this.values = new Map();
434
+ }
435
+
436
+ readSync(key) {
437
+ return this.values.has(key) ? this.values.get(key) : null;
438
+ }
439
+
440
+ writeSync(key, value) {
441
+ this.values.set(key, value);
442
+ }
443
+
444
+ deleteSync(key) {
445
+ this.values.delete(key);
446
+ }
447
+
448
+ async read(key) {
449
+ return this.readSync(key);
450
+ }
451
+
452
+ async write(key, value) {
453
+ this.writeSync(key, value);
454
+ }
455
+
456
+ async delete(key) {
457
+ this.deleteSync(key);
458
+ }
459
+
460
+ async listKeys(prefix = '') {
461
+ return Array.from(this.values.keys()).filter((key) => key.startsWith(prefix)).sort();
462
+ }
463
+ }
464
+
465
+ class MemoryAsyncBlobStorage {
466
+ constructor(kind, keyRefFactory = null) {
467
+ this.kind = kind;
468
+ this.keyRefFactory = keyRefFactory;
469
+ this.textValues = new Map();
470
+ this.byteValues = new Map();
471
+ }
472
+
473
+ keyRef(key) {
474
+ if (this.keyRefFactory) return this.keyRefFactory(key);
475
+ return { kind: this.kind, value: key };
476
+ }
477
+
478
+ async read(key) {
479
+ if (this.textValues.has(key)) return this.textValues.get(key);
480
+ const bytes = this.byteValues.get(key);
481
+ return bytes ? Buffer.from(bytes).toString('utf-8') : null;
482
+ }
483
+
484
+ async write(key, value) {
485
+ this.textValues.set(key, value);
486
+ this.byteValues.delete(key);
487
+ }
488
+
489
+ async readBytes(key) {
490
+ if (this.byteValues.has(key)) return this.byteValues.get(key);
491
+ if (this.textValues.has(key)) return Buffer.from(this.textValues.get(key), 'utf-8');
492
+ return null;
493
+ }
494
+
495
+ async writeBytes(key, value) {
496
+ this.byteValues.set(key, Buffer.from(value));
497
+ this.textValues.delete(key);
498
+ }
499
+
500
+ async remove(key) {
501
+ this.textValues.delete(key);
502
+ this.byteValues.delete(key);
503
+ }
504
+
505
+ async exists(key) {
506
+ return this.textValues.has(key) || this.byteValues.has(key);
507
+ }
508
+
509
+ async listKeys(prefix = '') {
510
+ const keys = new Set([
511
+ ...Array.from(this.textValues.keys()),
512
+ ...Array.from(this.byteValues.keys()),
513
+ ]);
514
+ return Array.from(keys).filter((key) => key.startsWith(prefix)).sort();
515
+ }
516
+ }
517
+
518
+ class MemoryAsyncQueueStorage {
519
+ constructor() {
520
+ this.queueItems = new Map();
521
+ this.deadQueueItems = new Map();
522
+ }
523
+
524
+ createId() {
525
+ return globalThis.crypto?.randomUUID?.() || genShortId();
526
+ }
527
+
528
+ async enqueue(body) {
529
+ const item = {
530
+ id: this.createId(),
531
+ body,
532
+ enqueuedAt: new Date().toISOString(),
533
+ attempt: 0,
534
+ };
535
+ this.queueItems.set(item.id, item);
536
+ return item;
537
+ }
538
+
539
+ async enqueueIfAbsent(body, dedupKey) {
540
+ for (const existing of this.queueItems.values()) {
541
+ if (existing.dedupKey === dedupKey) return null;
542
+ }
543
+ const item = {
544
+ id: this.createId(),
545
+ body,
546
+ enqueuedAt: new Date().toISOString(),
547
+ attempt: 0,
548
+ dedupKey,
549
+ };
550
+ this.queueItems.set(item.id, item);
551
+ return { id: item.id, body: item.body, enqueuedAt: item.enqueuedAt, attempt: item.attempt };
552
+ }
553
+
554
+ async lease(opts = {}) {
555
+ const max = Math.max(1, Math.floor(opts.max ?? 1));
556
+ const visibilityMs = Math.max(1, Math.floor(opts.visibilityMs ?? 60_000));
557
+ const now = Date.now();
558
+ for (const item of this.queueItems.values()) {
559
+ if (item.leaseExpiresAt && Date.parse(item.leaseExpiresAt) <= now) {
560
+ delete item.leaseToken;
561
+ delete item.leaseExpiresAt;
562
+ }
563
+ }
564
+ const leased = [];
565
+ for (const item of this.queueItems.values()) {
566
+ if (leased.length >= max) break;
567
+ if (item.leaseToken) continue;
568
+ item.attempt += 1;
569
+ item.leaseToken = this.createId();
570
+ item.leaseExpiresAt = new Date(Date.now() + visibilityMs).toISOString();
571
+ leased.push({
572
+ id: item.id,
573
+ body: item.body,
574
+ enqueuedAt: item.enqueuedAt,
575
+ attempt: item.attempt,
576
+ leaseToken: item.leaseToken,
577
+ leaseExpiresAt: item.leaseExpiresAt,
578
+ });
579
+ }
580
+ return leased;
581
+ }
582
+
583
+ async ack(messageId, leaseToken) {
584
+ const item = this.queueItems.get(messageId);
585
+ if (!item || item.leaseToken !== leaseToken) return false;
586
+ this.queueItems.delete(messageId);
587
+ return true;
588
+ }
589
+
590
+ async nack(messageId, leaseToken, opts = {}) {
591
+ const item = this.queueItems.get(messageId);
592
+ if (!item || item.leaseToken !== leaseToken) return false;
593
+ delete item.leaseToken;
594
+ delete item.leaseExpiresAt;
595
+ if (opts.dead) {
596
+ this.queueItems.delete(messageId);
597
+ this.deadQueueItems.set(messageId, { ...item, reason: opts.reason });
598
+ }
599
+ return true;
600
+ }
601
+
602
+ async peekActive() {
603
+ return Array.from(this.queueItems.values())
604
+ .filter((item) => !item.leaseToken)
605
+ .map((item) => ({ id: item.id, body: item.body, enqueuedAt: item.enqueuedAt, attempt: item.attempt }));
606
+ }
607
+
608
+ async peekDeadLetter() {
609
+ return Array.from(this.deadQueueItems.values())
610
+ .map((item) => ({ ...item, body: item.body }));
611
+ }
612
+ }
613
+
614
+ function createMemoryAsyncScratchStorage() {
615
+ const store = new MemoryAsyncBlobStorage('cloud-scratch-key');
616
+ let seq = 0;
617
+ return {
618
+ ...store,
619
+ async getUniqueKey(prefix = 'scratch', suffix = '.json') {
620
+ seq += 1;
621
+ return `${prefix}-${seq}${suffix}`;
622
+ },
623
+ async create(value, prefix, suffix) {
624
+ const key = await this.getUniqueKey(prefix, suffix);
625
+ await store.write(key, value);
626
+ return key;
627
+ },
628
+ config: {
629
+ get: () => null,
630
+ set: () => {},
631
+ },
632
+ };
633
+ }
634
+
635
+ function createMemoryArchiveFactory() {
636
+ const blobStores = new Map();
637
+ const journalStreams = new Map();
638
+ let seq = 0;
639
+ return {
640
+ stream(name) {
641
+ if (!journalStreams.has(name)) journalStreams.set(name, []);
642
+ const entries = journalStreams.get(name);
643
+ return {
644
+ async append(payload) {
645
+ seq += 1;
646
+ const entry = { id: `j-${seq}`, payload };
647
+ entries.push(entry);
648
+ return entry;
649
+ },
650
+ async readAll() {
651
+ return entries.slice();
652
+ },
653
+ async readAfter(cursor) {
654
+ const idx = cursor ? entries.findIndex((entry) => entry.id === cursor) : -1;
655
+ const items = idx >= 0 ? entries.slice(idx + 1) : entries.slice();
656
+ return {
657
+ entries: items,
658
+ newCursor: items.length ? items[items.length - 1].id : cursor,
659
+ };
660
+ },
661
+ async clear() {
662
+ entries.splice(0, entries.length);
663
+ },
664
+ };
665
+ },
666
+ blob(name) {
667
+ if (!blobStores.has(name)) blobStores.set(name, new MemoryAsyncBlobStorage('cloud-archive-key'));
668
+ return blobStores.get(name);
669
+ },
670
+ async listStreams(prefix = '') {
671
+ return Array.from(journalStreams.keys()).filter((key) => key.startsWith(prefix)).sort();
672
+ },
673
+ async listBlobs(prefix = '') {
674
+ return Array.from(blobStores.keys()).filter((key) => key.startsWith(prefix)).sort();
675
+ },
676
+ config: {
677
+ get: () => null,
678
+ set: () => {},
679
+ },
680
+ };
681
+ }
682
+
683
+ function createImmediateAsyncLock() {
684
+ let held = false;
685
+ return {
686
+ async tryAcquire() {
687
+ if (held) return null;
688
+ held = true;
689
+ return async () => { held = false; };
690
+ },
691
+ };
692
+ }
693
+
694
+ function stableHash(value) {
695
+ const json = JSON.stringify(value);
696
+ let hash = 0;
697
+ for (let i = 0; i < json.length; i += 1) {
698
+ hash = ((hash << 5) - hash + json.charCodeAt(i)) | 0;
699
+ }
700
+ return `h${Math.abs(hash)}`;
701
+ }
702
+
703
+ function genShortId() {
704
+ return `${Date.now().toString(16)}${Math.random().toString(16).slice(2, 18)}`;
705
+ }
706
+
707
+ function normalizeHostedBoardWorkerTransport(runtimeMode, requestedTransport) {
708
+ if (runtimeMode === 'cloud') return 'http';
709
+ return requestedTransport;
710
+ }
711
+
712
+ function seedCloudCardStore(cardStoreKv, cards) {
713
+ const index = {};
714
+ const now = new Date().toISOString();
715
+ for (const card of cards) {
716
+ if (!card || typeof card !== 'object' || !card.id) continue;
717
+ const key = String(card.id);
718
+ cardStoreKv.writeSync(key, card);
719
+ index[card.id] = {
720
+ key,
721
+ checksum: stableHash(card),
722
+ updatedAt: now,
723
+ };
724
+ }
725
+ cardStoreKv.writeSync('_index', index);
726
+ }
727
+
728
+ const cloudBoardBundles = new Map();
729
+
730
+ function getCloudBoardBundle(boardId, notifyChannel, boardDir = null) {
731
+ if (cloudBoardBundles.has(boardId)) return cloudBoardBundles.get(boardId);
732
+
733
+ const kvNamespaces = new Map();
734
+ const kvRefs = new Map();
735
+ const blobNamespaces = new Map();
736
+ const blobKindToNamespace = new Map([
737
+ ['cloud-blob-key', ''],
738
+ ['cloud-source-key', 'sources'],
739
+ ['cloud-archive-key', 'archive'],
740
+ ['cloud-scratch-key', 'scratch'],
741
+ ]);
742
+ const scratchStore = createMemoryAsyncScratchStorage();
743
+ const archiveFactory = createMemoryArchiveFactory();
744
+ const journalStorage = archiveFactory.stream('board-journal');
745
+ const boardWorkerQueueStorage = new MemoryAsyncQueueStorage();
746
+ const chatAgentQueueStorage = new MemoryAsyncQueueStorage();
747
+ const processAccumulatedQueueStorage = new MemoryAsyncQueueStorage();
748
+ const stagedSourcesDir = boardDir ? path.join(path.dirname(boardDir), 'runtime-out', '.cloud-staged-sources') : null;
749
+ if (stagedSourcesDir) fs.mkdirSync(stagedSourcesDir, { recursive: true });
750
+
751
+ const getKvNamespace = (namespace) => {
752
+ const key = String(namespace || '');
753
+ if (!kvNamespaces.has(key)) kvNamespaces.set(key, new MemoryAsyncKVStorage());
754
+ return kvNamespaces.get(key);
755
+ };
756
+ const getKvRef = (ref) => {
757
+ const key = String(ref || '');
758
+ if (!kvRefs.has(key)) kvRefs.set(key, new MemoryAsyncKVStorage());
759
+ return kvRefs.get(key);
760
+ };
761
+ const getBlobNamespace = (namespace) => {
762
+ const key = String(namespace || '');
763
+ if (key === 'scratch') return scratchStore;
764
+ if (!blobNamespaces.has(key)) {
765
+ const kind = key === 'sources' ? 'cloud-source-key' : 'cloud-blob-key';
766
+ const keyRefFactory = key === 'sources' && stagedSourcesDir
767
+ ? (blobKey) => ({ kind: 'fs-path', value: path.join(stagedSourcesDir, ...String(blobKey).split('/')) })
768
+ : null;
769
+ blobNamespaces.set(key, new MemoryAsyncBlobStorage(kind, keyRefFactory));
770
+ }
771
+ return blobNamespaces.get(key);
772
+ };
773
+
774
+ const bundle = {
775
+ getKvNamespace,
776
+ getKvRef,
777
+ getBlobNamespace,
778
+ adapter: null,
779
+ notifyChannel,
780
+ };
781
+
782
+ bundle.adapter = createHostedAsyncBoardPlatformAdapter({
783
+ boardId,
784
+ kvStorage: (namespace) => getKvNamespace(namespace),
785
+ kvStorageForRef: (ref) => getKvRef(ref),
786
+ blobStorage: (namespace) => getBlobNamespace(namespace),
787
+ scratchStorage: () => scratchStore,
788
+ scratchStorageForRef: () => scratchStore,
789
+ archiveFactory: () => archiveFactory,
790
+ archiveFactoryForRef: () => archiveFactory,
791
+ journalStorage: () => journalStorage,
792
+ queueStorage: boardWorkerQueueStorage,
793
+ chatAgentQueueStorage,
794
+ processAccumulatedQueueStorage,
795
+ lock: createImmediateAsyncLock(),
796
+ callbackTransport: undefined,
797
+ resolveBlob: async (ref) => {
798
+ if (ref.kind === 'fs-path') {
799
+ return fs.promises.readFile(ref.value, 'utf-8');
800
+ }
801
+ const namespace = blobKindToNamespace.get(ref.kind);
802
+ if (namespace !== undefined) {
803
+ const value = await getBlobNamespace(namespace).read(ref.value);
804
+ if (value != null) return value;
805
+ }
806
+ throw new Error(`Blob not found for ref ${ref.kind}:${ref.value}`);
807
+ },
808
+ hashFn: stableHash,
809
+ genId: genShortId,
810
+ supportsDirectSourceOutput: (ref) => ref?.howToRun === 'http:post',
811
+ publishBoardChangeNotifications: async (notifications) => {
812
+ notificationTransport.publish(notifyChannel, notifications);
813
+ },
814
+ });
815
+
816
+ cloudBoardBundles.set(boardId, bundle);
817
+ return bundle;
818
+ }
819
+
384
820
  // ---------------------------------------------------------------------------
385
821
  // Server meta store (multi-board registry)
386
822
  // ---------------------------------------------------------------------------
@@ -397,17 +833,17 @@ const serverMetaStore = createArtifactsStore(serverMetaAdapter.blobStorage('serv
397
833
 
398
834
  const apiBasePath = '/api/boards';
399
835
  const invocationAdapter = createNodeSpawnInvocationAdapter();
400
- const notificationTransport = createNamedPipeNotificationTransport();
836
+ const notificationTransport = createNotificationTransport();
401
837
  const logger = { info: console.log, warn: console.warn, error: console.error };
402
838
  const hostedBoardWorkerDispatchers = new Map();
403
- const hostedBoardWorkerQueueStops = new Map();
839
+ const hostedQueueLaneStops = new Map();
404
840
  const hostedBoardChatStorages = new Map();
405
841
 
406
842
  // Map config keys to board entries for the factory
407
843
  const boardConfigEntries = serverConfig.boards ? Object.entries(serverConfig.boards) : [];
408
844
  const boardConfigMap = new Map(boardConfigEntries);
409
845
 
410
- function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow, infAdapterPath, boardId, executionExtra = {}) {
846
+ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow, infAdapterPath, boardId, executionExtra = {}, runtimeMode = 'sync') {
411
847
  fs.mkdirSync(boardDir, { recursive: true });
412
848
  const runtimeCardsDir = path.join(path.dirname(boardDir), 'cards');
413
849
  const runtimeCardStoreDir = path.join(runtimeCardsDir, 'store');
@@ -419,16 +855,66 @@ function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow,
419
855
  fs.mkdirSync(archiveDir, { recursive: true });
420
856
 
421
857
  const notifyChannel = `yaml-flow-server-${label}-${boardId}-${process.pid}`;
858
+ const boardWorkerTransport = normalizeHostedBoardWorkerTransport(
859
+ runtimeMode,
860
+ normalizeBoardWorkerTransport(executionExtra.taskExecutorTransport),
861
+ );
862
+ const callbackTransport = makeBoardWorkerCallbackTransport(executionExtra.serverUrl, executionExtra.apiBasePath, boardWorkerTransport, boardId);
863
+
864
+ if (runtimeMode === 'cloud') {
865
+ const cloudBundle = getCloudBoardBundle(boardId, notifyChannel, boardDir);
866
+ cloudBundle.adapter.callbackTransport = callbackTransport;
867
+ const cloudNonCoreAdapter = createFsBoardNonCorePlatformAdapter(
868
+ parseRef(serializeRef({ kind: 'fs-path', value: boardDir })),
869
+ {
870
+ notifyChannel,
871
+ ...(callbackTransport ? { callbackTransport } : {}),
872
+ },
873
+ );
874
+ const cloudHostedTaskExecutorRef = makeHostedBoardWorkerRef(boardId, taskExecPath, boardWorkerTransport, executionExtra);
875
+ const cloudLocalSyncTaskExecutorRef = makeLocalTaskExecutorRef(taskExecPath, executionExtra);
876
+ if (cloudLocalSyncTaskExecutorRef) {
877
+ const invokeExecutor = cloudNonCoreAdapter.invokeExecutor.bind(cloudNonCoreAdapter);
878
+ cloudNonCoreAdapter.invokeExecutor = (ref, subcommand, execOpts) => {
879
+ const syncRef = isHostedTaskExecutorRef(ref) ? cloudLocalSyncTaskExecutorRef : ref;
880
+ return invokeExecutor(syncRef, subcommand, execOpts);
881
+ };
882
+ }
883
+ cloudNonCoreAdapter.requestProcessAccumulated = () => {};
884
+ try {
885
+ const seedTeRef = cloudLocalSyncTaskExecutorRef ?? cloudHostedTaskExecutorRef;
886
+ if (seedTeRef) {
887
+ cloudNonCoreAdapter.kvStorage('config').write('task-executor', serializeExecutionRef(seedTeRef));
888
+ }
889
+ } catch (e) {
890
+ logger.warn(`[cloud:${boardId}] failed to seed non-core task-executor config: ${e?.message || e}`);
891
+ }
892
+ return {
893
+ label,
894
+ boardAdapter: cloudBundle.adapter,
895
+ nonCoreAdapter: cloudNonCoreAdapter,
896
+ artifactsAdapter: cloudBundle.adapter,
897
+ baseRef: { kind: 'cloud-board', value: `board:${boardId}` },
898
+ cardStoreRef: `cloud:${boardId}:cards`,
899
+ outputsStoreRef: `cloud:${boardId}:runtime-out`,
900
+ artifactsStoreRef: `cloud:${boardId}:artifacts`,
901
+ scratchStoreRef: `cloud:${boardId}:scratch`,
902
+ archiveStoreRef: `cloud:${boardId}:archive`,
903
+ notifyRef: { kind: 'in-memory-notify', value: notifyChannel },
904
+ taskExecutorRef: cloudHostedTaskExecutorRef,
905
+ chatHandlerFlow,
906
+ inferenceAdapterRef: makeExecutionRef(infAdapterPath),
907
+ };
908
+ }
909
+
422
910
  const baseRef = parseRef(serializeRef({ kind: 'fs-path', value: boardDir }));
423
- const boardWorkerTransport = normalizeBoardWorkerTransport(executionExtra.taskExecutorTransport);
424
- const callbackSelfRef = makeBoardWorkerCallbackSelfRef(executionExtra.serverUrl, executionExtra.apiBasePath, boardWorkerTransport, boardId);
425
911
  const boardAdapter = createFsBoardPlatformAdapter(baseRef, {
426
912
  notifyChannel,
427
- ...(callbackSelfRef ? { selfRef: callbackSelfRef } : {}),
913
+ ...(callbackTransport ? { callbackTransport } : {}),
428
914
  });
429
915
  const nonCoreAdapter = createFsBoardNonCorePlatformAdapter(baseRef, {
430
916
  notifyChannel,
431
- ...(callbackSelfRef ? { selfRef: callbackSelfRef } : {}),
917
+ ...(callbackTransport ? { callbackTransport } : {}),
432
918
  });
433
919
  const localSyncTaskExecutorRef = makeLocalTaskExecutorRef(taskExecPath, executionExtra);
434
920
  if (localSyncTaskExecutorRef) {
@@ -536,7 +1022,11 @@ const runtime = createMultiBoardServerRuntime({
536
1022
  );
537
1023
  const infAdapterPath = resolveFromConfig(regular.inferenceAdapterPath) || (entry?.inferenceAdapterPath || configuredInferenceAdapterPath);
538
1024
  const stepMachinePath = resolveFromConfig(regular.stepMachineCliPath || cfg?.stepMachineCliPath) || (entry?.stepMachineCliPath || configuredStepMachineCliPath);
539
- const boardWorkerTransport = normalizeBoardWorkerTransport(regular.taskExecutorTransport || entry?.taskExecutorTransport || configuredBoardWorkerTransport);
1025
+ const runtimeMode = normalizeRuntimeMode(regular.mode || entry?.mode || cfg?.mode || configuredRuntimeMode);
1026
+ const boardWorkerTransport = normalizeHostedBoardWorkerTransport(
1027
+ runtimeMode,
1028
+ normalizeBoardWorkerTransport(regular.taskExecutorTransport || entry?.taskExecutorTransport || configuredBoardWorkerTransport),
1029
+ );
540
1030
  const chatInvokeRefTimeoutMs = configuredInvokeRefTimeoutMs;
541
1031
  const chatCopilotTimeoutMs = configuredCopilotTimeoutMs;
542
1032
 
@@ -577,11 +1067,20 @@ const runtime = createMultiBoardServerRuntime({
577
1067
  ...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
578
1068
  };
579
1069
 
580
- const baseCfg = buildBoardContextConfig('base', boardDir, taskExecPath, chatHandlerFlow, infAdapterPath, boardId, baseExecutionExtra);
1070
+ const baseCfg = buildBoardContextConfig('base', boardDir, taskExecPath, chatHandlerFlow, infAdapterPath, boardId, baseExecutionExtra, runtimeMode);
581
1071
  const boards = [baseCfg];
582
1072
 
583
1073
  demoPrepSetup({ cardsDir, boardDir });
584
1074
 
1075
+ if (runtimeMode === 'cloud' && cardsDir) {
1076
+ const cards = createFsCardSource(cardsDir, selectedCardsPattern).listCards();
1077
+ if (cards.length) {
1078
+ const cloudBundle = getCloudBoardBundle(boardId, baseCfg.notifyRef.value);
1079
+
1080
+ seedCloudCardStore(cloudBundle.getKvRef(baseCfg.cardStoreRef), cards);
1081
+ }
1082
+ }
1083
+
585
1084
  const chatStorage = createFsBoardChatStorage(boardDir);
586
1085
  hostedBoardChatStorages.set(boardId, chatStorage);
587
1086
 
@@ -612,10 +1111,10 @@ const runtime = createMultiBoardServerRuntime({
612
1111
  if (hostedBoardWorkerDispatch) {
613
1112
  hostedBoardWorkerDispatchers.set(boardId, hostedBoardWorkerDispatch);
614
1113
  }
615
- const previousQueueStop = hostedBoardWorkerQueueStops.get(boardId);
1114
+ const previousQueueStop = hostedQueueLaneStops.get(boardId);
616
1115
  if (previousQueueStop) {
617
1116
  previousQueueStop();
618
- hostedBoardWorkerQueueStops.delete(boardId);
1117
+ hostedQueueLaneStops.delete(boardId);
619
1118
  }
620
1119
  if (boardWorkerTransport === 'in-process-loop' && hostedBoardWorkerDispatch) {
621
1120
  registerInProcessExecutionHandler(`board:${boardId}:board-worker`, async (_ref, args) => {
@@ -625,7 +1124,7 @@ const runtime = createMultiBoardServerRuntime({
625
1124
  return { result: 'success', data: { dispatched: true } };
626
1125
  });
627
1126
  }
628
- if ((boardWorkerTransport === 'in-process-loop' || boardWorkerTransport === 'queue') && hostedBoardWorkerDispatch) {
1127
+ if ((boardWorkerTransport === 'in-process-loop' || boardWorkerTransport === 'queue' || boardWorkerTransport === 'http') && hostedBoardWorkerDispatch) {
629
1128
  registerInProcessBoardWorkerCallback(`board:${boardId}:board-worker-callback`, (payload) => {
630
1129
  if (payload.outcome === 'success') {
631
1130
  return singleBoardRuntime.reportSourceFetched(payload.token, String(payload.ref || ''));
@@ -633,25 +1132,25 @@ const runtime = createMultiBoardServerRuntime({
633
1132
  return singleBoardRuntime.reportSourceFetchFailure(payload.token, String(payload.reason || 'unknown'));
634
1133
  });
635
1134
  }
636
- if (boardWorkerTransport === 'queue' && hostedBoardWorkerDispatch) {
637
- const stopQueueRunner = startBoardWorkerQueueRunner({
638
- workerStore: baseCfg.boardAdapter.boardWorkerStore(),
639
- executeBoardWorkerRequest: hostedBoardWorkerDispatch,
640
- onError: (error, lease) => {
641
- logger.error(
642
- `[board-server] queued board-worker failed for ${boardId} (attempt ${lease.attempt}): ${String(error && error.message || error)}`,
643
- );
644
- },
645
- });
646
- hostedBoardWorkerQueueStops.set(boardId, stopQueueRunner);
647
- }
1135
+ const stopQueueRunner = startQueueLaneRunners(createHostedBoardQueueLaneRegistry({
1136
+ boardId,
1137
+ runtime: singleBoardRuntime,
1138
+ boardAdapter: baseCfg.boardAdapter,
1139
+ logger,
1140
+ ...(boardWorkerTransport === 'queue' && hostedBoardWorkerDispatch
1141
+ ? { executeTaskExecutorRequest: hostedBoardWorkerDispatch }
1142
+ : {}),
1143
+ }));
1144
+ hostedQueueLaneStops.set(boardId, stopQueueRunner);
648
1145
 
649
1146
  // Seed card store from source cardsDir if empty
650
- const existing = singleBoardRuntime.cardStore.get({});
651
- const isEmpty = existing.status !== 'success' || !existing.data?.cards?.length;
652
- if (isEmpty && cardsDir) {
653
- const cards = createFsCardSource(cardsDir, selectedCardsPattern).listCards();
654
- if (cards.length) singleBoardRuntime.cardStore.set({ body: cards });
1147
+ if (runtimeMode === 'sync') {
1148
+ const existing = singleBoardRuntime.cardStore.get({});
1149
+ const isEmpty = existing.status !== 'success' || !existing.data?.cards?.length;
1150
+ if (isEmpty && cardsDir) {
1151
+ const cards = createFsCardSource(cardsDir, selectedCardsPattern).listCards();
1152
+ if (cards.length) singleBoardRuntime.cardStore.set({ body: cards });
1153
+ }
655
1154
  }
656
1155
 
657
1156
  return singleBoardRuntime;