agentflow-core 0.6.3 → 0.8.0
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-R5SNE2HB.js → chunk-6X5HU5LB.js} +139 -57
- package/dist/cli.cjs +166 -80
- package/dist/cli.js +7 -3
- package/dist/index.cjs +1421 -124
- package/dist/index.d.cts +710 -37
- package/dist/index.d.ts +710 -37
- package/dist/index.js +1207 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -93,7 +93,7 @@ var init_loader = __esm({
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
// src/cli.ts
|
|
96
|
-
var
|
|
96
|
+
var import_node_path7 = require("path");
|
|
97
97
|
|
|
98
98
|
// src/live.ts
|
|
99
99
|
var import_node_fs2 = require("fs");
|
|
@@ -191,7 +191,7 @@ function groupByTraceId(graphs) {
|
|
|
191
191
|
}
|
|
192
192
|
function stitchTrace(graphs) {
|
|
193
193
|
if (graphs.length === 0) throw new Error("No graphs to stitch");
|
|
194
|
-
const traceId = graphs[0]
|
|
194
|
+
const traceId = graphs[0]?.traceId ?? "";
|
|
195
195
|
const graphsBySpan = /* @__PURE__ */ new Map();
|
|
196
196
|
const childMap = /* @__PURE__ */ new Map();
|
|
197
197
|
let rootGraph = null;
|
|
@@ -268,7 +268,7 @@ function pidMatchesName(pid, name) {
|
|
|
268
268
|
function readPidFile(path) {
|
|
269
269
|
try {
|
|
270
270
|
const pid = parseInt((0, import_node_fs.readFileSync)(path, "utf8").trim(), 10);
|
|
271
|
-
return isNaN(pid) ? null : pid;
|
|
271
|
+
return Number.isNaN(pid) ? null : pid;
|
|
272
272
|
} catch {
|
|
273
273
|
return null;
|
|
274
274
|
}
|
|
@@ -312,11 +312,11 @@ function auditSystemd(config) {
|
|
|
312
312
|
const [k, ...v] = line.split("=");
|
|
313
313
|
if (k) props[k.trim()] = v.join("=").trim();
|
|
314
314
|
}
|
|
315
|
-
const activeState = props
|
|
316
|
-
const subState = props
|
|
317
|
-
const mainPid = parseInt(props
|
|
318
|
-
const restarts = parseInt(props
|
|
319
|
-
const result = props
|
|
315
|
+
const activeState = props.ActiveState ?? "unknown";
|
|
316
|
+
const subState = props.SubState ?? "unknown";
|
|
317
|
+
const mainPid = parseInt(props.MainPID ?? "0", 10);
|
|
318
|
+
const restarts = parseInt(props.NRestarts ?? "0", 10);
|
|
319
|
+
const result = props.Result ?? "unknown";
|
|
320
320
|
return {
|
|
321
321
|
unit,
|
|
322
322
|
activeState,
|
|
@@ -369,10 +369,10 @@ function readCmdline(pid) {
|
|
|
369
369
|
}
|
|
370
370
|
function getOsProcesses(processName) {
|
|
371
371
|
try {
|
|
372
|
-
const raw = (0, import_node_child_process.execSync)(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
);
|
|
372
|
+
const raw = (0, import_node_child_process.execSync)(`ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`, {
|
|
373
|
+
encoding: "utf8",
|
|
374
|
+
timeout: 5e3
|
|
375
|
+
});
|
|
376
376
|
const results = [];
|
|
377
377
|
for (const line of raw.split("\n")) {
|
|
378
378
|
if (!line.includes(processName)) continue;
|
|
@@ -380,7 +380,7 @@ function getOsProcesses(processName) {
|
|
|
380
380
|
const trimmed = line.trim();
|
|
381
381
|
const parts = trimmed.split(/\s+/);
|
|
382
382
|
const pid = parseInt(parts[0] ?? "0", 10);
|
|
383
|
-
if (isNaN(pid) || pid <= 0) continue;
|
|
383
|
+
if (Number.isNaN(pid) || pid <= 0) continue;
|
|
384
384
|
const cpu = parts[1] ?? "0";
|
|
385
385
|
const mem = parts[2] ?? "0";
|
|
386
386
|
const elapsed = parts[3] ?? "";
|
|
@@ -457,16 +457,45 @@ function auditProcesses(config) {
|
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
459
|
if (systemd?.mainPid) knownPids.add(systemd.mainPid);
|
|
460
|
+
const childPids = /* @__PURE__ */ new Set();
|
|
461
|
+
for (const knownPid of knownPids) {
|
|
462
|
+
try {
|
|
463
|
+
const childrenRaw = (0, import_node_fs.readFileSync)(
|
|
464
|
+
`/proc/${knownPid}/task/${knownPid}/children`,
|
|
465
|
+
"utf8"
|
|
466
|
+
).trim();
|
|
467
|
+
if (childrenRaw) {
|
|
468
|
+
for (const c of childrenRaw.split(/\s+/)) {
|
|
469
|
+
const cp = parseInt(c, 10);
|
|
470
|
+
if (!Number.isNaN(cp)) childPids.add(cp);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (const p of osProcesses) {
|
|
477
|
+
if (knownPids.has(p.pid)) continue;
|
|
478
|
+
try {
|
|
479
|
+
const statusContent = (0, import_node_fs.readFileSync)(`/proc/${p.pid}/status`, "utf8");
|
|
480
|
+
const ppidMatch = statusContent.match(/^PPid:\s+(\d+)/m);
|
|
481
|
+
if (ppidMatch) {
|
|
482
|
+
const ppid = parseInt(ppidMatch[1] ?? "0", 10);
|
|
483
|
+
if (knownPids.has(ppid)) childPids.add(p.pid);
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
460
488
|
const selfPid = process.pid;
|
|
461
489
|
const selfPpid = process.ppid;
|
|
462
490
|
const orphans = osProcesses.filter(
|
|
463
|
-
(p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
|
|
491
|
+
(p) => !knownPids.has(p.pid) && !childPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
|
|
464
492
|
);
|
|
465
493
|
const problems = [];
|
|
466
494
|
if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
|
|
467
495
|
if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
|
|
468
496
|
if (systemd?.failed) problems.push("Systemd unit has failed");
|
|
469
|
-
if (systemd && systemd.restarts > 10)
|
|
497
|
+
if (systemd && systemd.restarts > 10)
|
|
498
|
+
problems.push(`High systemd restart count: ${systemd.restarts}`);
|
|
470
499
|
if (pidFile?.pid && systemd?.mainPid && pidFile.pid !== systemd.mainPid) {
|
|
471
500
|
problems.push(`PID mismatch: file says ${pidFile.pid}, systemd says ${systemd.mainPid}`);
|
|
472
501
|
}
|
|
@@ -475,15 +504,24 @@ function auditProcesses(config) {
|
|
|
475
504
|
if (w.stale) problems.push(`Worker "${w.name}" (pid ${w.pid}) declares running but is dead`);
|
|
476
505
|
}
|
|
477
506
|
}
|
|
478
|
-
if (orphans.length > 0)
|
|
507
|
+
if (orphans.length > 0)
|
|
508
|
+
problems.push(
|
|
509
|
+
`${orphans.length} orphan process(es) not tracked by PID file or workers registry`
|
|
510
|
+
);
|
|
479
511
|
return { pidFile, systemd, workers, osProcesses, orphans, problems };
|
|
480
512
|
}
|
|
481
513
|
function formatAuditReport(result) {
|
|
482
514
|
const lines = [];
|
|
483
515
|
lines.push("");
|
|
484
|
-
lines.push(
|
|
485
|
-
|
|
486
|
-
|
|
516
|
+
lines.push(
|
|
517
|
+
"\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"
|
|
518
|
+
);
|
|
519
|
+
lines.push(
|
|
520
|
+
"\u2551 \u{1F50D} P R O C E S S A U D I T \u2551"
|
|
521
|
+
);
|
|
522
|
+
lines.push(
|
|
523
|
+
"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
|
|
524
|
+
);
|
|
487
525
|
if (result.pidFile) {
|
|
488
526
|
const pf = result.pidFile;
|
|
489
527
|
const icon = pf.pid && pf.alive && pf.matchesProcess ? "\u2705" : pf.stale ? "\u26A0\uFE0F " : "\u2139\uFE0F ";
|
|
@@ -501,25 +539,33 @@ function formatAuditReport(result) {
|
|
|
501
539
|
}
|
|
502
540
|
if (result.workers) {
|
|
503
541
|
const w = result.workers;
|
|
504
|
-
lines.push(
|
|
505
|
-
|
|
542
|
+
lines.push(
|
|
543
|
+
`
|
|
544
|
+
Workers (orchestrator pid ${w.orchestratorPid ?? "unknown"} ${w.orchestratorAlive ? "\u2705" : "\u274C"})`
|
|
545
|
+
);
|
|
506
546
|
for (const worker of w.workers) {
|
|
507
547
|
const icon = worker.declaredStatus === "running" && worker.alive ? "\u{1F7E2}" : worker.stale ? "\u{1F534} STALE" : "\u26AA";
|
|
508
|
-
lines.push(
|
|
548
|
+
lines.push(
|
|
549
|
+
` ${icon} ${worker.name.padEnd(14)} pid=${String(worker.pid ?? "-").padEnd(8)} status=${worker.declaredStatus}`
|
|
550
|
+
);
|
|
509
551
|
}
|
|
510
552
|
}
|
|
511
553
|
if (result.osProcesses.length > 0) {
|
|
512
554
|
lines.push(`
|
|
513
555
|
OS Processes (${result.osProcesses.length} total)`);
|
|
514
556
|
for (const p of result.osProcesses) {
|
|
515
|
-
lines.push(
|
|
557
|
+
lines.push(
|
|
558
|
+
` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed.padEnd(10)} ${p.command.substring(0, 50)}`
|
|
559
|
+
);
|
|
516
560
|
}
|
|
517
561
|
}
|
|
518
562
|
if (result.orphans.length > 0) {
|
|
519
563
|
lines.push(`
|
|
520
564
|
\u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
|
|
521
565
|
for (const p of result.orphans) {
|
|
522
|
-
lines.push(
|
|
566
|
+
lines.push(
|
|
567
|
+
` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`
|
|
568
|
+
);
|
|
523
569
|
lines.push(` Started: ${p.started}`);
|
|
524
570
|
lines.push(` Command: ${p.cmdline || p.command}`);
|
|
525
571
|
}
|
|
@@ -564,7 +610,7 @@ function parseArgs(argv) {
|
|
|
564
610
|
} else if (arg === "--refresh" || arg === "-r") {
|
|
565
611
|
i++;
|
|
566
612
|
const v = parseInt(args[i] ?? "", 10);
|
|
567
|
-
if (!isNaN(v) && v > 0) config.refreshMs = v * 1e3;
|
|
613
|
+
if (!Number.isNaN(v) && v > 0) config.refreshMs = v * 1e3;
|
|
568
614
|
i++;
|
|
569
615
|
} else if (arg === "--recursive" || arg === "-R") {
|
|
570
616
|
config.recursive = true;
|
|
@@ -645,12 +691,22 @@ function scanFiles(dirs, recursive) {
|
|
|
645
691
|
if (!stat.isFile()) continue;
|
|
646
692
|
if (f.endsWith(".json")) {
|
|
647
693
|
seen.add(fp);
|
|
648
|
-
const entry = {
|
|
694
|
+
const entry = {
|
|
695
|
+
filename: f,
|
|
696
|
+
path: fp,
|
|
697
|
+
mtime: stat.mtime.getTime(),
|
|
698
|
+
ext: ".json"
|
|
699
|
+
};
|
|
649
700
|
results.push(entry);
|
|
650
701
|
dirResults.push(entry);
|
|
651
702
|
} else if (f.endsWith(".jsonl")) {
|
|
652
703
|
seen.add(fp);
|
|
653
|
-
const entry = {
|
|
704
|
+
const entry = {
|
|
705
|
+
filename: f,
|
|
706
|
+
path: fp,
|
|
707
|
+
mtime: stat.mtime.getTime(),
|
|
708
|
+
ext: ".jsonl"
|
|
709
|
+
};
|
|
654
710
|
results.push(entry);
|
|
655
711
|
dirResults.push(entry);
|
|
656
712
|
}
|
|
@@ -712,7 +768,7 @@ function findTimestamp(obj) {
|
|
|
712
768
|
if (typeof val === "number") return val > 1e12 ? val : val * 1e3;
|
|
713
769
|
if (typeof val === "string") {
|
|
714
770
|
const d = Date.parse(val);
|
|
715
|
-
if (!isNaN(d)) return d;
|
|
771
|
+
if (!Number.isNaN(d)) return d;
|
|
716
772
|
}
|
|
717
773
|
}
|
|
718
774
|
return 0;
|
|
@@ -744,7 +800,7 @@ function extractDetail(obj) {
|
|
|
744
800
|
}
|
|
745
801
|
return parts.join(" | ") || "";
|
|
746
802
|
}
|
|
747
|
-
function tryLoadTrace(
|
|
803
|
+
function tryLoadTrace(_fp, raw) {
|
|
748
804
|
if (typeof raw !== "object" || raw === null) return null;
|
|
749
805
|
const obj = raw;
|
|
750
806
|
if (!("nodes" in obj)) return null;
|
|
@@ -995,13 +1051,14 @@ function writeLine(lines, text) {
|
|
|
995
1051
|
}
|
|
996
1052
|
function flushLines(lines) {
|
|
997
1053
|
process.stdout.write("\x1B[H");
|
|
998
|
-
process.stdout.write(lines.join("\n")
|
|
1054
|
+
process.stdout.write(`${lines.join("\n")}
|
|
1055
|
+
`);
|
|
999
1056
|
process.stdout.write("\x1B[J");
|
|
1000
1057
|
}
|
|
1001
1058
|
var prevFileCount = 0;
|
|
1002
1059
|
var newExecCount = 0;
|
|
1003
1060
|
var sessionStart = Date.now();
|
|
1004
|
-
var
|
|
1061
|
+
var liveRunning = false;
|
|
1005
1062
|
var cachedAuditConfig = null;
|
|
1006
1063
|
var cachedAuditResult = null;
|
|
1007
1064
|
var lastAuditTime = 0;
|
|
@@ -1089,7 +1146,7 @@ function render(config) {
|
|
|
1089
1146
|
const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
|
|
1090
1147
|
groups.push({
|
|
1091
1148
|
name: groupName,
|
|
1092
|
-
source: records[0]
|
|
1149
|
+
source: records[0]?.source ?? "trace",
|
|
1093
1150
|
status,
|
|
1094
1151
|
lastTs,
|
|
1095
1152
|
detail: `${records.length} agents`,
|
|
@@ -1135,8 +1192,10 @@ function render(config) {
|
|
|
1135
1192
|
cachedAuditResult = auditResult;
|
|
1136
1193
|
lastAuditTime = now;
|
|
1137
1194
|
} catch (err) {
|
|
1138
|
-
process.stderr.write(
|
|
1139
|
-
`
|
|
1195
|
+
process.stderr.write(
|
|
1196
|
+
`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
|
|
1197
|
+
`
|
|
1198
|
+
);
|
|
1140
1199
|
}
|
|
1141
1200
|
}
|
|
1142
1201
|
} else {
|
|
@@ -1193,14 +1252,10 @@ function render(config) {
|
|
|
1193
1252
|
return new Date(ts).toLocaleTimeString();
|
|
1194
1253
|
}
|
|
1195
1254
|
function truncate(s, max) {
|
|
1196
|
-
return s.length > max ? s.slice(0, max - 1)
|
|
1255
|
+
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
1197
1256
|
}
|
|
1198
1257
|
const termWidth = process.stdout.columns || 120;
|
|
1199
1258
|
const detailWidth = Math.max(20, termWidth - 60);
|
|
1200
|
-
if (firstRender) {
|
|
1201
|
-
process.stdout.write("\x1B[2J");
|
|
1202
|
-
firstRender = false;
|
|
1203
|
-
}
|
|
1204
1259
|
const L = [];
|
|
1205
1260
|
writeLine(L, `${C.bold}${C.cyan}\u2554${"\u2550".repeat(70)}\u2557${C.reset}`);
|
|
1206
1261
|
writeLine(
|
|
@@ -1238,7 +1293,8 @@ function render(config) {
|
|
|
1238
1293
|
if (ar.systemd) {
|
|
1239
1294
|
const si = ar.systemd.activeState === "active" ? `${C.green}\u25CF${C.reset}` : ar.systemd.crashLooping ? `${C.yellow}\u25CF${C.reset}` : ar.systemd.failed ? `${C.red}\u25CF${C.reset}` : `${C.dim}\u25CB${C.reset}`;
|
|
1240
1295
|
sysdLabel = ` ${C.bold}Systemd${C.reset} ${si} ${ar.systemd.activeState}`;
|
|
1241
|
-
if (ar.systemd.restarts > 0)
|
|
1296
|
+
if (ar.systemd.restarts > 0)
|
|
1297
|
+
sysdLabel += ` ${C.dim}(${ar.systemd.restarts} restarts)${C.reset}`;
|
|
1242
1298
|
}
|
|
1243
1299
|
let pidLabel = "";
|
|
1244
1300
|
if (ar.pidFile?.pid) {
|
|
@@ -1247,7 +1303,10 @@ function render(config) {
|
|
|
1247
1303
|
}
|
|
1248
1304
|
writeLine(L, "");
|
|
1249
1305
|
writeLine(L, ` ${C.bold}${C.under}Process Health${C.reset}`);
|
|
1250
|
-
writeLine(
|
|
1306
|
+
writeLine(
|
|
1307
|
+
L,
|
|
1308
|
+
` ${healthIcon} ${healthLabel}${pidLabel}${sysdLabel} ${C.bold}Procs${C.reset} ${C.dim}${ar.osProcesses.length}${C.reset} ${ar.orphans.length > 0 ? `${C.red}Orphans ${ar.orphans.length}${C.reset}` : `${C.dim}Orphans 0${C.reset}`}`
|
|
1309
|
+
);
|
|
1251
1310
|
if (workerParts.length > 0) {
|
|
1252
1311
|
writeLine(L, ` ${C.dim}Workers${C.reset} ${workerParts.join(" ")}`);
|
|
1253
1312
|
}
|
|
@@ -1259,7 +1318,10 @@ function render(config) {
|
|
|
1259
1318
|
if (ar.orphans.length > 0) {
|
|
1260
1319
|
for (const o of ar.orphans.slice(0, 5)) {
|
|
1261
1320
|
const cmd = (o.cmdline || o.command).substring(0, detailWidth);
|
|
1262
|
-
writeLine(
|
|
1321
|
+
writeLine(
|
|
1322
|
+
L,
|
|
1323
|
+
` ${C.red}?${C.reset} ${C.dim}pid=${o.pid} cpu=${o.cpu} mem=${o.mem} up=${o.elapsed}${C.reset} ${C.dim}${cmd}${C.reset}`
|
|
1324
|
+
);
|
|
1263
1325
|
}
|
|
1264
1326
|
if (ar.orphans.length > 5) {
|
|
1265
1327
|
writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
|
|
@@ -1333,7 +1395,7 @@ function render(config) {
|
|
|
1333
1395
|
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
1334
1396
|
const tg = tree[i];
|
|
1335
1397
|
const depth = getDistDepth(dt, tg.spanId);
|
|
1336
|
-
const indent =
|
|
1398
|
+
const indent = ` ${"\u2502 ".repeat(Math.max(0, depth - 1))}`;
|
|
1337
1399
|
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
1338
1400
|
const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
1339
1401
|
const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
@@ -1354,7 +1416,7 @@ function render(config) {
|
|
|
1354
1416
|
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
1355
1417
|
const agent = truncate(r.id, 26).padEnd(26);
|
|
1356
1418
|
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
1357
|
-
const ageStr = age < 60 ? age
|
|
1419
|
+
const ageStr = age < 60 ? `${age}s ago` : age < 3600 ? `${Math.floor(age / 60)}m ago` : `${Math.floor(age / 3600)}h ago`;
|
|
1358
1420
|
const det = truncate(r.detail, detailWidth);
|
|
1359
1421
|
writeLine(
|
|
1360
1422
|
L,
|
|
@@ -1395,6 +1457,9 @@ function startLive(argv) {
|
|
|
1395
1457
|
console.warn(`Skipping non-existent: ${invalid.join(", ")}`);
|
|
1396
1458
|
}
|
|
1397
1459
|
config.dirs = valid;
|
|
1460
|
+
process.stdout.write("\x1B[?1049h");
|
|
1461
|
+
process.stdout.write("\x1B[?25l");
|
|
1462
|
+
liveRunning = true;
|
|
1398
1463
|
render(config);
|
|
1399
1464
|
let debounce = null;
|
|
1400
1465
|
for (const dir of config.dirs) {
|
|
@@ -1407,8 +1472,20 @@ function startLive(argv) {
|
|
|
1407
1472
|
}
|
|
1408
1473
|
}
|
|
1409
1474
|
setInterval(() => render(config), config.refreshMs);
|
|
1475
|
+
const cleanup = () => {
|
|
1476
|
+
if (liveRunning) {
|
|
1477
|
+
liveRunning = false;
|
|
1478
|
+
process.stdout.write("\x1B[?25h");
|
|
1479
|
+
process.stdout.write("\x1B[?1049l");
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1410
1482
|
process.on("SIGINT", () => {
|
|
1411
|
-
|
|
1483
|
+
cleanup();
|
|
1484
|
+
console.log(`${C.dim}Monitor stopped.${C.reset}`);
|
|
1485
|
+
process.exit(0);
|
|
1486
|
+
});
|
|
1487
|
+
process.on("SIGTERM", () => {
|
|
1488
|
+
cleanup();
|
|
1412
1489
|
process.exit(0);
|
|
1413
1490
|
});
|
|
1414
1491
|
}
|
|
@@ -1419,7 +1496,7 @@ var import_node_fs3 = require("fs");
|
|
|
1419
1496
|
var import_node_path3 = require("path");
|
|
1420
1497
|
|
|
1421
1498
|
// src/graph-builder.ts
|
|
1422
|
-
var
|
|
1499
|
+
var import_node_crypto = require("crypto");
|
|
1423
1500
|
function deepFreeze(obj) {
|
|
1424
1501
|
if (obj === null || typeof obj !== "object") return obj;
|
|
1425
1502
|
if (obj instanceof Map) {
|
|
@@ -1450,8 +1527,8 @@ function createGraphBuilder(config) {
|
|
|
1450
1527
|
const generateId = config?.idGenerator ?? createCounterIdGenerator();
|
|
1451
1528
|
const agentId = config?.agentId ?? "unknown";
|
|
1452
1529
|
const trigger = config?.trigger ?? "manual";
|
|
1453
|
-
const spanId = (0,
|
|
1454
|
-
const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? (0,
|
|
1530
|
+
const spanId = (0, import_node_crypto.randomUUID)();
|
|
1531
|
+
const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? (0, import_node_crypto.randomUUID)();
|
|
1455
1532
|
const parentSpanId = config?.parentSpanId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_PARENT_SPAN_ID : void 0) ?? null;
|
|
1456
1533
|
const graphId = generateId();
|
|
1457
1534
|
const startTime = Date.now();
|
|
@@ -1671,7 +1748,7 @@ function agentIdFromFilename(filePath) {
|
|
|
1671
1748
|
const cleaned = base.replace(/-state$/, "");
|
|
1672
1749
|
return `alfred-${cleaned}`;
|
|
1673
1750
|
}
|
|
1674
|
-
function deriveAgentId(
|
|
1751
|
+
function deriveAgentId(_command) {
|
|
1675
1752
|
return "orchestrator";
|
|
1676
1753
|
}
|
|
1677
1754
|
function fileTimestamp() {
|
|
@@ -1781,14 +1858,18 @@ async function runTraced(config) {
|
|
|
1781
1858
|
const filename = `${graph.agentId}-${ts}.json`;
|
|
1782
1859
|
const outPath = (0, import_node_path3.join)(resolvedTracesDir, filename);
|
|
1783
1860
|
const resolvedOut = (0, import_node_path3.resolve)(outPath);
|
|
1784
|
-
if (!resolvedOut.startsWith(resolvedTracesDir
|
|
1785
|
-
throw new Error(
|
|
1861
|
+
if (!resolvedOut.startsWith(`${resolvedTracesDir}/`) && resolvedOut !== resolvedTracesDir) {
|
|
1862
|
+
throw new Error(
|
|
1863
|
+
`Path traversal detected: agentId "${graph.agentId}" escapes traces directory`
|
|
1864
|
+
);
|
|
1786
1865
|
}
|
|
1787
1866
|
(0, import_node_fs3.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
|
|
1788
1867
|
tracePaths.push(outPath);
|
|
1789
1868
|
}
|
|
1790
1869
|
if (tracePaths.length > 0) {
|
|
1791
|
-
console.log(
|
|
1870
|
+
console.log(
|
|
1871
|
+
`\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`
|
|
1872
|
+
);
|
|
1792
1873
|
}
|
|
1793
1874
|
return {
|
|
1794
1875
|
exitCode,
|
|
@@ -1801,11 +1882,11 @@ async function runTraced(config) {
|
|
|
1801
1882
|
}
|
|
1802
1883
|
|
|
1803
1884
|
// src/trace-cli.ts
|
|
1804
|
-
var
|
|
1885
|
+
var import_node_path5 = require("path");
|
|
1805
1886
|
|
|
1806
1887
|
// src/trace-store.ts
|
|
1807
1888
|
var import_promises = require("fs/promises");
|
|
1808
|
-
var
|
|
1889
|
+
var import_node_path4 = require("path");
|
|
1809
1890
|
init_loader();
|
|
1810
1891
|
function createTraceStore(dir) {
|
|
1811
1892
|
async function ensureDir() {
|
|
@@ -1823,7 +1904,7 @@ function createTraceStore(dir) {
|
|
|
1823
1904
|
for (const file of files) {
|
|
1824
1905
|
if (!file.endsWith(".json")) continue;
|
|
1825
1906
|
try {
|
|
1826
|
-
const content = await (0, import_promises.readFile)((0,
|
|
1907
|
+
const content = await (0, import_promises.readFile)((0, import_node_path4.join)(dir, file), "utf-8");
|
|
1827
1908
|
const graph = loadGraph(content);
|
|
1828
1909
|
graphs.push(graph);
|
|
1829
1910
|
} catch {
|
|
@@ -1835,10 +1916,10 @@ function createTraceStore(dir) {
|
|
|
1835
1916
|
async save(graph) {
|
|
1836
1917
|
await ensureDir();
|
|
1837
1918
|
const json = graphToJson(graph);
|
|
1838
|
-
const filePath = (0,
|
|
1839
|
-
const resolvedBase = (0,
|
|
1840
|
-
const resolvedPath = (0,
|
|
1841
|
-
if (!resolvedPath.startsWith(resolvedBase
|
|
1919
|
+
const filePath = (0, import_node_path4.join)(dir, `${graph.id}.json`);
|
|
1920
|
+
const resolvedBase = (0, import_node_path4.resolve)(dir);
|
|
1921
|
+
const resolvedPath = (0, import_node_path4.resolve)(filePath);
|
|
1922
|
+
if (!resolvedPath.startsWith(`${resolvedBase}/`) && resolvedPath !== resolvedBase) {
|
|
1842
1923
|
throw new Error(`Path traversal detected: "${graph.id}" escapes base directory`);
|
|
1843
1924
|
}
|
|
1844
1925
|
await (0, import_promises.writeFile)(filePath, JSON.stringify(json, null, 2), "utf-8");
|
|
@@ -1846,7 +1927,7 @@ function createTraceStore(dir) {
|
|
|
1846
1927
|
},
|
|
1847
1928
|
async get(graphId) {
|
|
1848
1929
|
await ensureDir();
|
|
1849
|
-
const filePath = (0,
|
|
1930
|
+
const filePath = (0, import_node_path4.join)(dir, `${graphId}.json`);
|
|
1850
1931
|
try {
|
|
1851
1932
|
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
1852
1933
|
return loadGraph(content);
|
|
@@ -1968,7 +2049,8 @@ function toAsciiTree(graph) {
|
|
|
1968
2049
|
const children = getChildren(graph, nodeId);
|
|
1969
2050
|
const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
1970
2051
|
for (let i = 0; i < children.length; i++) {
|
|
1971
|
-
|
|
2052
|
+
const childId = children[i]?.id;
|
|
2053
|
+
if (childId) renderNode(childId, childPrefix, i === children.length - 1, false);
|
|
1972
2054
|
}
|
|
1973
2055
|
}
|
|
1974
2056
|
renderNode(graph.rootNodeId, "", true, true);
|
|
@@ -2042,9 +2124,9 @@ function toTimeline(graph) {
|
|
|
2042
2124
|
function getTracesDir(argv) {
|
|
2043
2125
|
const idx = argv.indexOf("--traces-dir");
|
|
2044
2126
|
if (idx !== -1 && argv[idx + 1]) {
|
|
2045
|
-
return (0,
|
|
2127
|
+
return (0, import_node_path5.resolve)(argv[idx + 1]);
|
|
2046
2128
|
}
|
|
2047
|
-
return (0,
|
|
2129
|
+
return (0, import_node_path5.resolve)("./traces");
|
|
2048
2130
|
}
|
|
2049
2131
|
function getFlag(argv, name) {
|
|
2050
2132
|
const idx = argv.indexOf(name);
|
|
@@ -2230,7 +2312,7 @@ async function handleTrace(argv) {
|
|
|
2230
2312
|
// src/watch.ts
|
|
2231
2313
|
var import_node_fs5 = require("fs");
|
|
2232
2314
|
var import_node_os = require("os");
|
|
2233
|
-
var
|
|
2315
|
+
var import_node_path6 = require("path");
|
|
2234
2316
|
|
|
2235
2317
|
// src/watch-alerts.ts
|
|
2236
2318
|
var import_node_child_process3 = require("child_process");
|
|
@@ -2356,10 +2438,10 @@ function parseDuration(input) {
|
|
|
2356
2438
|
const match = input.match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i);
|
|
2357
2439
|
if (!match) {
|
|
2358
2440
|
const n = parseInt(input, 10);
|
|
2359
|
-
return isNaN(n) ? 0 : n * 1e3;
|
|
2441
|
+
return Number.isNaN(n) ? 0 : n * 1e3;
|
|
2360
2442
|
}
|
|
2361
2443
|
const value = parseFloat(match[1]);
|
|
2362
|
-
switch (match[2]
|
|
2444
|
+
switch (match[2]?.toLowerCase()) {
|
|
2363
2445
|
case "s":
|
|
2364
2446
|
return value * 1e3;
|
|
2365
2447
|
case "m":
|
|
@@ -2386,7 +2468,7 @@ function loadWatchState(filePath) {
|
|
|
2386
2468
|
}
|
|
2387
2469
|
}
|
|
2388
2470
|
function saveWatchState(filePath, state) {
|
|
2389
|
-
const tmp = filePath
|
|
2471
|
+
const tmp = `${filePath}.tmp`;
|
|
2390
2472
|
try {
|
|
2391
2473
|
(0, import_node_fs4.writeFileSync)(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
2392
2474
|
(0, import_node_fs4.renameSync)(tmp, filePath);
|
|
@@ -2587,8 +2669,8 @@ function parseWatchArgs(argv) {
|
|
|
2587
2669
|
i++;
|
|
2588
2670
|
const val = args[i] ?? "";
|
|
2589
2671
|
if (val === "telegram") {
|
|
2590
|
-
const botToken = process.env
|
|
2591
|
-
const chatId = process.env
|
|
2672
|
+
const botToken = process.env.AGENTFLOW_TELEGRAM_BOT_TOKEN ?? "";
|
|
2673
|
+
const chatId = process.env.AGENTFLOW_TELEGRAM_CHAT_ID ?? "";
|
|
2592
2674
|
if (botToken && chatId) {
|
|
2593
2675
|
notifyChannels.push({ type: "telegram", botToken, chatId });
|
|
2594
2676
|
} else {
|
|
@@ -2605,7 +2687,7 @@ function parseWatchArgs(argv) {
|
|
|
2605
2687
|
} else if (arg === "--poll") {
|
|
2606
2688
|
i++;
|
|
2607
2689
|
const v = parseInt(args[i] ?? "", 10);
|
|
2608
|
-
if (!isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
|
|
2690
|
+
if (!Number.isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
|
|
2609
2691
|
i++;
|
|
2610
2692
|
} else if (arg === "--cooldown") {
|
|
2611
2693
|
i++;
|
|
@@ -2620,20 +2702,20 @@ function parseWatchArgs(argv) {
|
|
|
2620
2702
|
recursive = true;
|
|
2621
2703
|
i++;
|
|
2622
2704
|
} else if (!arg.startsWith("-")) {
|
|
2623
|
-
dirs.push((0,
|
|
2705
|
+
dirs.push((0, import_node_path6.resolve)(arg));
|
|
2624
2706
|
i++;
|
|
2625
2707
|
} else {
|
|
2626
2708
|
i++;
|
|
2627
2709
|
}
|
|
2628
2710
|
}
|
|
2629
|
-
if (dirs.length === 0) dirs.push((0,
|
|
2711
|
+
if (dirs.length === 0) dirs.push((0, import_node_path6.resolve)("."));
|
|
2630
2712
|
if (alertConditions.length === 0) {
|
|
2631
2713
|
alertConditions.push({ type: "error" });
|
|
2632
2714
|
alertConditions.push({ type: "recovery" });
|
|
2633
2715
|
}
|
|
2634
2716
|
notifyChannels.unshift({ type: "stdout" });
|
|
2635
2717
|
if (!stateFilePath) {
|
|
2636
|
-
stateFilePath = (0,
|
|
2718
|
+
stateFilePath = (0, import_node_path6.join)(dirs[0], ".agentflow-watch-state.json");
|
|
2637
2719
|
}
|
|
2638
2720
|
return {
|
|
2639
2721
|
dirs,
|
|
@@ -2641,7 +2723,7 @@ function parseWatchArgs(argv) {
|
|
|
2641
2723
|
pollIntervalMs,
|
|
2642
2724
|
alertConditions,
|
|
2643
2725
|
notifyChannels,
|
|
2644
|
-
stateFilePath: (0,
|
|
2726
|
+
stateFilePath: (0, import_node_path6.resolve)(stateFilePath),
|
|
2645
2727
|
cooldownMs
|
|
2646
2728
|
};
|
|
2647
2729
|
}
|
|
@@ -2721,7 +2803,7 @@ agentflow watch started`);
|
|
|
2721
2803
|
console.log(` Poll: ${config.pollIntervalMs / 1e3}s`);
|
|
2722
2804
|
console.log(` Alert on: ${condLabels.join(", ")}`);
|
|
2723
2805
|
console.log(
|
|
2724
|
-
` Notify: stdout${channelLabels.length > 0 ?
|
|
2806
|
+
` Notify: stdout${channelLabels.length > 0 ? `, ${channelLabels.join(", ")}` : ""}`
|
|
2725
2807
|
);
|
|
2726
2808
|
console.log(` Cooldown: ${Math.floor(config.cooldownMs / 6e4)}m`);
|
|
2727
2809
|
console.log(` State: ${config.stateFilePath}`);
|
|
@@ -2925,11 +3007,11 @@ async function runCommand(argv) {
|
|
|
2925
3007
|
if (result.tracePaths.length > 0) {
|
|
2926
3008
|
console.log("\u{1F4DD} Traces saved:");
|
|
2927
3009
|
const orchPath = result.tracePaths[0];
|
|
2928
|
-
const orchName = (0,
|
|
3010
|
+
const orchName = (0, import_node_path7.basename)(orchPath, ".json").split("-")[0] ?? "orchestrator";
|
|
2929
3011
|
console.log(` ${orchName.padEnd(14)} \u2192 ${orchPath}`);
|
|
2930
3012
|
for (let i = 1; i < result.tracePaths.length; i++) {
|
|
2931
3013
|
const tPath = result.tracePaths[i];
|
|
2932
|
-
const name = (0,
|
|
3014
|
+
const name = (0, import_node_path7.basename)(tPath, ".json").replace(/-\d{4}-.*$/, "");
|
|
2933
3015
|
const isLast = i === result.tracePaths.length - 1;
|
|
2934
3016
|
const prefix = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
2935
3017
|
console.log(` ${prefix} ${name.padEnd(12)} \u2192 ${tPath} (state changed)`);
|
|
@@ -2978,7 +3060,7 @@ function parseAuditArgs(argv) {
|
|
|
2978
3060
|
systemdUnit = null;
|
|
2979
3061
|
i++;
|
|
2980
3062
|
} else if (!arg.startsWith("-")) {
|
|
2981
|
-
discoverDirs.push((0,
|
|
3063
|
+
discoverDirs.push((0, import_node_path7.resolve)(arg));
|
|
2982
3064
|
i++;
|
|
2983
3065
|
} else {
|
|
2984
3066
|
i++;
|
|
@@ -2987,12 +3069,16 @@ function parseAuditArgs(argv) {
|
|
|
2987
3069
|
if (!processName && !pidFile && !workersFile && discoverDirs.length > 0) {
|
|
2988
3070
|
const discovered = discoverProcessConfig(discoverDirs);
|
|
2989
3071
|
if (discovered) {
|
|
2990
|
-
console.log(
|
|
3072
|
+
console.log(
|
|
3073
|
+
`Auto-discovered: process="${discovered.processName}"${discovered.pidFile ? ` pid-file=${discovered.pidFile}` : ""}${discovered.workersFile ? ` workers=${discovered.workersFile}` : ""}`
|
|
3074
|
+
);
|
|
2991
3075
|
return { ...discovered, systemdUnit };
|
|
2992
3076
|
}
|
|
2993
3077
|
}
|
|
2994
3078
|
if (!processName) {
|
|
2995
|
-
console.error(
|
|
3079
|
+
console.error(
|
|
3080
|
+
"Error: --process <name> is required, or provide directories for auto-discovery."
|
|
3081
|
+
);
|
|
2996
3082
|
console.error("Examples:");
|
|
2997
3083
|
console.error(" agentflow audit --process alfred --pid-file ./data/alfred.pid");
|
|
2998
3084
|
console.error(" agentflow audit ./data # auto-discovers *.pid and workers.json");
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
startWatch,
|
|
10
10
|
toAsciiTree,
|
|
11
11
|
toTimeline
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6X5HU5LB.js";
|
|
13
13
|
import "./chunk-DY7YHFIB.js";
|
|
14
14
|
|
|
15
15
|
// src/cli.ts
|
|
@@ -414,12 +414,16 @@ function parseAuditArgs(argv) {
|
|
|
414
414
|
if (!processName && !pidFile && !workersFile && discoverDirs.length > 0) {
|
|
415
415
|
const discovered = discoverProcessConfig(discoverDirs);
|
|
416
416
|
if (discovered) {
|
|
417
|
-
console.log(
|
|
417
|
+
console.log(
|
|
418
|
+
`Auto-discovered: process="${discovered.processName}"${discovered.pidFile ? ` pid-file=${discovered.pidFile}` : ""}${discovered.workersFile ? ` workers=${discovered.workersFile}` : ""}`
|
|
419
|
+
);
|
|
418
420
|
return { ...discovered, systemdUnit };
|
|
419
421
|
}
|
|
420
422
|
}
|
|
421
423
|
if (!processName) {
|
|
422
|
-
console.error(
|
|
424
|
+
console.error(
|
|
425
|
+
"Error: --process <name> is required, or provide directories for auto-discovery."
|
|
426
|
+
);
|
|
423
427
|
console.error("Examples:");
|
|
424
428
|
console.error(" agentflow audit --process alfred --pid-file ./data/alfred.pid");
|
|
425
429
|
console.error(" agentflow audit ./data # auto-discovers *.pid and workers.json");
|