agentflow-core 0.2.0 → 0.2.2
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-WOJEID7V.js} +317 -136
- package/dist/cli.cjs +320 -138
- package/dist/cli.js +6 -5
- package/dist/index.cjs +316 -135
- 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 = { dirs: [], 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,138 +598,276 @@ 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;
|
|
606
603
|
i++;
|
|
607
|
-
} else if (arg === "--
|
|
608
|
-
|
|
609
|
-
config.tracesDir = args[i] ?? config.tracesDir;
|
|
604
|
+
} else if (arg === "--recursive" || arg === "-R") {
|
|
605
|
+
config.recursive = true;
|
|
610
606
|
i++;
|
|
611
607
|
} else if (!arg.startsWith("-")) {
|
|
612
|
-
config.
|
|
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.
|
|
614
|
+
if (config.dirs.length === 0) config.dirs.push((0, import_node_path2.resolve)("."));
|
|
619
615
|
return config;
|
|
620
616
|
}
|
|
621
617
|
function printUsage() {
|
|
622
618
|
console.log(`
|
|
623
619
|
AgentFlow Live Monitor \u2014 real-time terminal dashboard for agent systems.
|
|
624
620
|
|
|
621
|
+
Auto-detects agent traces, state files, job schedulers, and session logs
|
|
622
|
+
from any JSON/JSONL files in the watched directories.
|
|
623
|
+
|
|
625
624
|
Usage:
|
|
626
|
-
agentflow live [
|
|
625
|
+
agentflow live [dir...] [options]
|
|
627
626
|
|
|
628
627
|
Arguments:
|
|
629
|
-
|
|
628
|
+
dir One or more directories to watch (default: .)
|
|
630
629
|
|
|
631
630
|
Options:
|
|
632
631
|
-r, --refresh <secs> Refresh interval in seconds (default: 3)
|
|
633
|
-
-
|
|
632
|
+
-R, --recursive Scan subdirectories (1 level deep)
|
|
634
633
|
-h, --help Show this help message
|
|
635
634
|
|
|
636
635
|
Examples:
|
|
637
|
-
agentflow live
|
|
638
|
-
agentflow live ./
|
|
639
|
-
agentflow live /var/
|
|
636
|
+
agentflow live ./data
|
|
637
|
+
agentflow live ./traces ./cron ./workers --refresh 5
|
|
638
|
+
agentflow live /var/lib/myagent -R
|
|
640
639
|
`.trim());
|
|
641
640
|
}
|
|
642
|
-
function
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
641
|
+
function scanFiles(dirs, recursive) {
|
|
642
|
+
const results = [];
|
|
643
|
+
const seen = /* @__PURE__ */ new Set();
|
|
644
|
+
function scanDir(d, topLevel) {
|
|
645
|
+
try {
|
|
646
|
+
for (const f of (0, import_node_fs2.readdirSync)(d)) {
|
|
647
|
+
if (f.startsWith(".")) continue;
|
|
648
|
+
const fp = (0, import_node_path2.join)(d, f);
|
|
649
|
+
if (seen.has(fp)) continue;
|
|
650
|
+
let stat;
|
|
651
|
+
try {
|
|
652
|
+
stat = (0, import_node_fs2.statSync)(fp);
|
|
653
|
+
} catch {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (stat.isDirectory() && recursive && topLevel) {
|
|
657
|
+
scanDir(fp, false);
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
if (!stat.isFile()) continue;
|
|
661
|
+
if (f.endsWith(".json")) {
|
|
662
|
+
seen.add(fp);
|
|
663
|
+
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
|
|
664
|
+
} else if (f.endsWith(".jsonl")) {
|
|
665
|
+
seen.add(fp);
|
|
666
|
+
results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} catch {
|
|
670
|
+
}
|
|
651
671
|
}
|
|
672
|
+
for (const dir of dirs) scanDir(dir, true);
|
|
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.dirs, 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,107 +876,153 @@ 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(
|
|
824
|
-
console.log(` ${C.dim}
|
|
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}`);
|
|
825
991
|
}
|
|
826
992
|
console.log("");
|
|
827
|
-
|
|
993
|
+
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
994
|
+
console.log(` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
828
995
|
console.log(` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
829
996
|
}
|
|
997
|
+
function getDistDepth(dt, spanId) {
|
|
998
|
+
if (!spanId) return 0;
|
|
999
|
+
const g = dt.graphs.get(spanId);
|
|
1000
|
+
if (!g || !g.parentSpanId) return 0;
|
|
1001
|
+
return 1 + getDistDepth(dt, g.parentSpanId);
|
|
1002
|
+
}
|
|
830
1003
|
function startLive(argv) {
|
|
831
1004
|
const config = parseArgs(argv);
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
console.error(
|
|
1005
|
+
const valid = config.dirs.filter((d) => (0, import_node_fs2.existsSync)(d));
|
|
1006
|
+
if (valid.length === 0) {
|
|
1007
|
+
console.error(`No valid directories found: ${config.dirs.join(", ")}`);
|
|
1008
|
+
console.error("Specify directories containing JSON/JSONL files: agentflow live <dir> [dir...]");
|
|
835
1009
|
process.exit(1);
|
|
836
1010
|
}
|
|
1011
|
+
const invalid = config.dirs.filter((d) => !(0, import_node_fs2.existsSync)(d));
|
|
1012
|
+
if (invalid.length > 0) {
|
|
1013
|
+
console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
|
|
1014
|
+
}
|
|
1015
|
+
config.dirs = valid;
|
|
837
1016
|
render(config);
|
|
838
1017
|
let debounce = null;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1018
|
+
for (const dir of config.dirs) {
|
|
1019
|
+
try {
|
|
1020
|
+
(0, import_node_fs2.watch)(dir, { recursive: config.recursive }, () => {
|
|
1021
|
+
if (debounce) clearTimeout(debounce);
|
|
1022
|
+
debounce = setTimeout(() => render(config), 500);
|
|
1023
|
+
});
|
|
1024
|
+
} catch {
|
|
1025
|
+
}
|
|
845
1026
|
}
|
|
846
1027
|
setInterval(() => render(config), config.refreshMs);
|
|
847
1028
|
process.on("SIGINT", () => {
|
|
@@ -859,15 +1040,16 @@ Usage:
|
|
|
859
1040
|
agentflow <command> [options]
|
|
860
1041
|
|
|
861
1042
|
Commands:
|
|
862
|
-
run [options] -- <cmd>
|
|
863
|
-
live [
|
|
1043
|
+
run [options] -- <cmd> Wrap a command with automatic execution tracing
|
|
1044
|
+
live [dir...] [options] Real-time terminal monitor (auto-detects any JSON/JSONL)
|
|
864
1045
|
|
|
865
1046
|
Run \`agentflow <command> --help\` for command-specific options.
|
|
866
1047
|
|
|
867
1048
|
Examples:
|
|
868
1049
|
agentflow run --traces-dir ./traces -- python -m myagent process
|
|
869
|
-
agentflow live ./
|
|
870
|
-
agentflow live ./traces
|
|
1050
|
+
agentflow live ./data
|
|
1051
|
+
agentflow live ./traces ./cron ./workers -R
|
|
1052
|
+
agentflow live ./data --refresh 5
|
|
871
1053
|
`.trim());
|
|
872
1054
|
}
|
|
873
1055
|
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-WOJEID7V.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>
|
|
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 ./traces
|
|
24
|
+
agentflow live ./data
|
|
25
|
+
agentflow live ./traces ./cron ./workers -R
|
|
26
|
+
agentflow live ./data --refresh 5
|
|
26
27
|
`.trim());
|
|
27
28
|
}
|
|
28
29
|
function parseRunArgs(argv) {
|