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
package/dist/index.cjs
CHANGED
|
@@ -693,7 +693,7 @@ var C = {
|
|
|
693
693
|
white: "\x1B[37m"
|
|
694
694
|
};
|
|
695
695
|
function parseArgs(argv) {
|
|
696
|
-
const config = {
|
|
696
|
+
const config = { dirs: [], refreshMs: 3e3, recursive: false };
|
|
697
697
|
const args = argv.slice(0);
|
|
698
698
|
if (args[0] === "live") args.shift();
|
|
699
699
|
let i = 0;
|
|
@@ -710,18 +710,14 @@ function parseArgs(argv) {
|
|
|
710
710
|
} else if (arg === "--recursive" || arg === "-R") {
|
|
711
711
|
config.recursive = true;
|
|
712
712
|
i++;
|
|
713
|
-
} else if (arg === "--traces-dir" || arg === "-t") {
|
|
714
|
-
i++;
|
|
715
|
-
config.tracesDir = args[i] ?? config.tracesDir;
|
|
716
|
-
i++;
|
|
717
713
|
} else if (!arg.startsWith("-")) {
|
|
718
|
-
config.
|
|
714
|
+
config.dirs.push((0, import_node_path2.resolve)(arg));
|
|
719
715
|
i++;
|
|
720
716
|
} else {
|
|
721
717
|
i++;
|
|
722
718
|
}
|
|
723
719
|
}
|
|
724
|
-
config.
|
|
720
|
+
if (config.dirs.length === 0) config.dirs.push((0, import_node_path2.resolve)("."));
|
|
725
721
|
return config;
|
|
726
722
|
}
|
|
727
723
|
function printUsage() {
|
|
@@ -729,13 +725,13 @@ function printUsage() {
|
|
|
729
725
|
AgentFlow Live Monitor \u2014 real-time terminal dashboard for agent systems.
|
|
730
726
|
|
|
731
727
|
Auto-detects agent traces, state files, job schedulers, and session logs
|
|
732
|
-
from any JSON/JSONL files in the watched
|
|
728
|
+
from any JSON/JSONL files in the watched directories.
|
|
733
729
|
|
|
734
730
|
Usage:
|
|
735
|
-
agentflow live [
|
|
731
|
+
agentflow live [dir...] [options]
|
|
736
732
|
|
|
737
733
|
Arguments:
|
|
738
|
-
|
|
734
|
+
dir One or more directories to watch (default: .)
|
|
739
735
|
|
|
740
736
|
Options:
|
|
741
737
|
-r, --refresh <secs> Refresh interval in seconds (default: 3)
|
|
@@ -744,38 +740,42 @@ Options:
|
|
|
744
740
|
|
|
745
741
|
Examples:
|
|
746
742
|
agentflow live ./data
|
|
747
|
-
agentflow live ./traces --refresh 5
|
|
743
|
+
agentflow live ./traces ./cron ./workers --refresh 5
|
|
748
744
|
agentflow live /var/lib/myagent -R
|
|
749
745
|
`.trim());
|
|
750
746
|
}
|
|
751
|
-
function scanFiles(
|
|
747
|
+
function scanFiles(dirs, recursive) {
|
|
752
748
|
const results = [];
|
|
753
|
-
|
|
749
|
+
const seen = /* @__PURE__ */ new Set();
|
|
750
|
+
function scanDir(d, topLevel) {
|
|
754
751
|
try {
|
|
755
752
|
for (const f of (0, import_node_fs2.readdirSync)(d)) {
|
|
756
753
|
if (f.startsWith(".")) continue;
|
|
757
754
|
const fp = (0, import_node_path2.join)(d, f);
|
|
755
|
+
if (seen.has(fp)) continue;
|
|
758
756
|
let stat;
|
|
759
757
|
try {
|
|
760
758
|
stat = (0, import_node_fs2.statSync)(fp);
|
|
761
759
|
} catch {
|
|
762
760
|
continue;
|
|
763
761
|
}
|
|
764
|
-
if (stat.isDirectory() && recursive &&
|
|
765
|
-
scanDir(fp);
|
|
762
|
+
if (stat.isDirectory() && recursive && topLevel) {
|
|
763
|
+
scanDir(fp, false);
|
|
766
764
|
continue;
|
|
767
765
|
}
|
|
768
766
|
if (!stat.isFile()) continue;
|
|
769
767
|
if (f.endsWith(".json")) {
|
|
768
|
+
seen.add(fp);
|
|
770
769
|
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
|
|
771
770
|
} else if (f.endsWith(".jsonl")) {
|
|
771
|
+
seen.add(fp);
|
|
772
772
|
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
|
|
773
773
|
}
|
|
774
774
|
}
|
|
775
775
|
} catch {
|
|
776
776
|
}
|
|
777
777
|
}
|
|
778
|
-
scanDir(dir);
|
|
778
|
+
for (const dir of dirs) scanDir(dir, true);
|
|
779
779
|
results.sort((a, b) => b.mtime - a.mtime);
|
|
780
780
|
return results;
|
|
781
781
|
}
|
|
@@ -925,11 +925,21 @@ function processJsonlFile(file) {
|
|
|
925
925
|
return [];
|
|
926
926
|
}
|
|
927
927
|
}
|
|
928
|
+
var K = "\x1B[K";
|
|
929
|
+
function writeLine(lines, text) {
|
|
930
|
+
lines.push(text + K);
|
|
931
|
+
}
|
|
932
|
+
function flushLines(lines) {
|
|
933
|
+
process.stdout.write("\x1B[H");
|
|
934
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
935
|
+
process.stdout.write("\x1B[J");
|
|
936
|
+
}
|
|
928
937
|
var prevFileCount = 0;
|
|
929
938
|
var newExecCount = 0;
|
|
930
939
|
var sessionStart = Date.now();
|
|
940
|
+
var firstRender = true;
|
|
931
941
|
function render(config) {
|
|
932
|
-
const files = scanFiles(config.
|
|
942
|
+
const files = scanFiles(config.dirs, config.recursive);
|
|
933
943
|
if (files.length > prevFileCount && prevFileCount > 0) {
|
|
934
944
|
newExecCount += files.length - prevFileCount;
|
|
935
945
|
}
|
|
@@ -943,26 +953,58 @@ function render(config) {
|
|
|
943
953
|
if (r.traceData) allTraces.push(r.traceData);
|
|
944
954
|
}
|
|
945
955
|
}
|
|
946
|
-
const
|
|
956
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
947
957
|
for (const r of allRecords) {
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
958
|
+
const arr = byFile.get(r.file) ?? [];
|
|
959
|
+
arr.push(r);
|
|
960
|
+
byFile.set(r.file, arr);
|
|
961
|
+
}
|
|
962
|
+
const groups = [];
|
|
963
|
+
for (const [file, records] of byFile) {
|
|
964
|
+
if (records.length === 1) {
|
|
965
|
+
const r = records[0];
|
|
966
|
+
groups.push({
|
|
967
|
+
name: r.id,
|
|
968
|
+
source: r.source,
|
|
969
|
+
status: r.status,
|
|
970
|
+
lastTs: r.lastActive,
|
|
971
|
+
detail: r.detail,
|
|
972
|
+
children: [],
|
|
973
|
+
ok: r.status === "ok" ? 1 : 0,
|
|
974
|
+
fail: r.status === "error" ? 1 : 0,
|
|
975
|
+
running: r.status === "running" ? 1 : 0,
|
|
976
|
+
total: 1
|
|
977
|
+
});
|
|
978
|
+
} else {
|
|
979
|
+
const groupName = nameFromFile(file);
|
|
980
|
+
let lastTs = 0;
|
|
981
|
+
let ok = 0, fail = 0, running = 0;
|
|
982
|
+
for (const r of records) {
|
|
983
|
+
if (r.lastActive > lastTs) lastTs = r.lastActive;
|
|
984
|
+
if (r.status === "ok") ok++;
|
|
985
|
+
else if (r.status === "error") fail++;
|
|
986
|
+
else if (r.status === "running") running++;
|
|
987
|
+
}
|
|
988
|
+
const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
|
|
989
|
+
groups.push({
|
|
990
|
+
name: groupName,
|
|
991
|
+
source: records[0].source,
|
|
992
|
+
status,
|
|
993
|
+
lastTs,
|
|
994
|
+
detail: `${records.length} agents`,
|
|
995
|
+
children: records.sort((a, b) => b.lastActive - a.lastActive),
|
|
996
|
+
ok,
|
|
997
|
+
fail,
|
|
998
|
+
running,
|
|
999
|
+
total: records.length
|
|
1000
|
+
});
|
|
960
1001
|
}
|
|
961
1002
|
}
|
|
962
|
-
|
|
963
|
-
const totExec =
|
|
964
|
-
const totFail =
|
|
965
|
-
const totRunning =
|
|
1003
|
+
groups.sort((a, b) => b.lastTs - a.lastTs);
|
|
1004
|
+
const totExec = allRecords.length;
|
|
1005
|
+
const totFail = allRecords.filter((r) => r.status === "error").length;
|
|
1006
|
+
const totRunning = allRecords.filter((r) => r.status === "running").length;
|
|
1007
|
+
const uniqueAgents = new Set(allRecords.map((r) => r.id)).size;
|
|
966
1008
|
const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
|
|
967
1009
|
const now = Date.now();
|
|
968
1010
|
const buckets = new Array(12).fill(0);
|
|
@@ -999,7 +1041,21 @@ function render(config) {
|
|
|
999
1041
|
const upMin = Math.floor(upSec / 60);
|
|
1000
1042
|
const upStr = upMin > 0 ? `${upMin}m ${upSec % 60}s` : `${upSec}s`;
|
|
1001
1043
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
1002
|
-
|
|
1044
|
+
function statusIcon(s, recent) {
|
|
1045
|
+
if (s === "error") return `${C.red}\u25CF${C.reset}`;
|
|
1046
|
+
if (s === "running") return `${C.green}\u25CF${C.reset}`;
|
|
1047
|
+
if (s === "ok" && recent) return `${C.green}\u25CF${C.reset}`;
|
|
1048
|
+
if (s === "ok") return `${C.dim}\u25CB${C.reset}`;
|
|
1049
|
+
return `${C.dim}\u25CB${C.reset}`;
|
|
1050
|
+
}
|
|
1051
|
+
function statusText(g) {
|
|
1052
|
+
if (g.fail > 0 && g.ok === 0 && g.running === 0) return `${C.red}error${C.reset}`;
|
|
1053
|
+
if (g.running > 0) return `${C.green}running${C.reset}`;
|
|
1054
|
+
if (g.fail > 0) return `${C.yellow}${g.ok}ok/${g.fail}err${C.reset}`;
|
|
1055
|
+
if (g.ok > 0) return g.total > 1 ? `${C.green}${g.ok}/${g.total} ok${C.reset}` : `${C.green}ok${C.reset}`;
|
|
1056
|
+
return `${C.dim}idle${C.reset}`;
|
|
1057
|
+
}
|
|
1058
|
+
function sourceTag(s) {
|
|
1003
1059
|
switch (s) {
|
|
1004
1060
|
case "trace":
|
|
1005
1061
|
return `${C.cyan}trace${C.reset}`;
|
|
@@ -1012,92 +1068,115 @@ function render(config) {
|
|
|
1012
1068
|
case "state":
|
|
1013
1069
|
return `${C.dim}state${C.reset}`;
|
|
1014
1070
|
}
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1071
|
+
}
|
|
1072
|
+
function timeStr(ts) {
|
|
1073
|
+
if (ts <= 0) return "n/a";
|
|
1074
|
+
return new Date(ts).toLocaleTimeString();
|
|
1075
|
+
}
|
|
1076
|
+
function truncate(s, max) {
|
|
1077
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
1078
|
+
}
|
|
1079
|
+
if (firstRender) {
|
|
1080
|
+
process.stdout.write("\x1B[2J");
|
|
1081
|
+
firstRender = false;
|
|
1082
|
+
}
|
|
1083
|
+
const L = [];
|
|
1084
|
+
writeLine(L, `${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
|
|
1085
|
+
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}`);
|
|
1019
1086
|
const metaLine = `Refresh: ${config.refreshMs / 1e3}s \xB7 Up: ${upStr} \xB7 Files: ${files.length}`;
|
|
1020
1087
|
const pad1 = Math.max(0, 64 - metaLine.length);
|
|
1021
|
-
|
|
1022
|
-
|
|
1088
|
+
writeLine(L, `${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
|
|
1089
|
+
writeLine(L, `${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
|
|
1023
1090
|
const sc = totFail === 0 ? C.green : C.yellow;
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
statusIcon = `${C.yellow}\u25CF${C.reset}`;
|
|
1043
|
-
statusText = `${C.yellow}${ag.ok}ok/${ag.fail}err${C.reset}`;
|
|
1044
|
-
} else if (ag.ok > 0) {
|
|
1045
|
-
statusIcon = isRecent ? `${C.green}\u25CF${C.reset}` : `${C.dim}\u25CB${C.reset}`;
|
|
1046
|
-
statusText = ag.total > 1 ? `${C.green}${ag.ok}/${ag.total}${C.reset}` : `${C.green}ok${C.reset}`;
|
|
1091
|
+
writeLine(L, "");
|
|
1092
|
+
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}`);
|
|
1093
|
+
writeLine(L, "");
|
|
1094
|
+
writeLine(L, ` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
|
|
1095
|
+
writeLine(L, "");
|
|
1096
|
+
writeLine(L, ` ${C.bold}${C.under}Agent Status Last Active Detail${C.reset}`);
|
|
1097
|
+
let lineCount = 0;
|
|
1098
|
+
for (const g of groups) {
|
|
1099
|
+
if (lineCount > 35) break;
|
|
1100
|
+
const isRecent = Date.now() - g.lastTs < 3e5;
|
|
1101
|
+
const icon = statusIcon(g.status, isRecent);
|
|
1102
|
+
const active = isRecent ? `${C.green}${timeStr(g.lastTs)}${C.reset}` : `${C.dim}${timeStr(g.lastTs)}${C.reset}`;
|
|
1103
|
+
if (g.children.length === 0) {
|
|
1104
|
+
const name = truncate(g.name, 26).padEnd(26);
|
|
1105
|
+
const st = statusText(g);
|
|
1106
|
+
const det = truncate(g.detail, 30);
|
|
1107
|
+
writeLine(L, ` ${icon} ${name} ${st.padEnd(20)} ${active.padEnd(20)} ${C.dim}${det}${C.reset}`);
|
|
1108
|
+
lineCount++;
|
|
1047
1109
|
} else {
|
|
1048
|
-
|
|
1049
|
-
|
|
1110
|
+
const name = truncate(g.name, 24).padEnd(24);
|
|
1111
|
+
const st = statusText(g);
|
|
1112
|
+
const tag = sourceTag(g.source);
|
|
1113
|
+
writeLine(L, ` ${icon} ${C.bold}${name}${C.reset} ${st.padEnd(20)} ${active.padEnd(20)} ${tag} ${C.dim}(${g.children.length} agents)${C.reset}`);
|
|
1114
|
+
lineCount++;
|
|
1115
|
+
const kids = g.children.slice(0, 12);
|
|
1116
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1117
|
+
if (lineCount > 35) break;
|
|
1118
|
+
const child = kids[i];
|
|
1119
|
+
const isLast = i === kids.length - 1;
|
|
1120
|
+
const connector = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
1121
|
+
const cIcon = statusIcon(child.status, Date.now() - child.lastActive < 3e5);
|
|
1122
|
+
const cName = truncate(child.id, 22).padEnd(22);
|
|
1123
|
+
const cActive = `${C.dim}${timeStr(child.lastActive)}${C.reset}`;
|
|
1124
|
+
const cDet = truncate(child.detail, 25);
|
|
1125
|
+
writeLine(L, ` ${C.dim}${connector}${C.reset} ${cIcon} ${cName} ${cActive.padEnd(20)} ${C.dim}${cDet}${C.reset}`);
|
|
1126
|
+
lineCount++;
|
|
1127
|
+
}
|
|
1128
|
+
if (g.children.length > 12) {
|
|
1129
|
+
writeLine(L, ` ${C.dim} ... +${g.children.length - 12} more${C.reset}`);
|
|
1130
|
+
lineCount++;
|
|
1131
|
+
}
|
|
1050
1132
|
}
|
|
1051
|
-
const name = ag.name.length > 23 ? ag.name.slice(0, 22) + "\u2026" : ag.name.padEnd(23);
|
|
1052
|
-
const src = sourceTag(ag.source).padEnd(16);
|
|
1053
|
-
const active = isRecent ? `${C.green}${lastTime}${C.reset}` : `${C.dim}${lastTime}${C.reset}`;
|
|
1054
|
-
const detail = ag.detail.length > 30 ? ag.detail.slice(0, 29) + "\u2026" : ag.detail;
|
|
1055
|
-
console.log(` ${statusIcon} ${name} ${src} ${statusText.padEnd(18)} ${active.padEnd(20)} ${C.dim}${detail}${C.reset}`);
|
|
1056
1133
|
}
|
|
1057
1134
|
if (distributedTraces.length > 0) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1135
|
+
writeLine(L, "");
|
|
1136
|
+
writeLine(L, ` ${C.bold}${C.under}Distributed Traces${C.reset}`);
|
|
1060
1137
|
for (const dt of distributedTraces.slice(0, 3)) {
|
|
1061
1138
|
const traceTime = new Date(dt.startTime).toLocaleTimeString();
|
|
1062
|
-
const
|
|
1139
|
+
const si = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
1063
1140
|
const dur = dt.endTime ? `${dt.endTime - dt.startTime}ms` : "running";
|
|
1064
1141
|
const tid = dt.traceId.slice(0, 8);
|
|
1065
|
-
|
|
1142
|
+
writeLine(L, ` ${si} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
|
|
1066
1143
|
const tree = getTraceTree(dt);
|
|
1067
1144
|
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
1068
|
-
const
|
|
1069
|
-
const depth = getDistDepth(dt,
|
|
1145
|
+
const tg = tree[i];
|
|
1146
|
+
const depth = getDistDepth(dt, tg.spanId);
|
|
1070
1147
|
const indent = " " + "\u2502 ".repeat(Math.max(0, depth - 1));
|
|
1071
1148
|
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
1072
1149
|
const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
1073
|
-
const gs =
|
|
1074
|
-
const gd =
|
|
1075
|
-
|
|
1150
|
+
const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
1151
|
+
const gd = tg.endTime ? `${tg.endTime - tg.startTime}ms` : "running";
|
|
1152
|
+
writeLine(L, `${indent}${conn}${gs} ${C.bold}${tg.agentId}${C.reset} ${C.dim}[${tg.trigger}] ${gd}${C.reset}`);
|
|
1076
1153
|
}
|
|
1077
1154
|
}
|
|
1078
1155
|
}
|
|
1079
|
-
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0,
|
|
1156
|
+
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
|
|
1080
1157
|
if (recentRecords.length > 0) {
|
|
1081
|
-
|
|
1082
|
-
|
|
1158
|
+
writeLine(L, "");
|
|
1159
|
+
writeLine(L, ` ${C.bold}${C.under}Recent Activity${C.reset}`);
|
|
1083
1160
|
for (const r of recentRecords) {
|
|
1084
1161
|
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}`;
|
|
1085
1162
|
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
1086
|
-
const agent = r.id
|
|
1163
|
+
const agent = truncate(r.id, 26).padEnd(26);
|
|
1087
1164
|
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
1088
1165
|
const ageStr = age < 60 ? age + "s ago" : age < 3600 ? Math.floor(age / 60) + "m ago" : Math.floor(age / 3600) + "h ago";
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1166
|
+
const det = truncate(r.detail, 25);
|
|
1167
|
+
writeLine(L, ` ${icon} ${agent} ${C.dim}${t} ${ageStr.padStart(8)}${C.reset} ${C.dim}${det}${C.reset}`);
|
|
1091
1168
|
}
|
|
1092
1169
|
}
|
|
1093
1170
|
if (files.length === 0) {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1171
|
+
writeLine(L, "");
|
|
1172
|
+
writeLine(L, ` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
|
|
1173
|
+
for (const d of config.dirs) writeLine(L, ` ${C.dim} ${d}${C.reset}`);
|
|
1097
1174
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1175
|
+
writeLine(L, "");
|
|
1176
|
+
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
1177
|
+
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1178
|
+
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1179
|
+
flushLines(L);
|
|
1101
1180
|
}
|
|
1102
1181
|
function getDistDepth(dt, spanId) {
|
|
1103
1182
|
if (!spanId) return 0;
|
|
@@ -1107,19 +1186,27 @@ function getDistDepth(dt, spanId) {
|
|
|
1107
1186
|
}
|
|
1108
1187
|
function startLive(argv) {
|
|
1109
1188
|
const config = parseArgs(argv);
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
console.error(
|
|
1189
|
+
const valid = config.dirs.filter((d) => (0, import_node_fs2.existsSync)(d));
|
|
1190
|
+
if (valid.length === 0) {
|
|
1191
|
+
console.error(`No valid directories found: ${config.dirs.join(", ")}`);
|
|
1192
|
+
console.error("Specify directories containing JSON/JSONL files: agentflow live <dir> [dir...]");
|
|
1113
1193
|
process.exit(1);
|
|
1114
1194
|
}
|
|
1195
|
+
const invalid = config.dirs.filter((d) => !(0, import_node_fs2.existsSync)(d));
|
|
1196
|
+
if (invalid.length > 0) {
|
|
1197
|
+
console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
|
|
1198
|
+
}
|
|
1199
|
+
config.dirs = valid;
|
|
1115
1200
|
render(config);
|
|
1116
1201
|
let debounce = null;
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1202
|
+
for (const dir of config.dirs) {
|
|
1203
|
+
try {
|
|
1204
|
+
(0, import_node_fs2.watch)(dir, { recursive: config.recursive }, () => {
|
|
1205
|
+
if (debounce) clearTimeout(debounce);
|
|
1206
|
+
debounce = setTimeout(() => render(config), 500);
|
|
1207
|
+
});
|
|
1208
|
+
} catch {
|
|
1209
|
+
}
|
|
1123
1210
|
}
|
|
1124
1211
|
setInterval(() => render(config), config.refreshMs);
|
|
1125
1212
|
process.on("SIGINT", () => {
|
package/dist/index.js
CHANGED