agentflow-core 0.2.2 → 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-WOJEID7V.js → chunk-5SPZPOFN.js} +153 -75
- package/dist/cli.cjs +153 -75
- package/dist/cli.js +1 -1
- package/dist/index.cjs +153 -75
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -879,9 +879,19 @@ 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
896
|
const files = scanFiles(config.dirs, config.recursive);
|
|
887
897
|
if (files.length > prevFileCount && prevFileCount > 0) {
|
|
@@ -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,93 +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
|
-
for (const d of config.dirs)
|
|
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
|
-
|
|
1129
|
+
writeLine(L, "");
|
|
1053
1130
|
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
1054
|
-
|
|
1055
|
-
|
|
1131
|
+
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1132
|
+
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1133
|
+
flushLines(L);
|
|
1056
1134
|
}
|
|
1057
1135
|
function getDistDepth(dt, spanId) {
|
|
1058
1136
|
if (!spanId) return 0;
|
package/dist/cli.cjs
CHANGED
|
@@ -819,9 +819,19 @@ function processJsonlFile(file) {
|
|
|
819
819
|
return [];
|
|
820
820
|
}
|
|
821
821
|
}
|
|
822
|
+
var K = "\x1B[K";
|
|
823
|
+
function writeLine(lines, text) {
|
|
824
|
+
lines.push(text + K);
|
|
825
|
+
}
|
|
826
|
+
function flushLines(lines) {
|
|
827
|
+
process.stdout.write("\x1B[H");
|
|
828
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
829
|
+
process.stdout.write("\x1B[J");
|
|
830
|
+
}
|
|
822
831
|
var prevFileCount = 0;
|
|
823
832
|
var newExecCount = 0;
|
|
824
833
|
var sessionStart = Date.now();
|
|
834
|
+
var firstRender = true;
|
|
825
835
|
function render(config) {
|
|
826
836
|
const files = scanFiles(config.dirs, config.recursive);
|
|
827
837
|
if (files.length > prevFileCount && prevFileCount > 0) {
|
|
@@ -837,26 +847,58 @@ function render(config) {
|
|
|
837
847
|
if (r.traceData) allTraces.push(r.traceData);
|
|
838
848
|
}
|
|
839
849
|
}
|
|
840
|
-
const
|
|
850
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
841
851
|
for (const r of allRecords) {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
852
|
+
const arr = byFile.get(r.file) ?? [];
|
|
853
|
+
arr.push(r);
|
|
854
|
+
byFile.set(r.file, arr);
|
|
855
|
+
}
|
|
856
|
+
const groups = [];
|
|
857
|
+
for (const [file, records] of byFile) {
|
|
858
|
+
if (records.length === 1) {
|
|
859
|
+
const r = records[0];
|
|
860
|
+
groups.push({
|
|
861
|
+
name: r.id,
|
|
862
|
+
source: r.source,
|
|
863
|
+
status: r.status,
|
|
864
|
+
lastTs: r.lastActive,
|
|
865
|
+
detail: r.detail,
|
|
866
|
+
children: [],
|
|
867
|
+
ok: r.status === "ok" ? 1 : 0,
|
|
868
|
+
fail: r.status === "error" ? 1 : 0,
|
|
869
|
+
running: r.status === "running" ? 1 : 0,
|
|
870
|
+
total: 1
|
|
871
|
+
});
|
|
872
|
+
} else {
|
|
873
|
+
const groupName = nameFromFile(file);
|
|
874
|
+
let lastTs = 0;
|
|
875
|
+
let ok = 0, fail = 0, running = 0;
|
|
876
|
+
for (const r of records) {
|
|
877
|
+
if (r.lastActive > lastTs) lastTs = r.lastActive;
|
|
878
|
+
if (r.status === "ok") ok++;
|
|
879
|
+
else if (r.status === "error") fail++;
|
|
880
|
+
else if (r.status === "running") running++;
|
|
881
|
+
}
|
|
882
|
+
const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
|
|
883
|
+
groups.push({
|
|
884
|
+
name: groupName,
|
|
885
|
+
source: records[0].source,
|
|
886
|
+
status,
|
|
887
|
+
lastTs,
|
|
888
|
+
detail: `${records.length} agents`,
|
|
889
|
+
children: records.sort((a, b) => b.lastActive - a.lastActive),
|
|
890
|
+
ok,
|
|
891
|
+
fail,
|
|
892
|
+
running,
|
|
893
|
+
total: records.length
|
|
894
|
+
});
|
|
854
895
|
}
|
|
855
896
|
}
|
|
856
|
-
|
|
857
|
-
const totExec =
|
|
858
|
-
const totFail =
|
|
859
|
-
const totRunning =
|
|
897
|
+
groups.sort((a, b) => b.lastTs - a.lastTs);
|
|
898
|
+
const totExec = allRecords.length;
|
|
899
|
+
const totFail = allRecords.filter((r) => r.status === "error").length;
|
|
900
|
+
const totRunning = allRecords.filter((r) => r.status === "running").length;
|
|
901
|
+
const uniqueAgents = new Set(allRecords.map((r) => r.id)).size;
|
|
860
902
|
const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
|
|
861
903
|
const now = Date.now();
|
|
862
904
|
const buckets = new Array(12).fill(0);
|
|
@@ -893,7 +935,21 @@ function render(config) {
|
|
|
893
935
|
const upMin = Math.floor(upSec / 60);
|
|
894
936
|
const upStr = upMin > 0 ? `${upMin}m ${upSec % 60}s` : `${upSec}s`;
|
|
895
937
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
896
|
-
|
|
938
|
+
function statusIcon(s, recent) {
|
|
939
|
+
if (s === "error") return `${C.red}\u25CF${C.reset}`;
|
|
940
|
+
if (s === "running") return `${C.green}\u25CF${C.reset}`;
|
|
941
|
+
if (s === "ok" && recent) return `${C.green}\u25CF${C.reset}`;
|
|
942
|
+
if (s === "ok") return `${C.dim}\u25CB${C.reset}`;
|
|
943
|
+
return `${C.dim}\u25CB${C.reset}`;
|
|
944
|
+
}
|
|
945
|
+
function statusText(g) {
|
|
946
|
+
if (g.fail > 0 && g.ok === 0 && g.running === 0) return `${C.red}error${C.reset}`;
|
|
947
|
+
if (g.running > 0) return `${C.green}running${C.reset}`;
|
|
948
|
+
if (g.fail > 0) return `${C.yellow}${g.ok}ok/${g.fail}err${C.reset}`;
|
|
949
|
+
if (g.ok > 0) return g.total > 1 ? `${C.green}${g.ok}/${g.total} ok${C.reset}` : `${C.green}ok${C.reset}`;
|
|
950
|
+
return `${C.dim}idle${C.reset}`;
|
|
951
|
+
}
|
|
952
|
+
function sourceTag(s) {
|
|
897
953
|
switch (s) {
|
|
898
954
|
case "trace":
|
|
899
955
|
return `${C.cyan}trace${C.reset}`;
|
|
@@ -906,93 +962,115 @@ function render(config) {
|
|
|
906
962
|
case "state":
|
|
907
963
|
return `${C.dim}state${C.reset}`;
|
|
908
964
|
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
965
|
+
}
|
|
966
|
+
function timeStr(ts) {
|
|
967
|
+
if (ts <= 0) return "n/a";
|
|
968
|
+
return new Date(ts).toLocaleTimeString();
|
|
969
|
+
}
|
|
970
|
+
function truncate(s, max) {
|
|
971
|
+
return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
972
|
+
}
|
|
973
|
+
if (firstRender) {
|
|
974
|
+
process.stdout.write("\x1B[2J");
|
|
975
|
+
firstRender = false;
|
|
976
|
+
}
|
|
977
|
+
const L = [];
|
|
978
|
+
writeLine(L, `${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
|
|
979
|
+
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}`);
|
|
913
980
|
const metaLine = `Refresh: ${config.refreshMs / 1e3}s \xB7 Up: ${upStr} \xB7 Files: ${files.length}`;
|
|
914
981
|
const pad1 = Math.max(0, 64 - metaLine.length);
|
|
915
|
-
|
|
916
|
-
|
|
982
|
+
writeLine(L, `${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
|
|
983
|
+
writeLine(L, `${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
|
|
917
984
|
const sc = totFail === 0 ? C.green : C.yellow;
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
statusIcon = `${C.yellow}\u25CF${C.reset}`;
|
|
937
|
-
statusText = `${C.yellow}${ag.ok}ok/${ag.fail}err${C.reset}`;
|
|
938
|
-
} else if (ag.ok > 0) {
|
|
939
|
-
statusIcon = isRecent ? `${C.green}\u25CF${C.reset}` : `${C.dim}\u25CB${C.reset}`;
|
|
940
|
-
statusText = ag.total > 1 ? `${C.green}${ag.ok}/${ag.total}${C.reset}` : `${C.green}ok${C.reset}`;
|
|
985
|
+
writeLine(L, "");
|
|
986
|
+
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}`);
|
|
987
|
+
writeLine(L, "");
|
|
988
|
+
writeLine(L, ` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
|
|
989
|
+
writeLine(L, "");
|
|
990
|
+
writeLine(L, ` ${C.bold}${C.under}Agent Status Last Active Detail${C.reset}`);
|
|
991
|
+
let lineCount = 0;
|
|
992
|
+
for (const g of groups) {
|
|
993
|
+
if (lineCount > 35) break;
|
|
994
|
+
const isRecent = Date.now() - g.lastTs < 3e5;
|
|
995
|
+
const icon = statusIcon(g.status, isRecent);
|
|
996
|
+
const active = isRecent ? `${C.green}${timeStr(g.lastTs)}${C.reset}` : `${C.dim}${timeStr(g.lastTs)}${C.reset}`;
|
|
997
|
+
if (g.children.length === 0) {
|
|
998
|
+
const name = truncate(g.name, 26).padEnd(26);
|
|
999
|
+
const st = statusText(g);
|
|
1000
|
+
const det = truncate(g.detail, 30);
|
|
1001
|
+
writeLine(L, ` ${icon} ${name} ${st.padEnd(20)} ${active.padEnd(20)} ${C.dim}${det}${C.reset}`);
|
|
1002
|
+
lineCount++;
|
|
941
1003
|
} else {
|
|
942
|
-
|
|
943
|
-
|
|
1004
|
+
const name = truncate(g.name, 24).padEnd(24);
|
|
1005
|
+
const st = statusText(g);
|
|
1006
|
+
const tag = sourceTag(g.source);
|
|
1007
|
+
writeLine(L, ` ${icon} ${C.bold}${name}${C.reset} ${st.padEnd(20)} ${active.padEnd(20)} ${tag} ${C.dim}(${g.children.length} agents)${C.reset}`);
|
|
1008
|
+
lineCount++;
|
|
1009
|
+
const kids = g.children.slice(0, 12);
|
|
1010
|
+
for (let i = 0; i < kids.length; i++) {
|
|
1011
|
+
if (lineCount > 35) break;
|
|
1012
|
+
const child = kids[i];
|
|
1013
|
+
const isLast = i === kids.length - 1;
|
|
1014
|
+
const connector = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
1015
|
+
const cIcon = statusIcon(child.status, Date.now() - child.lastActive < 3e5);
|
|
1016
|
+
const cName = truncate(child.id, 22).padEnd(22);
|
|
1017
|
+
const cActive = `${C.dim}${timeStr(child.lastActive)}${C.reset}`;
|
|
1018
|
+
const cDet = truncate(child.detail, 25);
|
|
1019
|
+
writeLine(L, ` ${C.dim}${connector}${C.reset} ${cIcon} ${cName} ${cActive.padEnd(20)} ${C.dim}${cDet}${C.reset}`);
|
|
1020
|
+
lineCount++;
|
|
1021
|
+
}
|
|
1022
|
+
if (g.children.length > 12) {
|
|
1023
|
+
writeLine(L, ` ${C.dim} ... +${g.children.length - 12} more${C.reset}`);
|
|
1024
|
+
lineCount++;
|
|
1025
|
+
}
|
|
944
1026
|
}
|
|
945
|
-
const name = ag.name.length > 23 ? ag.name.slice(0, 22) + "\u2026" : ag.name.padEnd(23);
|
|
946
|
-
const src = sourceTag(ag.source).padEnd(16);
|
|
947
|
-
const active = isRecent ? `${C.green}${lastTime}${C.reset}` : `${C.dim}${lastTime}${C.reset}`;
|
|
948
|
-
const detail = ag.detail.length > 30 ? ag.detail.slice(0, 29) + "\u2026" : ag.detail;
|
|
949
|
-
console.log(` ${statusIcon} ${name} ${src} ${statusText.padEnd(18)} ${active.padEnd(20)} ${C.dim}${detail}${C.reset}`);
|
|
950
1027
|
}
|
|
951
1028
|
if (distributedTraces.length > 0) {
|
|
952
|
-
|
|
953
|
-
|
|
1029
|
+
writeLine(L, "");
|
|
1030
|
+
writeLine(L, ` ${C.bold}${C.under}Distributed Traces${C.reset}`);
|
|
954
1031
|
for (const dt of distributedTraces.slice(0, 3)) {
|
|
955
1032
|
const traceTime = new Date(dt.startTime).toLocaleTimeString();
|
|
956
|
-
const
|
|
1033
|
+
const si = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
957
1034
|
const dur = dt.endTime ? `${dt.endTime - dt.startTime}ms` : "running";
|
|
958
1035
|
const tid = dt.traceId.slice(0, 8);
|
|
959
|
-
|
|
1036
|
+
writeLine(L, ` ${si} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
|
|
960
1037
|
const tree = getTraceTree(dt);
|
|
961
1038
|
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
962
|
-
const
|
|
963
|
-
const depth = getDistDepth(dt,
|
|
1039
|
+
const tg = tree[i];
|
|
1040
|
+
const depth = getDistDepth(dt, tg.spanId);
|
|
964
1041
|
const indent = " " + "\u2502 ".repeat(Math.max(0, depth - 1));
|
|
965
1042
|
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
966
1043
|
const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
967
|
-
const gs =
|
|
968
|
-
const gd =
|
|
969
|
-
|
|
1044
|
+
const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
1045
|
+
const gd = tg.endTime ? `${tg.endTime - tg.startTime}ms` : "running";
|
|
1046
|
+
writeLine(L, `${indent}${conn}${gs} ${C.bold}${tg.agentId}${C.reset} ${C.dim}[${tg.trigger}] ${gd}${C.reset}`);
|
|
970
1047
|
}
|
|
971
1048
|
}
|
|
972
1049
|
}
|
|
973
|
-
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0,
|
|
1050
|
+
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
|
|
974
1051
|
if (recentRecords.length > 0) {
|
|
975
|
-
|
|
976
|
-
|
|
1052
|
+
writeLine(L, "");
|
|
1053
|
+
writeLine(L, ` ${C.bold}${C.under}Recent Activity${C.reset}`);
|
|
977
1054
|
for (const r of recentRecords) {
|
|
978
1055
|
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}`;
|
|
979
1056
|
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
980
|
-
const agent = r.id
|
|
1057
|
+
const agent = truncate(r.id, 26).padEnd(26);
|
|
981
1058
|
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
982
1059
|
const ageStr = age < 60 ? age + "s ago" : age < 3600 ? Math.floor(age / 60) + "m ago" : Math.floor(age / 3600) + "h ago";
|
|
983
|
-
const
|
|
984
|
-
|
|
1060
|
+
const det = truncate(r.detail, 25);
|
|
1061
|
+
writeLine(L, ` ${icon} ${agent} ${C.dim}${t} ${ageStr.padStart(8)}${C.reset} ${C.dim}${det}${C.reset}`);
|
|
985
1062
|
}
|
|
986
1063
|
}
|
|
987
1064
|
if (files.length === 0) {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
for (const d of config.dirs)
|
|
1065
|
+
writeLine(L, "");
|
|
1066
|
+
writeLine(L, ` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
|
|
1067
|
+
for (const d of config.dirs) writeLine(L, ` ${C.dim} ${d}${C.reset}`);
|
|
991
1068
|
}
|
|
992
|
-
|
|
1069
|
+
writeLine(L, "");
|
|
993
1070
|
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
994
|
-
|
|
995
|
-
|
|
1071
|
+
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1072
|
+
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1073
|
+
flushLines(L);
|
|
996
1074
|
}
|
|
997
1075
|
function getDistDepth(dt, spanId) {
|
|
998
1076
|
if (!spanId) return 0;
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -925,9 +925,19 @@ 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
942
|
const files = scanFiles(config.dirs, config.recursive);
|
|
933
943
|
if (files.length > prevFileCount && prevFileCount > 0) {
|
|
@@ -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,93 +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
|
-
for (const d of config.dirs)
|
|
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
|
-
|
|
1175
|
+
writeLine(L, "");
|
|
1099
1176
|
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
1100
|
-
|
|
1101
|
-
|
|
1177
|
+
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1178
|
+
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1179
|
+
flushLines(L);
|
|
1102
1180
|
}
|
|
1103
1181
|
function getDistDepth(dt, spanId) {
|
|
1104
1182
|
if (!spanId) return 0;
|
package/dist/index.js
CHANGED