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.
@@ -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 agents = {};
910
+ const byFile = /* @__PURE__ */ new Map();
901
911
  for (const r of allRecords) {
902
- if (!agents[r.id]) {
903
- agents[r.id] = { name: r.id, total: 0, ok: 0, fail: 0, running: 0, lastTs: 0, source: r.source, detail: "" };
904
- }
905
- const ag = agents[r.id];
906
- ag.total++;
907
- if (r.status === "ok") ag.ok++;
908
- else if (r.status === "error") ag.fail++;
909
- else if (r.status === "running") ag.running++;
910
- if (r.lastActive > ag.lastTs) {
911
- ag.lastTs = r.lastActive;
912
- ag.detail = r.detail;
913
- ag.source = r.source;
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
- const agentList = Object.values(agents).sort((a, b) => b.lastTs - a.lastTs);
917
- const totExec = agentList.reduce((s, a) => s + a.total, 0);
918
- const totFail = agentList.reduce((s, a) => s + a.fail, 0);
919
- const totRunning = agentList.reduce((s, a) => s + a.running, 0);
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
- const sourceTag = (s) => {
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
- process.stdout.write("\x1B[2J\x1B[H");
971
- console.log(`${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
972
- console.log(`${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}`);
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
- console.log(`${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
976
- console.log(`${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
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
- console.log("");
979
- console.log(` ${C.bold}Agents${C.reset} ${sc}${agentList.length}${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}`);
980
- console.log("");
981
- console.log(` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
982
- console.log("");
983
- console.log(` ${C.bold}${C.under}Agent Type Status Last Active Detail${C.reset}`);
984
- for (const ag of agentList.slice(0, 30)) {
985
- const lastTime = ag.lastTs > 0 ? new Date(ag.lastTs).toLocaleTimeString() : "n/a";
986
- const isRecent = Date.now() - ag.lastTs < 3e5;
987
- let statusIcon;
988
- let statusText;
989
- if (ag.fail > 0 && ag.ok === 0 && ag.running === 0) {
990
- statusIcon = `${C.red}\u25CF${C.reset}`;
991
- statusText = `${C.red}error${C.reset}`;
992
- } else if (ag.running > 0) {
993
- statusIcon = `${C.green}\u25CF${C.reset}`;
994
- statusText = `${C.green}running${C.reset}`;
995
- } else if (ag.fail > 0) {
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
- statusIcon = `${C.dim}\u25CB${C.reset}`;
1003
- statusText = `${C.dim}idle${C.reset}`;
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
- console.log("");
1013
- console.log(` ${C.bold}${C.under}Distributed Traces${C.reset}`);
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 statusIcon = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
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
- console.log(` ${statusIcon} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
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 g = tree[i];
1023
- const depth = getDistDepth(dt, g.spanId);
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 = g.status === "completed" ? `${C.green}\u2713${C.reset}` : g.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
1028
- const gd = g.endTime ? `${g.endTime - g.startTime}ms` : "running";
1029
- console.log(`${indent}${conn}${gs} ${C.bold}${g.agentId}${C.reset} ${C.dim}[${g.trigger}] ${gd}${C.reset}`);
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, 8);
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
- console.log("");
1036
- console.log(` ${C.bold}${C.under}Recent Activity${C.reset}`);
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.length > 26 ? r.id.slice(0, 25) + "\u2026" : r.id.padEnd(26);
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 detail = r.detail.length > 25 ? r.detail.slice(0, 24) + "\u2026" : r.detail;
1044
- console.log(` ${icon} ${agent} ${C.dim}${t} ${ageStr.padStart(8)}${C.reset} ${C.dim}${detail}${C.reset}`);
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
- console.log("");
1049
- console.log(` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
1050
- for (const d of config.dirs) console.log(` ${C.dim} ${d}${C.reset}`);
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
- console.log("");
1129
+ writeLine(L, "");
1053
1130
  const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
1054
- console.log(` ${C.dim}Watching: ${dirLabel}${C.reset}`);
1055
- console.log(` ${C.dim}Press Ctrl+C to exit${C.reset}`);
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 agents = {};
850
+ const byFile = /* @__PURE__ */ new Map();
841
851
  for (const r of allRecords) {
842
- if (!agents[r.id]) {
843
- agents[r.id] = { name: r.id, total: 0, ok: 0, fail: 0, running: 0, lastTs: 0, source: r.source, detail: "" };
844
- }
845
- const ag = agents[r.id];
846
- ag.total++;
847
- if (r.status === "ok") ag.ok++;
848
- else if (r.status === "error") ag.fail++;
849
- else if (r.status === "running") ag.running++;
850
- if (r.lastActive > ag.lastTs) {
851
- ag.lastTs = r.lastActive;
852
- ag.detail = r.detail;
853
- ag.source = r.source;
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
- const agentList = Object.values(agents).sort((a, b) => b.lastTs - a.lastTs);
857
- const totExec = agentList.reduce((s, a) => s + a.total, 0);
858
- const totFail = agentList.reduce((s, a) => s + a.fail, 0);
859
- const totRunning = agentList.reduce((s, a) => s + a.running, 0);
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
- const sourceTag = (s) => {
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
- process.stdout.write("\x1B[2J\x1B[H");
911
- console.log(`${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
912
- console.log(`${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}`);
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
- console.log(`${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
916
- console.log(`${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
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
- console.log("");
919
- console.log(` ${C.bold}Agents${C.reset} ${sc}${agentList.length}${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}`);
920
- console.log("");
921
- console.log(` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
922
- console.log("");
923
- console.log(` ${C.bold}${C.under}Agent Type Status Last Active Detail${C.reset}`);
924
- for (const ag of agentList.slice(0, 30)) {
925
- const lastTime = ag.lastTs > 0 ? new Date(ag.lastTs).toLocaleTimeString() : "n/a";
926
- const isRecent = Date.now() - ag.lastTs < 3e5;
927
- let statusIcon;
928
- let statusText;
929
- if (ag.fail > 0 && ag.ok === 0 && ag.running === 0) {
930
- statusIcon = `${C.red}\u25CF${C.reset}`;
931
- statusText = `${C.red}error${C.reset}`;
932
- } else if (ag.running > 0) {
933
- statusIcon = `${C.green}\u25CF${C.reset}`;
934
- statusText = `${C.green}running${C.reset}`;
935
- } else if (ag.fail > 0) {
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
- statusIcon = `${C.dim}\u25CB${C.reset}`;
943
- statusText = `${C.dim}idle${C.reset}`;
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
- console.log("");
953
- console.log(` ${C.bold}${C.under}Distributed Traces${C.reset}`);
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 statusIcon = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
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
- console.log(` ${statusIcon} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
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 g = tree[i];
963
- const depth = getDistDepth(dt, g.spanId);
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 = g.status === "completed" ? `${C.green}\u2713${C.reset}` : g.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
968
- const gd = g.endTime ? `${g.endTime - g.startTime}ms` : "running";
969
- console.log(`${indent}${conn}${gs} ${C.bold}${g.agentId}${C.reset} ${C.dim}[${g.trigger}] ${gd}${C.reset}`);
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, 8);
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
- console.log("");
976
- console.log(` ${C.bold}${C.under}Recent Activity${C.reset}`);
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.length > 26 ? r.id.slice(0, 25) + "\u2026" : r.id.padEnd(26);
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 detail = r.detail.length > 25 ? r.detail.slice(0, 24) + "\u2026" : r.detail;
984
- console.log(` ${icon} ${agent} ${C.dim}${t} ${ageStr.padStart(8)}${C.reset} ${C.dim}${detail}${C.reset}`);
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
- console.log("");
989
- console.log(` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
990
- for (const d of config.dirs) console.log(` ${C.dim} ${d}${C.reset}`);
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
- console.log("");
1069
+ writeLine(L, "");
993
1070
  const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
994
- console.log(` ${C.dim}Watching: ${dirLabel}${C.reset}`);
995
- console.log(` ${C.dim}Press Ctrl+C to exit${C.reset}`);
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
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  runTraced,
4
4
  startLive
5
- } from "./chunk-WOJEID7V.js";
5
+ } from "./chunk-5SPZPOFN.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { basename } from "path";
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 agents = {};
956
+ const byFile = /* @__PURE__ */ new Map();
947
957
  for (const r of allRecords) {
948
- if (!agents[r.id]) {
949
- agents[r.id] = { name: r.id, total: 0, ok: 0, fail: 0, running: 0, lastTs: 0, source: r.source, detail: "" };
950
- }
951
- const ag = agents[r.id];
952
- ag.total++;
953
- if (r.status === "ok") ag.ok++;
954
- else if (r.status === "error") ag.fail++;
955
- else if (r.status === "running") ag.running++;
956
- if (r.lastActive > ag.lastTs) {
957
- ag.lastTs = r.lastActive;
958
- ag.detail = r.detail;
959
- ag.source = r.source;
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
- const agentList = Object.values(agents).sort((a, b) => b.lastTs - a.lastTs);
963
- const totExec = agentList.reduce((s, a) => s + a.total, 0);
964
- const totFail = agentList.reduce((s, a) => s + a.fail, 0);
965
- const totRunning = agentList.reduce((s, a) => s + a.running, 0);
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
- const sourceTag = (s) => {
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
- process.stdout.write("\x1B[2J\x1B[H");
1017
- console.log(`${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
1018
- console.log(`${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}`);
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
- console.log(`${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
1022
- console.log(`${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
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
- console.log("");
1025
- console.log(` ${C.bold}Agents${C.reset} ${sc}${agentList.length}${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}`);
1026
- console.log("");
1027
- console.log(` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
1028
- console.log("");
1029
- console.log(` ${C.bold}${C.under}Agent Type Status Last Active Detail${C.reset}`);
1030
- for (const ag of agentList.slice(0, 30)) {
1031
- const lastTime = ag.lastTs > 0 ? new Date(ag.lastTs).toLocaleTimeString() : "n/a";
1032
- const isRecent = Date.now() - ag.lastTs < 3e5;
1033
- let statusIcon;
1034
- let statusText;
1035
- if (ag.fail > 0 && ag.ok === 0 && ag.running === 0) {
1036
- statusIcon = `${C.red}\u25CF${C.reset}`;
1037
- statusText = `${C.red}error${C.reset}`;
1038
- } else if (ag.running > 0) {
1039
- statusIcon = `${C.green}\u25CF${C.reset}`;
1040
- statusText = `${C.green}running${C.reset}`;
1041
- } else if (ag.fail > 0) {
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
- statusIcon = `${C.dim}\u25CB${C.reset}`;
1049
- statusText = `${C.dim}idle${C.reset}`;
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
- console.log("");
1059
- console.log(` ${C.bold}${C.under}Distributed Traces${C.reset}`);
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 statusIcon = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
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
- console.log(` ${statusIcon} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
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 g = tree[i];
1069
- const depth = getDistDepth(dt, g.spanId);
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 = g.status === "completed" ? `${C.green}\u2713${C.reset}` : g.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
1074
- const gd = g.endTime ? `${g.endTime - g.startTime}ms` : "running";
1075
- console.log(`${indent}${conn}${gs} ${C.bold}${g.agentId}${C.reset} ${C.dim}[${g.trigger}] ${gd}${C.reset}`);
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, 8);
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
- console.log("");
1082
- console.log(` ${C.bold}${C.under}Recent Activity${C.reset}`);
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.length > 26 ? r.id.slice(0, 25) + "\u2026" : r.id.padEnd(26);
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 detail = r.detail.length > 25 ? r.detail.slice(0, 24) + "\u2026" : r.detail;
1090
- console.log(` ${icon} ${agent} ${C.dim}${t} ${ageStr.padStart(8)}${C.reset} ${C.dim}${detail}${C.reset}`);
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
- console.log("");
1095
- console.log(` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
1096
- for (const d of config.dirs) console.log(` ${C.dim} ${d}${C.reset}`);
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
- console.log("");
1175
+ writeLine(L, "");
1099
1176
  const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
1100
- console.log(` ${C.dim}Watching: ${dirLabel}${C.reset}`);
1101
- console.log(` ${C.dim}Press Ctrl+C to exit${C.reset}`);
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
@@ -18,7 +18,7 @@ import {
18
18
  runTraced,
19
19
  startLive,
20
20
  stitchTrace
21
- } from "./chunk-WOJEID7V.js";
21
+ } from "./chunk-5SPZPOFN.js";
22
22
  export {
23
23
  createGraphBuilder,
24
24
  findWaitingOn,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentflow-core",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Universal execution tracing for AI agent systems",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",