agentflow-core 0.2.0 → 0.2.1
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-TRKBUPIN.js → chunk-FJVQYJFB.js} +295 -123
- package/dist/cli.cjs +297 -125
- package/dist/cli.js +5 -5
- package/dist/index.cjs +294 -122
- package/dist/index.d.cts +10 -11
- package/dist/index.d.ts +10 -11
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -587,10 +587,7 @@ var C = {
|
|
|
587
587
|
white: "\x1B[37m"
|
|
588
588
|
};
|
|
589
589
|
function parseArgs(argv) {
|
|
590
|
-
const config = {
|
|
591
|
-
tracesDir: "./traces",
|
|
592
|
-
refreshMs: 3e3
|
|
593
|
-
};
|
|
590
|
+
const config = { tracesDir: ".", refreshMs: 3e3, recursive: false };
|
|
594
591
|
const args = argv.slice(0);
|
|
595
592
|
if (args[0] === "live") args.shift();
|
|
596
593
|
let i = 0;
|
|
@@ -601,8 +598,11 @@ function parseArgs(argv) {
|
|
|
601
598
|
process.exit(0);
|
|
602
599
|
} else if (arg === "--refresh" || arg === "-r") {
|
|
603
600
|
i++;
|
|
604
|
-
const
|
|
605
|
-
if (!isNaN(
|
|
601
|
+
const v = parseInt(args[i] ?? "", 10);
|
|
602
|
+
if (!isNaN(v) && v > 0) config.refreshMs = v * 1e3;
|
|
603
|
+
i++;
|
|
604
|
+
} else if (arg === "--recursive" || arg === "-R") {
|
|
605
|
+
config.recursive = true;
|
|
606
606
|
i++;
|
|
607
607
|
} else if (arg === "--traces-dir" || arg === "-t") {
|
|
608
608
|
i++;
|
|
@@ -622,117 +622,252 @@ function printUsage() {
|
|
|
622
622
|
console.log(`
|
|
623
623
|
AgentFlow Live Monitor \u2014 real-time terminal dashboard for agent systems.
|
|
624
624
|
|
|
625
|
+
Auto-detects agent traces, state files, job schedulers, and session logs
|
|
626
|
+
from any JSON/JSONL files in the watched directory.
|
|
627
|
+
|
|
625
628
|
Usage:
|
|
626
|
-
agentflow live [
|
|
629
|
+
agentflow live [directory] [options]
|
|
627
630
|
|
|
628
631
|
Arguments:
|
|
629
|
-
|
|
632
|
+
directory Directory to watch (default: current directory)
|
|
630
633
|
|
|
631
634
|
Options:
|
|
632
635
|
-r, --refresh <secs> Refresh interval in seconds (default: 3)
|
|
633
|
-
-
|
|
636
|
+
-R, --recursive Scan subdirectories (1 level deep)
|
|
634
637
|
-h, --help Show this help message
|
|
635
638
|
|
|
636
639
|
Examples:
|
|
637
|
-
agentflow live
|
|
638
|
-
agentflow live ./
|
|
639
|
-
agentflow live /var/
|
|
640
|
+
agentflow live ./data
|
|
641
|
+
agentflow live ./traces --refresh 5
|
|
642
|
+
agentflow live /var/lib/myagent -R
|
|
640
643
|
`.trim());
|
|
641
644
|
}
|
|
642
|
-
function
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
645
|
+
function scanFiles(dir, recursive) {
|
|
646
|
+
const results = [];
|
|
647
|
+
function scanDir(d) {
|
|
648
|
+
try {
|
|
649
|
+
for (const f of (0, import_node_fs2.readdirSync)(d)) {
|
|
650
|
+
if (f.startsWith(".")) continue;
|
|
651
|
+
const fp = (0, import_node_path2.join)(d, f);
|
|
652
|
+
let stat;
|
|
653
|
+
try {
|
|
654
|
+
stat = (0, import_node_fs2.statSync)(fp);
|
|
655
|
+
} catch {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (stat.isDirectory() && recursive && d === dir) {
|
|
659
|
+
scanDir(fp);
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (!stat.isFile()) continue;
|
|
663
|
+
if (f.endsWith(".json")) {
|
|
664
|
+
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
|
|
665
|
+
} else if (f.endsWith(".jsonl")) {
|
|
666
|
+
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} catch {
|
|
670
|
+
}
|
|
651
671
|
}
|
|
672
|
+
scanDir(dir);
|
|
673
|
+
results.sort((a, b) => b.mtime - a.mtime);
|
|
674
|
+
return results;
|
|
652
675
|
}
|
|
653
|
-
function
|
|
676
|
+
function safeReadJson(fp) {
|
|
654
677
|
try {
|
|
655
|
-
return
|
|
678
|
+
return JSON.parse((0, import_node_fs2.readFileSync)(fp, "utf8"));
|
|
656
679
|
} catch {
|
|
657
680
|
return null;
|
|
658
681
|
}
|
|
659
682
|
}
|
|
660
|
-
function
|
|
683
|
+
function nameFromFile(filename) {
|
|
684
|
+
return (0, import_node_path2.basename)(filename).replace(/\.(json|jsonl)$/, "").replace(/-state$/, "");
|
|
685
|
+
}
|
|
686
|
+
function normalizeStatus(val) {
|
|
687
|
+
if (typeof val !== "string") return "unknown";
|
|
688
|
+
const s = val.toLowerCase();
|
|
689
|
+
if (["ok", "success", "completed", "done", "passed", "healthy", "good"].includes(s)) return "ok";
|
|
690
|
+
if (["error", "failed", "failure", "crashed", "unhealthy", "bad", "timeout"].includes(s)) return "error";
|
|
691
|
+
if (["running", "active", "in_progress", "started", "pending", "processing"].includes(s)) return "running";
|
|
692
|
+
return "unknown";
|
|
693
|
+
}
|
|
694
|
+
function findStatus(obj) {
|
|
695
|
+
for (const key of ["status", "state", "lastRunStatus", "lastStatus", "health", "result"]) {
|
|
696
|
+
if (key in obj) {
|
|
697
|
+
const val = obj[key];
|
|
698
|
+
if (typeof val === "string") return normalizeStatus(val);
|
|
699
|
+
if (typeof val === "object" && val !== null && "status" in val) {
|
|
700
|
+
return normalizeStatus(val.status);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return "unknown";
|
|
705
|
+
}
|
|
706
|
+
function findTimestamp(obj) {
|
|
707
|
+
for (const key of ["ts", "timestamp", "lastRunAtMs", "last_run", "lastExecution", "updated_at", "started_at", "endTime", "startTime"]) {
|
|
708
|
+
const val = obj[key];
|
|
709
|
+
if (typeof val === "number") return val > 1e12 ? val : val * 1e3;
|
|
710
|
+
if (typeof val === "string") {
|
|
711
|
+
const d = Date.parse(val);
|
|
712
|
+
if (!isNaN(d)) return d;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return 0;
|
|
716
|
+
}
|
|
717
|
+
function extractDetail(obj) {
|
|
718
|
+
const parts = [];
|
|
719
|
+
for (const key of ["summary", "message", "description", "lastError", "error", "name", "jobId", "id"]) {
|
|
720
|
+
const val = obj[key];
|
|
721
|
+
if (typeof val === "string" && val.length > 0 && val.length < 200) {
|
|
722
|
+
parts.push(val.slice(0, 80));
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
for (const key of ["totalExecutions", "runs", "count", "processed", "consecutiveErrors"]) {
|
|
727
|
+
const val = obj[key];
|
|
728
|
+
if (typeof val === "number") {
|
|
729
|
+
parts.push(`${key}: ${val}`);
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return parts.join(" | ") || "";
|
|
734
|
+
}
|
|
735
|
+
function tryLoadTrace(fp, raw) {
|
|
736
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
737
|
+
const obj = raw;
|
|
738
|
+
if (!("nodes" in obj)) return null;
|
|
739
|
+
if (!("agentId" in obj) && !("rootNodeId" in obj) && !("rootId" in obj)) return null;
|
|
661
740
|
try {
|
|
662
|
-
|
|
663
|
-
const fails = getFailures(trace);
|
|
664
|
-
const hung = getHungNodes(trace);
|
|
665
|
-
return {
|
|
666
|
-
agentId: trace.agentId,
|
|
667
|
-
trigger: trace.trigger,
|
|
668
|
-
traceId: trace.traceId,
|
|
669
|
-
spanId: trace.spanId,
|
|
670
|
-
parentSpanId: trace.parentSpanId,
|
|
671
|
-
nodes: stats.totalNodes,
|
|
672
|
-
success: fails.length === 0 && hung.length === 0,
|
|
673
|
-
failures: fails.length,
|
|
674
|
-
hung: hung.length
|
|
675
|
-
};
|
|
741
|
+
return loadGraph(obj);
|
|
676
742
|
} catch {
|
|
677
743
|
return null;
|
|
678
744
|
}
|
|
679
745
|
}
|
|
680
|
-
function
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
746
|
+
function processJsonFile(file) {
|
|
747
|
+
const raw = safeReadJson(file.path);
|
|
748
|
+
if (raw === null) return [];
|
|
749
|
+
const records = [];
|
|
750
|
+
const trace = tryLoadTrace(file.path, raw);
|
|
751
|
+
if (trace) {
|
|
752
|
+
try {
|
|
753
|
+
const fails = getFailures(trace);
|
|
754
|
+
const hung = getHungNodes(trace);
|
|
755
|
+
const stats = getStats(trace);
|
|
756
|
+
records.push({
|
|
757
|
+
id: trace.agentId,
|
|
758
|
+
source: "trace",
|
|
759
|
+
status: fails.length === 0 && hung.length === 0 ? "ok" : "error",
|
|
760
|
+
lastActive: file.mtime,
|
|
761
|
+
detail: `${stats.totalNodes} nodes [${trace.trigger}]`,
|
|
762
|
+
file: file.filename,
|
|
763
|
+
traceData: trace
|
|
764
|
+
});
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
return records;
|
|
768
|
+
}
|
|
769
|
+
if (typeof raw !== "object") return records;
|
|
770
|
+
const arr = Array.isArray(raw) ? raw : Array.isArray(raw.jobs) ? raw.jobs : Array.isArray(raw.tasks) ? raw.tasks : Array.isArray(raw.items) ? raw.items : null;
|
|
771
|
+
if (arr && arr.length > 0 && typeof arr[0] === "object" && arr[0] !== null) {
|
|
772
|
+
for (const item of arr.slice(0, 50)) {
|
|
773
|
+
const name = item.name ?? item.id ?? item.jobId ?? item.agentId;
|
|
774
|
+
if (!name) continue;
|
|
775
|
+
const state = typeof item.state === "object" && item.state !== null ? item.state : item;
|
|
776
|
+
const status2 = findStatus(state);
|
|
777
|
+
const ts2 = findTimestamp(state) || file.mtime;
|
|
778
|
+
const detail2 = extractDetail(state);
|
|
779
|
+
records.push({ id: String(name), source: "jobs", status: status2, lastActive: ts2, detail: detail2, file: file.filename });
|
|
780
|
+
}
|
|
781
|
+
return records;
|
|
782
|
+
}
|
|
783
|
+
const obj = raw;
|
|
784
|
+
for (const containerKey of ["tools", "workers", "services", "agents", "daemons"]) {
|
|
785
|
+
const container = obj[containerKey];
|
|
786
|
+
if (typeof container === "object" && container !== null && !Array.isArray(container)) {
|
|
787
|
+
for (const [name, info] of Object.entries(container)) {
|
|
788
|
+
if (typeof info !== "object" || info === null) continue;
|
|
789
|
+
const w = info;
|
|
790
|
+
const status2 = findStatus(w);
|
|
791
|
+
const ts2 = findTimestamp(w) || findTimestamp(obj) || file.mtime;
|
|
792
|
+
const pid = w.pid;
|
|
793
|
+
const detail2 = pid ? `pid: ${pid}` : extractDetail(w);
|
|
794
|
+
records.push({ id: name, source: "workers", status: status2, lastActive: ts2, detail: detail2, file: file.filename });
|
|
795
|
+
}
|
|
796
|
+
return records;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
const status = findStatus(obj);
|
|
800
|
+
const ts = findTimestamp(obj) || file.mtime;
|
|
801
|
+
const detail = extractDetail(obj);
|
|
802
|
+
records.push({ id: nameFromFile(file.filename), source: "state", status, lastActive: ts, detail, file: file.filename });
|
|
803
|
+
return records;
|
|
804
|
+
}
|
|
805
|
+
function processJsonlFile(file) {
|
|
806
|
+
try {
|
|
807
|
+
const content = (0, import_node_fs2.readFileSync)(file.path, "utf8").trim();
|
|
808
|
+
if (!content) return [];
|
|
809
|
+
const lines = content.split("\n");
|
|
810
|
+
const lastLine = lines[lines.length - 1];
|
|
811
|
+
const obj = JSON.parse(lastLine);
|
|
812
|
+
const name = obj.jobId ?? obj.agentId ?? obj.name ?? obj.id ?? nameFromFile(file.filename);
|
|
813
|
+
const status = findStatus(obj);
|
|
814
|
+
const ts = findTimestamp(obj) || file.mtime;
|
|
815
|
+
const action = obj.action;
|
|
816
|
+
const detail = action ? `${action} (${lines.length} entries)` : `${lines.length} entries`;
|
|
817
|
+
return [{ id: String(name), source: "session", status, lastActive: ts, detail, file: file.filename }];
|
|
818
|
+
} catch {
|
|
819
|
+
return [];
|
|
820
|
+
}
|
|
685
821
|
}
|
|
686
822
|
var prevFileCount = 0;
|
|
687
823
|
var newExecCount = 0;
|
|
688
824
|
var sessionStart = Date.now();
|
|
689
825
|
function render(config) {
|
|
690
|
-
const files =
|
|
826
|
+
const files = scanFiles(config.tracesDir, config.recursive);
|
|
691
827
|
if (files.length > prevFileCount && prevFileCount > 0) {
|
|
692
828
|
newExecCount += files.length - prevFileCount;
|
|
693
829
|
}
|
|
694
830
|
prevFileCount = files.length;
|
|
831
|
+
const allRecords = [];
|
|
695
832
|
const allTraces = [];
|
|
833
|
+
for (const f of files.slice(0, 300)) {
|
|
834
|
+
const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
|
|
835
|
+
for (const r of records) {
|
|
836
|
+
allRecords.push(r);
|
|
837
|
+
if (r.traceData) allTraces.push(r.traceData);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
696
840
|
const agents = {};
|
|
697
|
-
for (const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
allTraces.push(trace);
|
|
701
|
-
const a = analyze(trace);
|
|
702
|
-
if (!a) continue;
|
|
703
|
-
if (!agents[a.agentId]) {
|
|
704
|
-
agents[a.agentId] = { name: a.agentId, total: 0, ok: 0, fail: 0, lastTs: 0 };
|
|
841
|
+
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: "" };
|
|
705
844
|
}
|
|
706
|
-
const ag = agents[
|
|
845
|
+
const ag = agents[r.id];
|
|
707
846
|
ag.total++;
|
|
708
|
-
|
|
709
|
-
if (
|
|
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;
|
|
854
|
+
}
|
|
710
855
|
}
|
|
711
|
-
const agentList = Object.values(agents).sort((a, b) => b.
|
|
856
|
+
const agentList = Object.values(agents).sort((a, b) => b.lastTs - a.lastTs);
|
|
712
857
|
const totExec = agentList.reduce((s, a) => s + a.total, 0);
|
|
713
858
|
const totFail = agentList.reduce((s, a) => s + a.fail, 0);
|
|
859
|
+
const totRunning = agentList.reduce((s, a) => s + a.running, 0);
|
|
714
860
|
const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
|
|
715
|
-
const recent = [];
|
|
716
|
-
for (const f of files.slice(0, 15)) {
|
|
717
|
-
const trace = safeLoadTrace(f.path);
|
|
718
|
-
if (!trace) continue;
|
|
719
|
-
const a = analyze(trace);
|
|
720
|
-
if (a) recent.push({ ...a, ts: f.mtime });
|
|
721
|
-
}
|
|
722
861
|
const now = Date.now();
|
|
723
862
|
const buckets = new Array(12).fill(0);
|
|
724
863
|
const failBuckets = new Array(12).fill(0);
|
|
725
|
-
for (const
|
|
726
|
-
const age = now -
|
|
727
|
-
if (age > 36e5) continue;
|
|
864
|
+
for (const r of allRecords) {
|
|
865
|
+
const age = now - r.lastActive;
|
|
866
|
+
if (age > 36e5 || age < 0) continue;
|
|
728
867
|
const idx = 11 - Math.floor(age / 3e5);
|
|
729
868
|
if (idx >= 0 && idx < 12) {
|
|
730
|
-
const trace = safeLoadTrace(f.path);
|
|
731
|
-
if (!trace) continue;
|
|
732
|
-
const a = analyze(trace);
|
|
733
|
-
if (!a) continue;
|
|
734
869
|
buckets[idx]++;
|
|
735
|
-
if (
|
|
870
|
+
if (r.status === "error") failBuckets[idx]++;
|
|
736
871
|
}
|
|
737
872
|
}
|
|
738
873
|
const maxBucket = Math.max(...buckets, 1);
|
|
@@ -741,103 +876,140 @@ function render(config) {
|
|
|
741
876
|
const level = Math.round(v / maxBucket * 8);
|
|
742
877
|
return (failBuckets[i] > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
|
|
743
878
|
}).join("");
|
|
744
|
-
const traceGroups = groupByTraceId(allTraces);
|
|
745
879
|
const distributedTraces = [];
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
880
|
+
if (allTraces.length > 1) {
|
|
881
|
+
const traceGroups = groupByTraceId(allTraces);
|
|
882
|
+
for (const [_tid, graphs] of traceGroups) {
|
|
883
|
+
if (graphs.length > 1) {
|
|
884
|
+
try {
|
|
885
|
+
distributedTraces.push(stitchTrace(graphs));
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
751
888
|
}
|
|
752
889
|
}
|
|
890
|
+
distributedTraces.sort((a, b) => b.startTime - a.startTime);
|
|
753
891
|
}
|
|
754
|
-
distributedTraces.sort((a, b) => b.startTime - a.startTime);
|
|
755
892
|
const upSec = Math.floor((Date.now() - sessionStart) / 1e3);
|
|
756
893
|
const upMin = Math.floor(upSec / 60);
|
|
757
894
|
const upStr = upMin > 0 ? `${upMin}m ${upSec % 60}s` : `${upSec}s`;
|
|
758
895
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
896
|
+
const sourceTag = (s) => {
|
|
897
|
+
switch (s) {
|
|
898
|
+
case "trace":
|
|
899
|
+
return `${C.cyan}trace${C.reset}`;
|
|
900
|
+
case "jobs":
|
|
901
|
+
return `${C.blue}job${C.reset}`;
|
|
902
|
+
case "workers":
|
|
903
|
+
return `${C.magenta}worker${C.reset}`;
|
|
904
|
+
case "session":
|
|
905
|
+
return `${C.yellow}session${C.reset}`;
|
|
906
|
+
case "state":
|
|
907
|
+
return `${C.dim}state${C.reset}`;
|
|
908
|
+
}
|
|
909
|
+
};
|
|
759
910
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
760
|
-
console.log(`${C.bold}${C.cyan}\u2554\u2550\
|
|
911
|
+
console.log(`${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
|
|
761
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}`);
|
|
762
|
-
const metaLine = `Refresh: ${config.refreshMs / 1e3}s \xB7 Up: ${upStr}`;
|
|
913
|
+
const metaLine = `Refresh: ${config.refreshMs / 1e3}s \xB7 Up: ${upStr} \xB7 Files: ${files.length}`;
|
|
763
914
|
const pad1 = Math.max(0, 64 - metaLine.length);
|
|
764
915
|
console.log(`${C.bold}${C.cyan}\u2551${C.reset} ${C.dim}${metaLine}${C.reset}${" ".repeat(pad1)}${C.bold}${C.cyan}\u2551${C.reset}`);
|
|
765
|
-
console.log(`${C.bold}${C.cyan}\u255A\u2550\
|
|
916
|
+
console.log(`${C.bold}${C.cyan}\u255A${"\u2550".repeat(70)}\u255D${C.reset}`);
|
|
766
917
|
const sc = totFail === 0 ? C.green : C.yellow;
|
|
767
918
|
console.log("");
|
|
768
|
-
console.log(` ${C.bold}Agents${C.reset} ${sc}${agentList.length}${C.reset} ${C.bold}
|
|
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}`);
|
|
769
920
|
console.log("");
|
|
770
921
|
console.log(` ${C.bold}Activity (1h)${C.reset} ${spark} ${C.dim}\u2190 now${C.reset}`);
|
|
771
922
|
console.log("");
|
|
772
|
-
console.log(` ${C.bold}${C.under}Agent
|
|
773
|
-
for (const ag of agentList) {
|
|
774
|
-
const
|
|
775
|
-
const lastTime = new Date(ag.lastTs).toLocaleTimeString();
|
|
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";
|
|
776
926
|
const isRecent = Date.now() - ag.lastTs < 3e5;
|
|
777
|
-
let
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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}`;
|
|
941
|
+
} else {
|
|
942
|
+
statusIcon = `${C.dim}\u25CB${C.reset}`;
|
|
943
|
+
statusText = `${C.dim}idle${C.reset}`;
|
|
944
|
+
}
|
|
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}`);
|
|
788
950
|
}
|
|
789
951
|
if (distributedTraces.length > 0) {
|
|
790
952
|
console.log("");
|
|
791
|
-
console.log(` ${C.bold}${C.under}Distributed Traces
|
|
792
|
-
for (const dt of distributedTraces.slice(0,
|
|
953
|
+
console.log(` ${C.bold}${C.under}Distributed Traces${C.reset}`);
|
|
954
|
+
for (const dt of distributedTraces.slice(0, 3)) {
|
|
793
955
|
const traceTime = new Date(dt.startTime).toLocaleTimeString();
|
|
794
956
|
const statusIcon = dt.status === "completed" ? `${C.green}\u2713${C.reset}` : dt.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
795
957
|
const dur = dt.endTime ? `${dt.endTime - dt.startTime}ms` : "running";
|
|
796
958
|
const tid = dt.traceId.slice(0, 8);
|
|
797
|
-
console.log(` ${statusIcon} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime}
|
|
959
|
+
console.log(` ${statusIcon} ${C.magenta}trace:${tid}${C.reset} ${C.dim}${traceTime} ${dur} (${dt.graphs.size} agents)${C.reset}`);
|
|
798
960
|
const tree = getTraceTree(dt);
|
|
799
|
-
for (let i = 0; i < tree.length; i++) {
|
|
961
|
+
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
800
962
|
const g = tree[i];
|
|
801
|
-
const depth =
|
|
963
|
+
const depth = getDistDepth(dt, g.spanId);
|
|
802
964
|
const indent = " " + "\u2502 ".repeat(Math.max(0, depth - 1));
|
|
803
|
-
const isLast = i === tree.length - 1 ||
|
|
804
|
-
const
|
|
805
|
-
const
|
|
806
|
-
const
|
|
807
|
-
console.log(`${indent}${
|
|
965
|
+
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
966
|
+
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}`);
|
|
808
970
|
}
|
|
809
971
|
}
|
|
810
972
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
973
|
+
const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 8);
|
|
974
|
+
if (recentRecords.length > 0) {
|
|
975
|
+
console.log("");
|
|
976
|
+
console.log(` ${C.bold}${C.under}Recent Activity${C.reset}`);
|
|
977
|
+
for (const r of recentRecords) {
|
|
978
|
+
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
|
+
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
980
|
+
const agent = r.id.length > 26 ? r.id.slice(0, 25) + "\u2026" : r.id.padEnd(26);
|
|
981
|
+
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
982
|
+
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}`);
|
|
985
|
+
}
|
|
821
986
|
}
|
|
822
987
|
if (files.length === 0) {
|
|
823
|
-
console.log(
|
|
988
|
+
console.log("");
|
|
989
|
+
console.log(` ${C.dim}No JSON/JSONL files found. Waiting for data in:${C.reset}`);
|
|
824
990
|
console.log(` ${C.dim}${config.tracesDir}${C.reset}`);
|
|
825
991
|
}
|
|
826
992
|
console.log("");
|
|
827
993
|
console.log(` ${C.dim}Watching: ${config.tracesDir}${C.reset}`);
|
|
828
994
|
console.log(` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
829
995
|
}
|
|
996
|
+
function getDistDepth(dt, spanId) {
|
|
997
|
+
if (!spanId) return 0;
|
|
998
|
+
const g = dt.graphs.get(spanId);
|
|
999
|
+
if (!g || !g.parentSpanId) return 0;
|
|
1000
|
+
return 1 + getDistDepth(dt, g.parentSpanId);
|
|
1001
|
+
}
|
|
830
1002
|
function startLive(argv) {
|
|
831
1003
|
const config = parseArgs(argv);
|
|
832
1004
|
if (!(0, import_node_fs2.existsSync)(config.tracesDir)) {
|
|
833
|
-
console.error(`
|
|
834
|
-
console.error("
|
|
1005
|
+
console.error(`Directory does not exist: ${config.tracesDir}`);
|
|
1006
|
+
console.error("Specify a directory containing JSON/JSONL files: agentflow live <dir>");
|
|
835
1007
|
process.exit(1);
|
|
836
1008
|
}
|
|
837
1009
|
render(config);
|
|
838
1010
|
let debounce = null;
|
|
839
1011
|
try {
|
|
840
|
-
(0, import_node_fs2.watch)(config.tracesDir, () => {
|
|
1012
|
+
(0, import_node_fs2.watch)(config.tracesDir, { recursive: config.recursive }, () => {
|
|
841
1013
|
if (debounce) clearTimeout(debounce);
|
|
842
1014
|
debounce = setTimeout(() => render(config), 500);
|
|
843
1015
|
});
|
|
@@ -859,15 +1031,15 @@ Usage:
|
|
|
859
1031
|
agentflow <command> [options]
|
|
860
1032
|
|
|
861
1033
|
Commands:
|
|
862
|
-
run [options] -- <cmd>
|
|
863
|
-
live [
|
|
1034
|
+
run [options] -- <cmd> Wrap a command with automatic execution tracing
|
|
1035
|
+
live [dir] [options] Real-time terminal monitor (auto-detects any JSON/JSONL)
|
|
864
1036
|
|
|
865
1037
|
Run \`agentflow <command> --help\` for command-specific options.
|
|
866
1038
|
|
|
867
1039
|
Examples:
|
|
868
1040
|
agentflow run --traces-dir ./traces -- python -m myagent process
|
|
869
|
-
agentflow live ./
|
|
870
|
-
agentflow live ./
|
|
1041
|
+
agentflow live ./data
|
|
1042
|
+
agentflow live ./data -R --refresh 5
|
|
871
1043
|
`.trim());
|
|
872
1044
|
}
|
|
873
1045
|
function parseRunArgs(argv) {
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runTraced,
|
|
4
4
|
startLive
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FJVQYJFB.js";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { basename } from "path";
|
|
@@ -14,15 +14,15 @@ Usage:
|
|
|
14
14
|
agentflow <command> [options]
|
|
15
15
|
|
|
16
16
|
Commands:
|
|
17
|
-
run [options] -- <cmd>
|
|
18
|
-
live [
|
|
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
|
-
agentflow live ./
|
|
25
|
-
agentflow live ./
|
|
24
|
+
agentflow live ./data
|
|
25
|
+
agentflow live ./data -R --refresh 5
|
|
26
26
|
`.trim());
|
|
27
27
|
}
|
|
28
28
|
function parseRunArgs(argv) {
|