agentflow-core 0.2.1 → 0.2.3
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/dist/{chunk-FJVQYJFB.js → chunk-5SPZPOFN.js} +188 -101
- package/dist/cli.cjs +192 -104
- package/dist/cli.js +5 -4
- package/dist/index.cjs +188 -101
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -647,7 +647,7 @@ var C = {
|
|
|
647
647
|
white: "\x1B[37m"
|
|
648
648
|
};
|
|
649
649
|
function parseArgs(argv) {
|
|
650
|
-
const config = {
|
|
650
|
+
const config = { dirs: [], refreshMs: 3e3, recursive: false };
|
|
651
651
|
const args = argv.slice(0);
|
|
652
652
|
if (args[0] === "live") args.shift();
|
|
653
653
|
let i = 0;
|
|
@@ -664,18 +664,14 @@ function parseArgs(argv) {
|
|
|
664
664
|
} else if (arg === "--recursive" || arg === "-R") {
|
|
665
665
|
config.recursive = true;
|
|
666
666
|
i++;
|
|
667
|
-
} else if (arg === "--traces-dir" || arg === "-t") {
|
|
668
|
-
i++;
|
|
669
|
-
config.tracesDir = args[i] ?? config.tracesDir;
|
|
670
|
-
i++;
|
|
671
667
|
} else if (!arg.startsWith("-")) {
|
|
672
|
-
config.
|
|
668
|
+
config.dirs.push(resolve2(arg));
|
|
673
669
|
i++;
|
|
674
670
|
} else {
|
|
675
671
|
i++;
|
|
676
672
|
}
|
|
677
673
|
}
|
|
678
|
-
config.
|
|
674
|
+
if (config.dirs.length === 0) config.dirs.push(resolve2("."));
|
|
679
675
|
return config;
|
|
680
676
|
}
|
|
681
677
|
function printUsage() {
|
|
@@ -683,13 +679,13 @@ function printUsage() {
|
|
|
683
679
|
AgentFlow Live Monitor \u2014 real-time terminal dashboard for agent systems.
|
|
684
680
|
|
|
685
681
|
Auto-detects agent traces, state files, job schedulers, and session logs
|
|
686
|
-
from any JSON/JSONL files in the watched
|
|
682
|
+
from any JSON/JSONL files in the watched directories.
|
|
687
683
|
|
|
688
684
|
Usage:
|
|
689
|
-
agentflow live [
|
|
685
|
+
agentflow live [dir...] [options]
|
|
690
686
|
|
|
691
687
|
Arguments:
|
|
692
|
-
|
|
688
|
+
dir One or more directories to watch (default: .)
|
|
693
689
|
|
|
694
690
|
Options:
|
|
695
691
|
-r, --refresh <secs> Refresh interval in seconds (default: 3)
|
|
@@ -698,38 +694,42 @@ Options:
|
|
|
698
694
|
|
|
699
695
|
Examples:
|
|
700
696
|
agentflow live ./data
|
|
701
|
-
agentflow live ./traces --refresh 5
|
|
697
|
+
agentflow live ./traces ./cron ./workers --refresh 5
|
|
702
698
|
agentflow live /var/lib/myagent -R
|
|
703
699
|
`.trim());
|
|
704
700
|
}
|
|
705
|
-
function scanFiles(
|
|
701
|
+
function scanFiles(dirs, recursive) {
|
|
706
702
|
const results = [];
|
|
707
|
-
|
|
703
|
+
const seen = /* @__PURE__ */ new Set();
|
|
704
|
+
function scanDir(d, topLevel) {
|
|
708
705
|
try {
|
|
709
706
|
for (const f of readdirSync2(d)) {
|
|
710
707
|
if (f.startsWith(".")) continue;
|
|
711
708
|
const fp = join2(d, f);
|
|
709
|
+
if (seen.has(fp)) continue;
|
|
712
710
|
let stat;
|
|
713
711
|
try {
|
|
714
712
|
stat = statSync2(fp);
|
|
715
713
|
} catch {
|
|
716
714
|
continue;
|
|
717
715
|
}
|
|
718
|
-
if (stat.isDirectory() && recursive &&
|
|
719
|
-
scanDir(fp);
|
|
716
|
+
if (stat.isDirectory() && recursive && topLevel) {
|
|
717
|
+
scanDir(fp, false);
|
|
720
718
|
continue;
|
|
721
719
|
}
|
|
722
720
|
if (!stat.isFile()) continue;
|
|
723
721
|
if (f.endsWith(".json")) {
|
|
722
|
+
seen.add(fp);
|
|
724
723
|
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
|
|
725
724
|
} else if (f.endsWith(".jsonl")) {
|
|
725
|
+
seen.add(fp);
|
|
726
726
|
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
|
|
727
727
|
}
|
|
728
728
|
}
|
|
729
729
|
} catch {
|
|
730
730
|
}
|
|
731
731
|
}
|
|
732
|
-
scanDir(dir);
|
|
732
|
+
for (const dir of dirs) scanDir(dir, true);
|
|
733
733
|
results.sort((a, b) => b.mtime - a.mtime);
|
|
734
734
|
return results;
|
|
735
735
|
}
|
|
@@ -879,11 +879,21 @@ function processJsonlFile(file) {
|
|
|
879
879
|
return [];
|
|
880
880
|
}
|
|
881
881
|
}
|
|
882
|
+
var K = "\x1B[K";
|
|
883
|
+
function writeLine(lines, text) {
|
|
884
|
+
lines.push(text + K);
|
|
885
|
+
}
|
|
886
|
+
function flushLines(lines) {
|
|
887
|
+
process.stdout.write("\x1B[H");
|
|
888
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
889
|
+
process.stdout.write("\x1B[J");
|
|
890
|
+
}
|
|
882
891
|
var prevFileCount = 0;
|
|
883
892
|
var newExecCount = 0;
|
|
884
893
|
var sessionStart = Date.now();
|
|
894
|
+
var firstRender = true;
|
|
885
895
|
function render(config) {
|
|
886
|
-
const files = scanFiles(config.
|
|
896
|
+
const files = scanFiles(config.dirs, config.recursive);
|
|
887
897
|
if (files.length > prevFileCount && prevFileCount > 0) {
|
|
888
898
|
newExecCount += files.length - prevFileCount;
|
|
889
899
|
}
|
|
@@ -897,26 +907,58 @@ function render(config) {
|
|
|
897
907
|
if (r.traceData) allTraces.push(r.traceData);
|
|
898
908
|
}
|
|
899
909
|
}
|
|
900
|
-
const
|
|
910
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
901
911
|
for (const r of allRecords) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
912
|
+
const arr = byFile.get(r.file) ?? [];
|
|
913
|
+
arr.push(r);
|
|
914
|
+
byFile.set(r.file, arr);
|
|
915
|
+
}
|
|
916
|
+
const groups = [];
|
|
917
|
+
for (const [file, records] of byFile) {
|
|
918
|
+
if (records.length === 1) {
|
|
919
|
+
const r = records[0];
|
|
920
|
+
groups.push({
|
|
921
|
+
name: r.id,
|
|
922
|
+
source: r.source,
|
|
923
|
+
status: r.status,
|
|
924
|
+
lastTs: r.lastActive,
|
|
925
|
+
detail: r.detail,
|
|
926
|
+
children: [],
|
|
927
|
+
ok: r.status === "ok" ? 1 : 0,
|
|
928
|
+
fail: r.status === "error" ? 1 : 0,
|
|
929
|
+
running: r.status === "running" ? 1 : 0,
|
|
930
|
+
total: 1
|
|
931
|
+
});
|
|
932
|
+
} else {
|
|
933
|
+
const groupName = nameFromFile(file);
|
|
934
|
+
let lastTs = 0;
|
|
935
|
+
let ok = 0, fail = 0, running = 0;
|
|
936
|
+
for (const r of records) {
|
|
937
|
+
if (r.lastActive > lastTs) lastTs = r.lastActive;
|
|
938
|
+
if (r.status === "ok") ok++;
|
|
939
|
+
else if (r.status === "error") fail++;
|
|
940
|
+
else if (r.status === "running") running++;
|
|
941
|
+
}
|
|
942
|
+
const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
|
|
943
|
+
groups.push({
|
|
944
|
+
name: groupName,
|
|
945
|
+
source: records[0].source,
|
|
946
|
+
status,
|
|
947
|
+
lastTs,
|
|
948
|
+
detail: `${records.length} agents`,
|
|
949
|
+
children: records.sort((a, b) => b.lastActive - a.lastActive),
|
|
950
|
+
ok,
|
|
951
|
+
fail,
|
|
952
|
+
running,
|
|
953
|
+
total: records.length
|
|
954
|
+
});
|
|
914
955
|
}
|
|
915
956
|
}
|
|
916
|
-
|
|
917
|
-
const totExec =
|
|
918
|
-
const totFail =
|
|
919
|
-
const totRunning =
|
|
957
|
+
groups.sort((a, b) => b.lastTs - a.lastTs);
|
|
958
|
+
const totExec = allRecords.length;
|
|
959
|
+
const totFail = allRecords.filter((r) => r.status === "error").length;
|
|
960
|
+
const totRunning = allRecords.filter((r) => r.status === "running").length;
|
|
961
|
+
const uniqueAgents = new Set(allRecords.map((r) => r.id)).size;
|
|
920
962
|
const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
|
|
921
963
|
const now = Date.now();
|
|
922
964
|
const buckets = new Array(12).fill(0);
|
|
@@ -953,7 +995,21 @@ function render(config) {
|
|
|
953
995
|
const upMin = Math.floor(upSec / 60);
|
|
954
996
|
const upStr = upMin > 0 ? `${upMin}m ${upSec % 60}s` : `${upSec}s`;
|
|
955
997
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
956
|
-
|
|
998
|
+
function statusIcon(s, recent) {
|
|
999
|
+
if (s === "error") return `${C.red}\u25CF${C.reset}`;
|
|
1000
|
+
if (s === "running") return `${C.green}\u25CF${C.reset}`;
|
|
1001
|
+
if (s === "ok" && recent) return `${C.green}\u25CF${C.reset}`;
|
|
1002
|
+
if (s === "ok") return `${C.dim}\u25CB${C.reset}`;
|
|
1003
|
+
return `${C.dim}\u25CB${C.reset}`;
|
|
1004
|
+
}
|
|
1005
|
+
function statusText(g) {
|
|
1006
|
+
if (g.fail > 0 && g.ok === 0 && g.running === 0) return `${C.red}error${C.reset}`;
|
|
1007
|
+
if (g.running > 0) return `${C.green}running${C.reset}`;
|
|
1008
|
+
if (g.fail > 0) return `${C.yellow}${g.ok}ok/${g.fail}err${C.reset}`;
|
|
1009
|
+
if (g.ok > 0) return g.total > 1 ? `${C.green}${g.ok}/${g.total} ok${C.reset}` : `${C.green}ok${C.reset}`;
|
|
1010
|
+
return `${C.dim}idle${C.reset}`;
|
|
1011
|
+
}
|
|
1012
|
+
function sourceTag(s) {
|
|
957
1013
|
switch (s) {
|
|
958
1014
|
case "trace":
|
|
959
1015
|
return `${C.cyan}trace${C.reset}`;
|
|
@@ -966,92 +1022,115 @@ function render(config) {
|
|
|
966
1022
|
case "state":
|
|
967
1023
|
return `${C.dim}state${C.reset}`;
|
|
968
1024
|
}
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1025
|
+
}
|
|
1026
|
+
function timeStr(ts) {
|
|
1027
|
+
if (ts <= 0) return "n/a";
|
|
1028
|
+
return new Date(ts).toLocaleTimeString();
|
|
1029
|
+
}
|
|
1030
|
+
function truncate(s, max) {
|
|
1031
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1032
|
+
}
|
|
1033
|
+
if (firstRender) {
|
|
1034
|
+
process.stdout.write("\x1B[2J");
|
|
1035
|
+
firstRender = false;
|
|
1036
|
+
}
|
|
1037
|
+
const L = [];
|
|
1038
|
+
writeLine(L, `${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
|
|
1039
|
+
writeLine(L, `${C.bold}${C.cyan}\u2551${C.reset} ${C.bold}${C.white}AGENTFLOW LIVE${C.reset} ${C.green}\u25CF LIVE${C.reset} ${C.dim}${time}${C.reset} ${C.bold}${C.cyan}\u2551${C.reset}`);
|
|
973
1040
|
const metaLine = `Refresh: ${config.refreshMs / 1e3}s \xB7 Up: ${upStr} \xB7 Files: ${files.length}`;
|
|
974
1041
|
const pad1 = Math.max(0, 64 - metaLine.length);
|
|
975
|
-
|
|
976
|
-
|
|
1042
|
+
writeLine(L, `${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
|
|
1043
|
+
writeLine(L, `${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
|
|
977
1044
|
const sc = totFail === 0 ? C.green : C.yellow;
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
statusIcon = `${C.yellow}\u25CF${C.reset}`;
|
|
997
|
-
statusText = `${C.yellow}${ag.ok}ok/${ag.fail}err${C.reset}`;
|
|
998
|
-
} else if (ag.ok > 0) {
|
|
999
|
-
statusIcon = isRecent ? `${C.green}\u25CF${C.reset}` : `${C.dim}\u25CB${C.reset}`;
|
|
1000
|
-
statusText = ag.total > 1 ? `${C.green}${ag.ok}/${ag.total}${C.reset}` : `${C.green}ok${C.reset}`;
|
|
1045
|
+
writeLine(L, "");
|
|
1046
|
+
writeLine(L, ` ${C.bold}Agents${C.reset} ${sc}${uniqueAgents}${C.reset} ${C.bold}Records${C.reset} ${sc}${totExec}${C.reset} ${C.bold}Success${C.reset} ${sc}${sysRate}%${C.reset} ${C.bold}Running${C.reset} ${C.green}${totRunning}${C.reset} ${C.bold}Errors${C.reset} ${totFail > 0 ? C.red : C.dim}${totFail}${C.reset} ${C.bold}New${C.reset} ${C.yellow}+${newExecCount}${C.reset}`);
|
|
1047
|
+
writeLine(L, "");
|
|
1048
|
+
writeLine(L, ` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
|
|
1049
|
+
writeLine(L, "");
|
|
1050
|
+
writeLine(L, ` ${C.bold}${C.under}Agent Status Last Active Detail${C.reset}`);
|
|
1051
|
+
let lineCount = 0;
|
|
1052
|
+
for (const g of groups) {
|
|
1053
|
+
if (lineCount > 35) break;
|
|
1054
|
+
const isRecent = Date.now() - g.lastTs < 3e5;
|
|
1055
|
+
const icon = statusIcon(g.status, isRecent);
|
|
1056
|
+
const active = isRecent ? `${C.green}${timeStr(g.lastTs)}${C.reset}` : `${C.dim}${timeStr(g.lastTs)}${C.reset}`;
|
|
1057
|
+
if (g.children.length === 0) {
|
|
1058
|
+
const name = truncate(g.name, 26).padEnd(26);
|
|
1059
|
+
const st = statusText(g);
|
|
1060
|
+
const det = truncate(g.detail, 30);
|
|
1061
|
+
writeLine(L, ` ${icon} ${name} ${st.padEnd(20)} ${active.padEnd(20)} ${C.dim}${det}${C.reset}`);
|
|
1062
|
+
lineCount++;
|
|
1001
1063
|
} else {
|
|
1002
|
-
|
|
1003
|
-
|
|
1064
|
+
const name = truncate(g.name, 24).padEnd(24);
|
|
1065
|
+
const st = statusText(g);
|
|
1066
|
+
const tag = sourceTag(g.source);
|
|
1067
|
+
writeLine(L, ` ${icon} ${C.bold}${name}${C.reset} ${st.padEnd(20)} ${active.padEnd(20)} ${tag} ${C.dim}(${g.children.length} agents)${C.reset}`);
|
|
1068
|
+
lineCount++;
|
|
1069
|
+
const kids = g.children.slice(0, 12);
|
|
1070
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1071
|
+
if (lineCount > 35) break;
|
|
1072
|
+
const child = kids[i];
|
|
1073
|
+
const isLast = i === kids.length - 1;
|
|
1074
|
+
const connector = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
1075
|
+
const cIcon = statusIcon(child.status, Date.now() - child.lastActive < 3e5);
|
|
1076
|
+
const cName = truncate(child.id, 22).padEnd(22);
|
|
1077
|
+
const cActive = `${C.dim}${timeStr(child.lastActive)}${C.reset}`;
|
|
1078
|
+
const cDet = truncate(child.detail, 25);
|
|
1079
|
+
writeLine(L, ` ${C.dim}${connector}${C.reset} ${cIcon} ${cName} ${cActive.padEnd(20)} ${C.dim}${cDet}${C.reset}`);
|
|
1080
|
+
lineCount++;
|
|
1081
|
+
}
|
|
1082
|
+
if (g.children.length > 12) {
|
|
1083
|
+
writeLine(L, ` ${C.dim} ... +${g.children.length - 12} more${C.reset}`);
|
|
1084
|
+
lineCount++;
|
|
1085
|
+
}
|
|
1004
1086
|
}
|
|
1005
|
-
const name = ag.name.length > 23 ? ag.name.slice(0, 22) + "\u2026" : ag.name.padEnd(23);
|
|
1006
|
-
const src = sourceTag(ag.source).padEnd(16);
|
|
1007
|
-
const active = isRecent ? `${C.green}${lastTime}${C.reset}` : `${C.dim}${lastTime}${C.reset}`;
|
|
1008
|
-
const detail = ag.detail.length > 30 ? ag.detail.slice(0, 29) + "\u2026" : ag.detail;
|
|
1009
|
-
console.log(` ${statusIcon} ${name} ${src} ${statusText.padEnd(18)} ${active.padEnd(20)} ${C.dim}${detail}${C.reset}`);
|
|
1010
1087
|
}
|
|
1011
1088
|
if (distributedTraces.length > 0) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1089
|
+
writeLine(L, "");
|
|
1090
|
+
writeLine(L, ` ${C.bold}${C.under}Distributed Traces${C.reset}`);
|
|
1014
1091
|
for (const dt of distributedTraces.slice(0, 3)) {
|
|
1015
1092
|
const traceTime = new Date(dt.startTime).toLocaleTimeString();
|
|
1016
|
-
const
|
|
1093
|
+
const si = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
1017
1094
|
const dur = dt.endTime ? `${dt.endTime - dt.startTime}ms` : "running";
|
|
1018
1095
|
const tid = dt.traceId.slice(0, 8);
|
|
1019
|
-
|
|
1096
|
+
writeLine(L, ` ${si} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
|
|
1020
1097
|
const tree = getTraceTree(dt);
|
|
1021
1098
|
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
1022
|
-
const
|
|
1023
|
-
const depth = getDistDepth(dt,
|
|
1099
|
+
const tg = tree[i];
|
|
1100
|
+
const depth = getDistDepth(dt, tg.spanId);
|
|
1024
1101
|
const indent = " " + "\u2502 ".repeat(Math.max(0, depth - 1));
|
|
1025
1102
|
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
1026
1103
|
const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
1027
|
-
const gs =
|
|
1028
|
-
const gd =
|
|
1029
|
-
|
|
1104
|
+
const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
1105
|
+
const gd = tg.endTime ? `${tg.endTime - tg.startTime}ms` : "running";
|
|
1106
|
+
writeLine(L, `${indent}${conn}${gs} ${C.bold}${tg.agentId}${C.reset} ${C.dim}[${tg.trigger}] ${gd}${C.reset}`);
|
|
1030
1107
|
}
|
|
1031
1108
|
}
|
|
1032
1109
|
}
|
|
1033
|
-
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0,
|
|
1110
|
+
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
|
|
1034
1111
|
if (recentRecords.length > 0) {
|
|
1035
|
-
|
|
1036
|
-
|
|
1112
|
+
writeLine(L, "");
|
|
1113
|
+
writeLine(L, ` ${C.bold}${C.under}Recent Activity${C.reset}`);
|
|
1037
1114
|
for (const r of recentRecords) {
|
|
1038
1115
|
const icon = r.status === "ok" ? `${C.green}\u2713${C.reset}` : r.status === "error" ? `${C.red}\u2717${C.reset}` : r.status === "running" ? `${C.green}\u25B6${C.reset}` : `${C.dim}\u25CB${C.reset}`;
|
|
1039
1116
|
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
1040
|
-
const agent = r.id
|
|
1117
|
+
const agent = truncate(r.id, 26).padEnd(26);
|
|
1041
1118
|
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
1042
1119
|
const ageStr = age < 60 ? age + "s ago" : age < 3600 ? Math.floor(age / 60) + "m ago" : Math.floor(age / 3600) + "h ago";
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1120
|
+
const det = truncate(r.detail, 25);
|
|
1121
|
+
writeLine(L, ` ${icon} ${agent} ${C.dim}${t} ${ageStr.padStart(8)}${C.reset} ${C.dim}${det}${C.reset}`);
|
|
1045
1122
|
}
|
|
1046
1123
|
}
|
|
1047
1124
|
if (files.length === 0) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1125
|
+
writeLine(L, "");
|
|
1126
|
+
writeLine(L, ` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
|
|
1127
|
+
for (const d of config.dirs) writeLine(L, ` ${C.dim} ${d}${C.reset}`);
|
|
1051
1128
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1129
|
+
writeLine(L, "");
|
|
1130
|
+
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
1131
|
+
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1132
|
+
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1133
|
+
flushLines(L);
|
|
1055
1134
|
}
|
|
1056
1135
|
function getDistDepth(dt, spanId) {
|
|
1057
1136
|
if (!spanId) return 0;
|
|
@@ -1061,19 +1140,27 @@ function getDistDepth(dt, spanId) {
|
|
|
1061
1140
|
}
|
|
1062
1141
|
function startLive(argv) {
|
|
1063
1142
|
const config = parseArgs(argv);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
console.error(
|
|
1143
|
+
const valid = config.dirs.filter((d) => existsSync2(d));
|
|
1144
|
+
if (valid.length === 0) {
|
|
1145
|
+
console.error(`No valid directories found: ${config.dirs.join(", ")}`);
|
|
1146
|
+
console.error("Specify directories containing JSON/JSONL files: agentflow live <dir> [dir...]");
|
|
1067
1147
|
process.exit(1);
|
|
1068
1148
|
}
|
|
1149
|
+
const invalid = config.dirs.filter((d) => !existsSync2(d));
|
|
1150
|
+
if (invalid.length > 0) {
|
|
1151
|
+
console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
|
|
1152
|
+
}
|
|
1153
|
+
config.dirs = valid;
|
|
1069
1154
|
render(config);
|
|
1070
1155
|
let debounce = null;
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1156
|
+
for (const dir of config.dirs) {
|
|
1157
|
+
try {
|
|
1158
|
+
watch(dir, { recursive: config.recursive }, () => {
|
|
1159
|
+
if (debounce) clearTimeout(debounce);
|
|
1160
|
+
debounce = setTimeout(() => render(config), 500);
|
|
1161
|
+
});
|
|
1162
|
+
} catch {
|
|
1163
|
+
}
|
|
1077
1164
|
}
|
|
1078
1165
|
setInterval(() => render(config), config.refreshMs);
|
|
1079
1166
|
process.on("SIGINT", () => {
|