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/cli.cjs CHANGED
@@ -587,7 +587,7 @@ var C = {
587
587
  white: "\x1B[37m"
588
588
  };
589
589
  function parseArgs(argv) {
590
- const config = { tracesDir: ".", refreshMs: 3e3, recursive: false };
590
+ const config = { dirs: [], refreshMs: 3e3, recursive: false };
591
591
  const args = argv.slice(0);
592
592
  if (args[0] === "live") args.shift();
593
593
  let i = 0;
@@ -604,18 +604,14 @@ function parseArgs(argv) {
604
604
  } else if (arg === "--recursive" || arg === "-R") {
605
605
  config.recursive = true;
606
606
  i++;
607
- } else if (arg === "--traces-dir" || arg === "-t") {
608
- i++;
609
- config.tracesDir = args[i] ?? config.tracesDir;
610
- i++;
611
607
  } else if (!arg.startsWith("-")) {
612
- config.tracesDir = arg;
608
+ config.dirs.push((0, import_node_path2.resolve)(arg));
613
609
  i++;
614
610
  } else {
615
611
  i++;
616
612
  }
617
613
  }
618
- config.tracesDir = (0, import_node_path2.resolve)(config.tracesDir);
614
+ if (config.dirs.length === 0) config.dirs.push((0, import_node_path2.resolve)("."));
619
615
  return config;
620
616
  }
621
617
  function printUsage() {
@@ -623,13 +619,13 @@ function printUsage() {
623
619
  AgentFlow Live Monitor \u2014 real-time terminal dashboard for agent systems.
624
620
 
625
621
  Auto-detects agent traces, state files, job schedulers, and session logs
626
- from any JSON/JSONL files in the watched directory.
622
+ from any JSON/JSONL files in the watched directories.
627
623
 
628
624
  Usage:
629
- agentflow live [directory] [options]
625
+ agentflow live [dir...] [options]
630
626
 
631
627
  Arguments:
632
- directory Directory to watch (default: current directory)
628
+ dir One or more directories to watch (default: .)
633
629
 
634
630
  Options:
635
631
  -r, --refresh <secs> Refresh interval in seconds (default: 3)
@@ -638,38 +634,42 @@ Options:
638
634
 
639
635
  Examples:
640
636
  agentflow live ./data
641
- agentflow live ./traces --refresh 5
637
+ agentflow live ./traces ./cron ./workers --refresh 5
642
638
  agentflow live /var/lib/myagent -R
643
639
  `.trim());
644
640
  }
645
- function scanFiles(dir, recursive) {
641
+ function scanFiles(dirs, recursive) {
646
642
  const results = [];
647
- function scanDir(d) {
643
+ const seen = /* @__PURE__ */ new Set();
644
+ function scanDir(d, topLevel) {
648
645
  try {
649
646
  for (const f of (0, import_node_fs2.readdirSync)(d)) {
650
647
  if (f.startsWith(".")) continue;
651
648
  const fp = (0, import_node_path2.join)(d, f);
649
+ if (seen.has(fp)) continue;
652
650
  let stat;
653
651
  try {
654
652
  stat = (0, import_node_fs2.statSync)(fp);
655
653
  } catch {
656
654
  continue;
657
655
  }
658
- if (stat.isDirectory() && recursive && d === dir) {
659
- scanDir(fp);
656
+ if (stat.isDirectory() && recursive && topLevel) {
657
+ scanDir(fp, false);
660
658
  continue;
661
659
  }
662
660
  if (!stat.isFile()) continue;
663
661
  if (f.endsWith(".json")) {
662
+ seen.add(fp);
664
663
  results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
665
664
  } else if (f.endsWith(".jsonl")) {
665
+ seen.add(fp);
666
666
  results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
667
667
  }
668
668
  }
669
669
  } catch {
670
670
  }
671
671
  }
672
- scanDir(dir);
672
+ for (const dir of dirs) scanDir(dir, true);
673
673
  results.sort((a, b) => b.mtime - a.mtime);
674
674
  return results;
675
675
  }
@@ -819,11 +819,21 @@ 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
- const files = scanFiles(config.tracesDir, config.recursive);
836
+ const files = scanFiles(config.dirs, config.recursive);
827
837
  if (files.length > prevFileCount && prevFileCount > 0) {
828
838
  newExecCount += files.length - prevFileCount;
829
839
  }
@@ -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,92 +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
- console.log(` ${C.dim}${config.tracesDir}${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("");
993
- console.log(` ${C.dim}Watching: ${config.tracesDir}${C.reset}`);
994
- console.log(` ${C.dim}Press Ctrl+C to exit${C.reset}`);
1069
+ writeLine(L, "");
1070
+ const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
1071
+ writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
1072
+ writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
1073
+ flushLines(L);
995
1074
  }
996
1075
  function getDistDepth(dt, spanId) {
997
1076
  if (!spanId) return 0;
@@ -1001,19 +1080,27 @@ function getDistDepth(dt, spanId) {
1001
1080
  }
1002
1081
  function startLive(argv) {
1003
1082
  const config = parseArgs(argv);
1004
- if (!(0, import_node_fs2.existsSync)(config.tracesDir)) {
1005
- console.error(`Directory does not exist: ${config.tracesDir}`);
1006
- console.error("Specify a directory containing JSON/JSONL files: agentflow live <dir>");
1083
+ const valid = config.dirs.filter((d) => (0, import_node_fs2.existsSync)(d));
1084
+ if (valid.length === 0) {
1085
+ console.error(`No valid directories found: ${config.dirs.join(", ")}`);
1086
+ console.error("Specify directories containing JSON/JSONL files: agentflow live <dir> [dir...]");
1007
1087
  process.exit(1);
1008
1088
  }
1089
+ const invalid = config.dirs.filter((d) => !(0, import_node_fs2.existsSync)(d));
1090
+ if (invalid.length > 0) {
1091
+ console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
1092
+ }
1093
+ config.dirs = valid;
1009
1094
  render(config);
1010
1095
  let debounce = null;
1011
- try {
1012
- (0, import_node_fs2.watch)(config.tracesDir, { recursive: config.recursive }, () => {
1013
- if (debounce) clearTimeout(debounce);
1014
- debounce = setTimeout(() => render(config), 500);
1015
- });
1016
- } catch {
1096
+ for (const dir of config.dirs) {
1097
+ try {
1098
+ (0, import_node_fs2.watch)(dir, { recursive: config.recursive }, () => {
1099
+ if (debounce) clearTimeout(debounce);
1100
+ debounce = setTimeout(() => render(config), 500);
1101
+ });
1102
+ } catch {
1103
+ }
1017
1104
  }
1018
1105
  setInterval(() => render(config), config.refreshMs);
1019
1106
  process.on("SIGINT", () => {
@@ -1031,15 +1118,16 @@ Usage:
1031
1118
  agentflow <command> [options]
1032
1119
 
1033
1120
  Commands:
1034
- run [options] -- <cmd> Wrap a command with automatic execution tracing
1035
- live [dir] [options] Real-time terminal monitor (auto-detects any JSON/JSONL)
1121
+ run [options] -- <cmd> Wrap a command with automatic execution tracing
1122
+ live [dir...] [options] Real-time terminal monitor (auto-detects any JSON/JSONL)
1036
1123
 
1037
1124
  Run \`agentflow <command> --help\` for command-specific options.
1038
1125
 
1039
1126
  Examples:
1040
1127
  agentflow run --traces-dir ./traces -- python -m myagent process
1041
1128
  agentflow live ./data
1042
- agentflow live ./data -R --refresh 5
1129
+ agentflow live ./traces ./cron ./workers -R
1130
+ agentflow live ./data --refresh 5
1043
1131
  `.trim());
1044
1132
  }
1045
1133
  function parseRunArgs(argv) {
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  runTraced,
4
4
  startLive
5
- } from "./chunk-FJVQYJFB.js";
5
+ } from "./chunk-5SPZPOFN.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { basename } from "path";
@@ -14,15 +14,16 @@ Usage:
14
14
  agentflow <command> [options]
15
15
 
16
16
  Commands:
17
- run [options] -- <cmd> Wrap a command with automatic execution tracing
18
- live [dir] [options] Real-time terminal monitor (auto-detects any JSON/JSONL)
17
+ run [options] -- <cmd> Wrap a command with automatic execution tracing
18
+ live [dir...] [options] Real-time terminal monitor (auto-detects any JSON/JSONL)
19
19
 
20
20
  Run \`agentflow <command> --help\` for command-specific options.
21
21
 
22
22
  Examples:
23
23
  agentflow run --traces-dir ./traces -- python -m myagent process
24
24
  agentflow live ./data
25
- agentflow live ./data -R --refresh 5
25
+ agentflow live ./traces ./cron ./workers -R
26
+ agentflow live ./data --refresh 5
26
27
  `.trim());
27
28
  }
28
29
  function parseRunArgs(argv) {