yaml-flow 5.2.1 → 5.2.5
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/board-livecards-server-runtime.js +48 -9
- package/browser/live-cards.js +55 -9
- package/dist/cli/board-live-cards-cli.cjs +9 -6
- package/dist/cli/board-live-cards-cli.cjs.map +1 -1
- package/dist/cli/board-live-cards-cli.js +9 -6
- package/dist/cli/board-live-cards-cli.js.map +1 -1
- package/examples/example-board/demo-chat-handler.js +111 -362
- package/examples/example-board/demo-server.js +6 -0
- package/examples/example-board/demo-shell-browser.html +3 -3
- package/examples/example-board/demo-shell-with-server.html +4 -4
- package/package.json +1 -1
|
@@ -527,7 +527,7 @@ export function createExampleBoardServerRuntime(options = {}) {
|
|
|
527
527
|
|
|
528
528
|
for (const sourceDef of cardDefinition.sources) {
|
|
529
529
|
if (!sourceDef || !sourceDef.bindTo || !sourceDef.outputFile) continue;
|
|
530
|
-
const filePath = path.join(boardDir, sourceDef.outputFile);
|
|
530
|
+
const filePath = path.join(boardDir, cardDefinition.id, sourceDef.outputFile);
|
|
531
531
|
if (!fs.existsSync(filePath)) continue;
|
|
532
532
|
|
|
533
533
|
const raw = fs.readFileSync(filePath, 'utf-8').trim();
|
|
@@ -563,13 +563,15 @@ export function createExampleBoardServerRuntime(options = {}) {
|
|
|
563
563
|
function readChatSignal(cardId) {
|
|
564
564
|
const chatsDir = path.join(tmpCardsDir, cardId, 'chats');
|
|
565
565
|
if (!fs.existsSync(chatsDir)) {
|
|
566
|
-
return { count: 0, latest_mtime_ms: 0 };
|
|
566
|
+
return { count: 0, latest_mtime_ms: 0, processing: false };
|
|
567
567
|
}
|
|
568
568
|
|
|
569
569
|
let count = 0;
|
|
570
570
|
let latestMtimeMs = 0;
|
|
571
|
+
let processing = false;
|
|
571
572
|
for (const entry of fs.readdirSync(chatsDir, { withFileTypes: true })) {
|
|
572
573
|
if (!entry.isFile()) continue;
|
|
574
|
+
if (entry.name === '.processing') { processing = true; continue; }
|
|
573
575
|
count += 1;
|
|
574
576
|
try {
|
|
575
577
|
const st = fs.statSync(path.join(chatsDir, entry.name));
|
|
@@ -580,7 +582,7 @@ export function createExampleBoardServerRuntime(options = {}) {
|
|
|
580
582
|
}
|
|
581
583
|
}
|
|
582
584
|
|
|
583
|
-
return { count, latest_mtime_ms: latestMtimeMs };
|
|
585
|
+
return { count, latest_mtime_ms: latestMtimeMs, processing };
|
|
584
586
|
}
|
|
585
587
|
|
|
586
588
|
function buildPublishedRuntimePayload() {
|
|
@@ -642,6 +644,21 @@ export function createExampleBoardServerRuntime(options = {}) {
|
|
|
642
644
|
fs.copyFileSync(src, dst);
|
|
643
645
|
}
|
|
644
646
|
|
|
647
|
+
// Concatenate agent-instructions*.md files into copilot-instructions.md at boardSetupRoot
|
|
648
|
+
const boardSetupRoot = path.dirname(boardDir);
|
|
649
|
+
const agentInstructionFiles = ['agent-instructions.md', 'agent-instructions-cardlayout.md'];
|
|
650
|
+
const srcDir = path.dirname(cardsDir); // board source dir where agent-instructions*.md live
|
|
651
|
+
const parts = [];
|
|
652
|
+
for (const fname of agentInstructionFiles) {
|
|
653
|
+
const fpath = path.join(srcDir, fname);
|
|
654
|
+
if (fs.existsSync(fpath)) {
|
|
655
|
+
parts.push(fs.readFileSync(fpath, 'utf-8').trimEnd());
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (parts.length > 0) {
|
|
659
|
+
fs.writeFileSync(path.join(boardSetupRoot, 'copilot-instructions.md'), parts.join('\n\n') + '\n', 'utf-8');
|
|
660
|
+
}
|
|
661
|
+
|
|
645
662
|
didDemoSetup = true;
|
|
646
663
|
}
|
|
647
664
|
|
|
@@ -930,8 +947,9 @@ export function createExampleBoardServerRuntime(options = {}) {
|
|
|
930
947
|
if (!entry.isFile()) continue;
|
|
931
948
|
const name = entry.name;
|
|
932
949
|
const parsed = String(name).match(/^(\d+)[-_]([a-z0-9_-]+)\.txt$/i);
|
|
933
|
-
|
|
934
|
-
const
|
|
950
|
+
if (!parsed) continue; // skip .processing and other non-chat files
|
|
951
|
+
const serial = parseInt(parsed[1], 10);
|
|
952
|
+
const role = parsed[2].toLowerCase();
|
|
935
953
|
const filePath = path.join(chatsDir, name);
|
|
936
954
|
const text = fs.readFileSync(filePath, 'utf-8');
|
|
937
955
|
const stat = fs.statSync(filePath);
|
|
@@ -975,23 +993,44 @@ export function createExampleBoardServerRuntime(options = {}) {
|
|
|
975
993
|
|
|
976
994
|
// Fire-and-forget invocation of .chat-handler after a user chat message is persisted.
|
|
977
995
|
// boardDir/.chat-handler must contain the handler command as a single-line string.
|
|
978
|
-
// Called with: --boardId <id> --cardId <id> --
|
|
979
|
-
//
|
|
996
|
+
// Called with: --boardId <id> --cardId <id> --extraEncJson <base64json>
|
|
997
|
+
// extraEncJson decodes to:
|
|
998
|
+
// boardSetupRoot — absolute path to board root (parent of runtime/, surface/, runtime-out/)
|
|
999
|
+
// boardRuntimeDir — relative: 'runtime'
|
|
1000
|
+
// runtimeStatusDir— relative: 'runtime-out'
|
|
1001
|
+
// cardsDir — relative: 'surface/tmp-cards'
|
|
1002
|
+
// chatDir — relative (from cardsDir): e.g. 'card-portfolio/chats'
|
|
1003
|
+
// lastChatFile — filename of the just-written user message, e.g. '001_user.txt'
|
|
980
1004
|
// Handler failures are logged and silently ignored — chat-send response is never affected.
|
|
981
1005
|
function invokeChatHandler(cardId, chatsDir, lastChatFile) {
|
|
982
1006
|
const handlerFile = path.join(boardDir, '.chat-handler');
|
|
983
1007
|
if (!fs.existsSync(handlerFile)) return;
|
|
984
1008
|
const handlerCmd = fs.readFileSync(handlerFile, 'utf-8').trim();
|
|
985
1009
|
if (!handlerCmd) return;
|
|
986
|
-
const
|
|
1010
|
+
const boardSetupRoot = path.dirname(boardDir);
|
|
1011
|
+
const processingFile = path.join(chatsDir, '.processing');
|
|
1012
|
+
try { fs.mkdirSync(chatsDir, { recursive: true }); fs.writeFileSync(processingFile, '', 'utf-8'); } catch {}
|
|
1013
|
+
const extra = Buffer.from(JSON.stringify({
|
|
1014
|
+
boardSetupRoot,
|
|
1015
|
+
boardRuntimeDir: path.relative(boardSetupRoot, boardDir),
|
|
1016
|
+
runtimeStatusDir: path.relative(boardSetupRoot, runtimeOutDir),
|
|
1017
|
+
cardsDir: path.relative(boardSetupRoot, tmpCardsDir),
|
|
1018
|
+
chatDir: chatsDir,
|
|
1019
|
+
lastChatFile,
|
|
1020
|
+
})).toString('base64');
|
|
987
1021
|
try {
|
|
988
|
-
const proc = spawn(handlerCmd, [
|
|
1022
|
+
const proc = spawn(handlerCmd, [
|
|
1023
|
+
'--boardId', boardId, '--cardId', String(cardId),
|
|
1024
|
+
'--extraEncJson', extra,
|
|
1025
|
+
'--cleanOnExit', processingFile,
|
|
1026
|
+
], {
|
|
989
1027
|
shell: true,
|
|
990
1028
|
stdio: 'ignore',
|
|
991
1029
|
});
|
|
992
1030
|
proc.unref();
|
|
993
1031
|
console.log(`[chat-handler] invoked for card "${cardId}" (boardId: "${boardId}")`);
|
|
994
1032
|
} catch (err) {
|
|
1033
|
+
try { fs.unlinkSync(processingFile); } catch {}
|
|
995
1034
|
console.warn(`[chat-handler] spawn failed for card "${cardId}":`, (err && err.message) || String(err));
|
|
996
1035
|
}
|
|
997
1036
|
}
|
package/browser/live-cards.js
CHANGED
|
@@ -64,12 +64,16 @@ var LiveCard = (function () {
|
|
|
64
64
|
.lc-staged-file { display:flex; align-items:center; gap:.5rem; padding:.125rem 0; }
|
|
65
65
|
.lc-chat-el { display:flex; flex-direction:column; }
|
|
66
66
|
.lc-chat-body { flex:1; overflow-y:auto; max-height:300px; padding:.25rem; }
|
|
67
|
-
.lc-chat-bubble { padding:.5rem .75rem; margin:.375rem 0; border-radius:.75rem; max-width:85%; word-wrap:break-word; font-size:.875rem; line-height:1.4; }
|
|
68
|
-
.lc-chat-bubble-user { background:var(--bs-primary-bg-subtle,#cfe2ff); margin-left:auto; }
|
|
69
|
-
.lc-chat-bubble-assistant { background:var(--bs-light,#f8f9fa); }
|
|
70
|
-
.lc-chat-bubble-system { background:transparent; color:var(--bs-secondary,#6c757d); font-style:italic; text-align:center; max-width:100%; font-size:.8rem; }
|
|
67
|
+
.lc-chat-bubble { padding:.5rem .75rem; margin:.375rem 0; border-radius:.75rem; max-width:85%; word-wrap:break-word; font-size:.875rem; line-height:1.4; display:flex; gap:.5rem; align-items:flex-start; }
|
|
68
|
+
.lc-chat-bubble-user { background:var(--bs-primary-bg-subtle,#cfe2ff); margin-left:auto; flex-direction:row-reverse; }
|
|
69
|
+
.lc-chat-bubble-assistant { background:var(--bs-light,#f8f9fa); border:1px solid var(--bs-border-color,#dee2e6); }
|
|
70
|
+
.lc-chat-bubble-system { background:transparent; color:var(--bs-secondary,#6c757d); font-style:italic; text-align:center; max-width:100%; font-size:.8rem; align-self:center; gap:0; padding:.125rem .5rem; }
|
|
71
|
+
.lc-chat-icon { flex-shrink:0; line-height:1.4; opacity:.6; display:flex; align-items:center; margin-top:.15rem; }
|
|
72
|
+
.lc-chat-bubble-content { flex:1; min-width:0; }
|
|
73
|
+
.lc-chat-bubble-content p:last-child { margin-bottom:0; }
|
|
71
74
|
.lc-chat-bubble-pending { opacity:.85; }
|
|
72
75
|
.lc-chat-bubble-pending .spinner-border { width:.75rem; height:.75rem; margin-left:.4rem; border-width:.12em; vertical-align:middle; }
|
|
76
|
+
.lc-chat-processing { display:flex; align-items:center; gap:.5rem; padding:.375rem .75rem; color:var(--bs-secondary,#6c757d); font-size:.8rem; font-style:italic; }
|
|
73
77
|
.lc-chat-input-bar { display:flex; gap:.25rem; align-items:center; }
|
|
74
78
|
.lc-chat-modal-input-row { display:flex; align-items:center; gap:.375rem; }
|
|
75
79
|
.lc-chat-modal-input-row .form-control { min-width:0; }
|
|
@@ -403,25 +407,46 @@ var LiveCard = (function () {
|
|
|
403
407
|
function _appendModalChatMessage(role, text, files) {
|
|
404
408
|
_ensureChatModal();
|
|
405
409
|
if (!_chatModal.body) return;
|
|
410
|
+
if (!text && !files) return; // skip empty messages
|
|
406
411
|
|
|
407
|
-
const bubble = document.createElement('div');
|
|
408
412
|
const normalizedRole = role === 'user' || role === 'assistant' ? role : 'system';
|
|
409
413
|
const roleClass = normalizedRole === 'user'
|
|
410
414
|
? 'lc-chat-bubble-user'
|
|
411
415
|
: (normalizedRole === 'assistant' ? 'lc-chat-bubble-assistant' : 'lc-chat-bubble-system');
|
|
416
|
+
|
|
417
|
+
const bubble = document.createElement('div');
|
|
412
418
|
bubble.className = 'lc-chat-bubble ' + roleClass;
|
|
413
|
-
|
|
419
|
+
|
|
420
|
+
if (normalizedRole !== 'system') {
|
|
421
|
+
// SVG icons: person for user, sparkle-star for assistant
|
|
422
|
+
const userSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/></svg>';
|
|
423
|
+
const asstSvg = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>';
|
|
424
|
+
const iconEl = document.createElement('span');
|
|
425
|
+
iconEl.className = 'lc-chat-icon';
|
|
426
|
+
iconEl.setAttribute('aria-hidden', 'true');
|
|
427
|
+
iconEl.innerHTML = normalizedRole === 'user' ? userSvg : asstSvg;
|
|
428
|
+
bubble.appendChild(iconEl);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const content = document.createElement('div');
|
|
432
|
+
content.className = 'lc-chat-bubble-content';
|
|
433
|
+
if (normalizedRole === 'assistant') {
|
|
434
|
+
content.innerHTML = _renderMd(text || '');
|
|
435
|
+
} else {
|
|
436
|
+
content.textContent = text || '';
|
|
437
|
+
}
|
|
414
438
|
|
|
415
439
|
if (Array.isArray(files) && files.length) {
|
|
416
440
|
const meta = document.createElement('div');
|
|
417
|
-
meta.className = '
|
|
418
|
-
meta.textContent = files.map(function (f) {
|
|
441
|
+
meta.className = 'small mt-1 text-muted';
|
|
442
|
+
meta.textContent = '\uD83D\uDCCE ' + files.map(function (f) {
|
|
419
443
|
if (!f) return 'file';
|
|
420
444
|
return typeof f === 'string' ? f : (f.name || 'file');
|
|
421
445
|
}).join(', ');
|
|
422
|
-
|
|
446
|
+
content.appendChild(meta);
|
|
423
447
|
}
|
|
424
448
|
|
|
449
|
+
bubble.appendChild(content);
|
|
425
450
|
_chatModal.body.appendChild(bubble);
|
|
426
451
|
_chatModal.body.scrollTop = _chatModal.body.scrollHeight;
|
|
427
452
|
}
|
|
@@ -471,9 +496,29 @@ var LiveCard = (function () {
|
|
|
471
496
|
_chatModal.body.innerHTML = '';
|
|
472
497
|
if (!normalized.length) {
|
|
473
498
|
_chatModal.body.innerHTML = '<div class="text-muted small">No messages yet.</div>';
|
|
499
|
+
_syncProcessingBar(nodeId);
|
|
474
500
|
return;
|
|
475
501
|
}
|
|
476
502
|
normalized.forEach(function (m) { _appendModalChatMessage(m.role, m.text, m.files); });
|
|
503
|
+
_syncProcessingBar(nodeId);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function _syncProcessingBar(nodeId) {
|
|
507
|
+
if (!_chatModal.body) return;
|
|
508
|
+
const node = nodeId ? cfg.resolve(nodeId) : null;
|
|
509
|
+
const isProcessing = !!(node && node.card_data && node.card_data.__chat_signal && node.card_data.__chat_signal.processing);
|
|
510
|
+
let ind = _chatModal.body.querySelector('.lc-chat-processing');
|
|
511
|
+
if (isProcessing) {
|
|
512
|
+
if (!ind) {
|
|
513
|
+
ind = document.createElement('div');
|
|
514
|
+
ind.className = 'lc-chat-processing';
|
|
515
|
+
ind.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-label="AI working"></span><span>\u2728 AI working\u2026</span>';
|
|
516
|
+
_chatModal.body.appendChild(ind);
|
|
517
|
+
}
|
|
518
|
+
_chatModal.body.scrollTop = _chatModal.body.scrollHeight;
|
|
519
|
+
} else {
|
|
520
|
+
if (ind) ind.remove();
|
|
521
|
+
}
|
|
477
522
|
}
|
|
478
523
|
|
|
479
524
|
async function openChatModal(nodeId) {
|
|
@@ -2130,6 +2175,7 @@ var LiveCard = (function () {
|
|
|
2130
2175
|
const nodeId = _chatModal.currentNodeId;
|
|
2131
2176
|
if (!nodeId || !_chatModal.backdrop || !_chatModal.backdrop.classList.contains('lc-open')) return;
|
|
2132
2177
|
_clearPendingModalChatMessages();
|
|
2178
|
+
_syncProcessingBar(nodeId);
|
|
2133
2179
|
_refreshModalChatHistory(nodeId).catch(function () {});
|
|
2134
2180
|
}
|
|
2135
2181
|
|
|
@@ -1459,7 +1459,7 @@ function nextEntryAfterFetchFailure(entry, reason) {
|
|
|
1459
1459
|
return next;
|
|
1460
1460
|
}
|
|
1461
1461
|
function runtimePath(boardDir, cardId) {
|
|
1462
|
-
return path__namespace.join(boardDir,
|
|
1462
|
+
return path__namespace.join(boardDir, cardId, "runtime.json");
|
|
1463
1463
|
}
|
|
1464
1464
|
function readRuntimeState(boardDir, cardId) {
|
|
1465
1465
|
const p = runtimePath(boardDir, cardId);
|
|
@@ -1471,7 +1471,9 @@ function readRuntimeState(boardDir, cardId) {
|
|
|
1471
1471
|
}
|
|
1472
1472
|
}
|
|
1473
1473
|
function writeRuntimeState(boardDir, cardId, state) {
|
|
1474
|
-
|
|
1474
|
+
const p = runtimePath(boardDir, cardId);
|
|
1475
|
+
fs__namespace.mkdirSync(path__namespace.dirname(p), { recursive: true });
|
|
1476
|
+
fs__namespace.writeFileSync(p, JSON.stringify(state, null, 2));
|
|
1475
1477
|
}
|
|
1476
1478
|
function appendEventToJournal(boardDir, event) {
|
|
1477
1479
|
const journalPath = path__namespace.join(boardDir, JOURNAL_FILE);
|
|
@@ -1798,7 +1800,7 @@ function createBoardReactiveGraph(boardDir) {
|
|
|
1798
1800
|
const sourcesData = {};
|
|
1799
1801
|
for (const src of allSources) {
|
|
1800
1802
|
if (src.outputFile) {
|
|
1801
|
-
const filePath = path__namespace.join(boardDir, src.outputFile);
|
|
1803
|
+
const filePath = path__namespace.join(boardDir, cardId, src.outputFile);
|
|
1802
1804
|
if (fs__namespace.existsSync(filePath)) {
|
|
1803
1805
|
const raw = fs__namespace.readFileSync(filePath, "utf-8").trim();
|
|
1804
1806
|
try {
|
|
@@ -2329,10 +2331,10 @@ function cmdSourceDataFetched(args) {
|
|
|
2329
2331
|
process.exit(1);
|
|
2330
2332
|
}
|
|
2331
2333
|
const { cbk, rg, cid, b, d, cs } = payload;
|
|
2332
|
-
const destPath = path__namespace.join(rg, d);
|
|
2334
|
+
const destPath = path__namespace.join(rg, cid, d);
|
|
2333
2335
|
fs__namespace.mkdirSync(path__namespace.dirname(destPath), { recursive: true });
|
|
2334
2336
|
fs__namespace.renameSync(tmpFile, destPath);
|
|
2335
|
-
console.log(`[source-data-fetched] ${cid}.${b} \u2192 ${d}`);
|
|
2337
|
+
console.log(`[source-data-fetched] ${cid}.${b} \u2192 ${cid}/${d}`);
|
|
2336
2338
|
const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2337
2339
|
const cbkDecoded = decodeCallbackToken2(cbk);
|
|
2338
2340
|
if (!cbkDecoded) {
|
|
@@ -2664,7 +2666,7 @@ function cmdInferenceDone(args) {
|
|
|
2664
2666
|
inferenceCompletedAt
|
|
2665
2667
|
};
|
|
2666
2668
|
fs__namespace.writeFileSync(cardPath, JSON.stringify(card, null, 2), "utf-8");
|
|
2667
|
-
const runtimePath2 = path__namespace.join(dir,
|
|
2669
|
+
const runtimePath2 = path__namespace.join(dir, taskName, "runtime.json");
|
|
2668
2670
|
let runtime = { _sources: {} };
|
|
2669
2671
|
if (fs__namespace.existsSync(runtimePath2)) {
|
|
2670
2672
|
try {
|
|
@@ -2674,6 +2676,7 @@ function cmdInferenceDone(args) {
|
|
|
2674
2676
|
}
|
|
2675
2677
|
const inferenceEntry = runtime._inferenceEntry ?? {};
|
|
2676
2678
|
runtime._inferenceEntry = nextEntryAfterFetchDelivery(inferenceEntry, inferenceCompletedAt);
|
|
2679
|
+
fs__namespace.mkdirSync(path__namespace.dirname(runtimePath2), { recursive: true });
|
|
2677
2680
|
fs__namespace.writeFileSync(runtimePath2, JSON.stringify(runtime, null, 2), "utf-8");
|
|
2678
2681
|
appendEventToJournal(dir, {
|
|
2679
2682
|
type: "task-progress",
|