agentflow-core 0.7.0 → 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-5PRHVYYD.js → chunk-6X5HU5LB.js} +123 -52
- package/dist/cli.cjs +150 -75
- package/dist/cli.js +7 -3
- package/dist/index.cjs +1405 -119
- 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,7 +1051,8 @@ 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;
|
|
@@ -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,7 +1252,7 @@ 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);
|
|
@@ -1234,7 +1293,8 @@ function render(config) {
|
|
|
1234
1293
|
if (ar.systemd) {
|
|
1235
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}`;
|
|
1236
1295
|
sysdLabel = ` ${C.bold}Systemd${C.reset} ${si} ${ar.systemd.activeState}`;
|
|
1237
|
-
if (ar.systemd.restarts > 0)
|
|
1296
|
+
if (ar.systemd.restarts > 0)
|
|
1297
|
+
sysdLabel += ` ${C.dim}(${ar.systemd.restarts} restarts)${C.reset}`;
|
|
1238
1298
|
}
|
|
1239
1299
|
let pidLabel = "";
|
|
1240
1300
|
if (ar.pidFile?.pid) {
|
|
@@ -1243,7 +1303,10 @@ function render(config) {
|
|
|
1243
1303
|
}
|
|
1244
1304
|
writeLine(L, "");
|
|
1245
1305
|
writeLine(L, ` ${C.bold}${C.under}Process Health${C.reset}`);
|
|
1246
|
-
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
|
+
);
|
|
1247
1310
|
if (workerParts.length > 0) {
|
|
1248
1311
|
writeLine(L, ` ${C.dim}Workers${C.reset} ${workerParts.join(" ")}`);
|
|
1249
1312
|
}
|
|
@@ -1255,7 +1318,10 @@ function render(config) {
|
|
|
1255
1318
|
if (ar.orphans.length > 0) {
|
|
1256
1319
|
for (const o of ar.orphans.slice(0, 5)) {
|
|
1257
1320
|
const cmd = (o.cmdline || o.command).substring(0, detailWidth);
|
|
1258
|
-
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
|
+
);
|
|
1259
1325
|
}
|
|
1260
1326
|
if (ar.orphans.length > 5) {
|
|
1261
1327
|
writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
|
|
@@ -1329,7 +1395,7 @@ function render(config) {
|
|
|
1329
1395
|
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
1330
1396
|
const tg = tree[i];
|
|
1331
1397
|
const depth = getDistDepth(dt, tg.spanId);
|
|
1332
|
-
const indent =
|
|
1398
|
+
const indent = ` ${"\u2502 ".repeat(Math.max(0, depth - 1))}`;
|
|
1333
1399
|
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
1334
1400
|
const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
1335
1401
|
const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
@@ -1350,7 +1416,7 @@ function render(config) {
|
|
|
1350
1416
|
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
1351
1417
|
const agent = truncate(r.id, 26).padEnd(26);
|
|
1352
1418
|
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
1353
|
-
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`;
|
|
1354
1420
|
const det = truncate(r.detail, detailWidth);
|
|
1355
1421
|
writeLine(
|
|
1356
1422
|
L,
|
|
@@ -1415,7 +1481,7 @@ function startLive(argv) {
|
|
|
1415
1481
|
};
|
|
1416
1482
|
process.on("SIGINT", () => {
|
|
1417
1483
|
cleanup();
|
|
1418
|
-
console.log(C.dim
|
|
1484
|
+
console.log(`${C.dim}Monitor stopped.${C.reset}`);
|
|
1419
1485
|
process.exit(0);
|
|
1420
1486
|
});
|
|
1421
1487
|
process.on("SIGTERM", () => {
|
|
@@ -1430,7 +1496,7 @@ var import_node_fs3 = require("fs");
|
|
|
1430
1496
|
var import_node_path3 = require("path");
|
|
1431
1497
|
|
|
1432
1498
|
// src/graph-builder.ts
|
|
1433
|
-
var
|
|
1499
|
+
var import_node_crypto = require("crypto");
|
|
1434
1500
|
function deepFreeze(obj) {
|
|
1435
1501
|
if (obj === null || typeof obj !== "object") return obj;
|
|
1436
1502
|
if (obj instanceof Map) {
|
|
@@ -1461,8 +1527,8 @@ function createGraphBuilder(config) {
|
|
|
1461
1527
|
const generateId = config?.idGenerator ?? createCounterIdGenerator();
|
|
1462
1528
|
const agentId = config?.agentId ?? "unknown";
|
|
1463
1529
|
const trigger = config?.trigger ?? "manual";
|
|
1464
|
-
const spanId = (0,
|
|
1465
|
-
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)();
|
|
1466
1532
|
const parentSpanId = config?.parentSpanId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_PARENT_SPAN_ID : void 0) ?? null;
|
|
1467
1533
|
const graphId = generateId();
|
|
1468
1534
|
const startTime = Date.now();
|
|
@@ -1682,7 +1748,7 @@ function agentIdFromFilename(filePath) {
|
|
|
1682
1748
|
const cleaned = base.replace(/-state$/, "");
|
|
1683
1749
|
return `alfred-${cleaned}`;
|
|
1684
1750
|
}
|
|
1685
|
-
function deriveAgentId(
|
|
1751
|
+
function deriveAgentId(_command) {
|
|
1686
1752
|
return "orchestrator";
|
|
1687
1753
|
}
|
|
1688
1754
|
function fileTimestamp() {
|
|
@@ -1792,14 +1858,18 @@ async function runTraced(config) {
|
|
|
1792
1858
|
const filename = `${graph.agentId}-${ts}.json`;
|
|
1793
1859
|
const outPath = (0, import_node_path3.join)(resolvedTracesDir, filename);
|
|
1794
1860
|
const resolvedOut = (0, import_node_path3.resolve)(outPath);
|
|
1795
|
-
if (!resolvedOut.startsWith(resolvedTracesDir
|
|
1796
|
-
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
|
+
);
|
|
1797
1865
|
}
|
|
1798
1866
|
(0, import_node_fs3.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
|
|
1799
1867
|
tracePaths.push(outPath);
|
|
1800
1868
|
}
|
|
1801
1869
|
if (tracePaths.length > 0) {
|
|
1802
|
-
console.log(
|
|
1870
|
+
console.log(
|
|
1871
|
+
`\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`
|
|
1872
|
+
);
|
|
1803
1873
|
}
|
|
1804
1874
|
return {
|
|
1805
1875
|
exitCode,
|
|
@@ -1812,11 +1882,11 @@ async function runTraced(config) {
|
|
|
1812
1882
|
}
|
|
1813
1883
|
|
|
1814
1884
|
// src/trace-cli.ts
|
|
1815
|
-
var
|
|
1885
|
+
var import_node_path5 = require("path");
|
|
1816
1886
|
|
|
1817
1887
|
// src/trace-store.ts
|
|
1818
1888
|
var import_promises = require("fs/promises");
|
|
1819
|
-
var
|
|
1889
|
+
var import_node_path4 = require("path");
|
|
1820
1890
|
init_loader();
|
|
1821
1891
|
function createTraceStore(dir) {
|
|
1822
1892
|
async function ensureDir() {
|
|
@@ -1834,7 +1904,7 @@ function createTraceStore(dir) {
|
|
|
1834
1904
|
for (const file of files) {
|
|
1835
1905
|
if (!file.endsWith(".json")) continue;
|
|
1836
1906
|
try {
|
|
1837
|
-
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");
|
|
1838
1908
|
const graph = loadGraph(content);
|
|
1839
1909
|
graphs.push(graph);
|
|
1840
1910
|
} catch {
|
|
@@ -1846,10 +1916,10 @@ function createTraceStore(dir) {
|
|
|
1846
1916
|
async save(graph) {
|
|
1847
1917
|
await ensureDir();
|
|
1848
1918
|
const json = graphToJson(graph);
|
|
1849
|
-
const filePath = (0,
|
|
1850
|
-
const resolvedBase = (0,
|
|
1851
|
-
const resolvedPath = (0,
|
|
1852
|
-
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) {
|
|
1853
1923
|
throw new Error(`Path traversal detected: "${graph.id}" escapes base directory`);
|
|
1854
1924
|
}
|
|
1855
1925
|
await (0, import_promises.writeFile)(filePath, JSON.stringify(json, null, 2), "utf-8");
|
|
@@ -1857,7 +1927,7 @@ function createTraceStore(dir) {
|
|
|
1857
1927
|
},
|
|
1858
1928
|
async get(graphId) {
|
|
1859
1929
|
await ensureDir();
|
|
1860
|
-
const filePath = (0,
|
|
1930
|
+
const filePath = (0, import_node_path4.join)(dir, `${graphId}.json`);
|
|
1861
1931
|
try {
|
|
1862
1932
|
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
1863
1933
|
return loadGraph(content);
|
|
@@ -1979,7 +2049,8 @@ function toAsciiTree(graph) {
|
|
|
1979
2049
|
const children = getChildren(graph, nodeId);
|
|
1980
2050
|
const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
1981
2051
|
for (let i = 0; i < children.length; i++) {
|
|
1982
|
-
|
|
2052
|
+
const childId = children[i]?.id;
|
|
2053
|
+
if (childId) renderNode(childId, childPrefix, i === children.length - 1, false);
|
|
1983
2054
|
}
|
|
1984
2055
|
}
|
|
1985
2056
|
renderNode(graph.rootNodeId, "", true, true);
|
|
@@ -2053,9 +2124,9 @@ function toTimeline(graph) {
|
|
|
2053
2124
|
function getTracesDir(argv) {
|
|
2054
2125
|
const idx = argv.indexOf("--traces-dir");
|
|
2055
2126
|
if (idx !== -1 && argv[idx + 1]) {
|
|
2056
|
-
return (0,
|
|
2127
|
+
return (0, import_node_path5.resolve)(argv[idx + 1]);
|
|
2057
2128
|
}
|
|
2058
|
-
return (0,
|
|
2129
|
+
return (0, import_node_path5.resolve)("./traces");
|
|
2059
2130
|
}
|
|
2060
2131
|
function getFlag(argv, name) {
|
|
2061
2132
|
const idx = argv.indexOf(name);
|
|
@@ -2241,7 +2312,7 @@ async function handleTrace(argv) {
|
|
|
2241
2312
|
// src/watch.ts
|
|
2242
2313
|
var import_node_fs5 = require("fs");
|
|
2243
2314
|
var import_node_os = require("os");
|
|
2244
|
-
var
|
|
2315
|
+
var import_node_path6 = require("path");
|
|
2245
2316
|
|
|
2246
2317
|
// src/watch-alerts.ts
|
|
2247
2318
|
var import_node_child_process3 = require("child_process");
|
|
@@ -2367,10 +2438,10 @@ function parseDuration(input) {
|
|
|
2367
2438
|
const match = input.match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i);
|
|
2368
2439
|
if (!match) {
|
|
2369
2440
|
const n = parseInt(input, 10);
|
|
2370
|
-
return isNaN(n) ? 0 : n * 1e3;
|
|
2441
|
+
return Number.isNaN(n) ? 0 : n * 1e3;
|
|
2371
2442
|
}
|
|
2372
2443
|
const value = parseFloat(match[1]);
|
|
2373
|
-
switch (match[2]
|
|
2444
|
+
switch (match[2]?.toLowerCase()) {
|
|
2374
2445
|
case "s":
|
|
2375
2446
|
return value * 1e3;
|
|
2376
2447
|
case "m":
|
|
@@ -2397,7 +2468,7 @@ function loadWatchState(filePath) {
|
|
|
2397
2468
|
}
|
|
2398
2469
|
}
|
|
2399
2470
|
function saveWatchState(filePath, state) {
|
|
2400
|
-
const tmp = filePath
|
|
2471
|
+
const tmp = `${filePath}.tmp`;
|
|
2401
2472
|
try {
|
|
2402
2473
|
(0, import_node_fs4.writeFileSync)(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
2403
2474
|
(0, import_node_fs4.renameSync)(tmp, filePath);
|
|
@@ -2598,8 +2669,8 @@ function parseWatchArgs(argv) {
|
|
|
2598
2669
|
i++;
|
|
2599
2670
|
const val = args[i] ?? "";
|
|
2600
2671
|
if (val === "telegram") {
|
|
2601
|
-
const botToken = process.env
|
|
2602
|
-
const chatId = process.env
|
|
2672
|
+
const botToken = process.env.AGENTFLOW_TELEGRAM_BOT_TOKEN ?? "";
|
|
2673
|
+
const chatId = process.env.AGENTFLOW_TELEGRAM_CHAT_ID ?? "";
|
|
2603
2674
|
if (botToken && chatId) {
|
|
2604
2675
|
notifyChannels.push({ type: "telegram", botToken, chatId });
|
|
2605
2676
|
} else {
|
|
@@ -2616,7 +2687,7 @@ function parseWatchArgs(argv) {
|
|
|
2616
2687
|
} else if (arg === "--poll") {
|
|
2617
2688
|
i++;
|
|
2618
2689
|
const v = parseInt(args[i] ?? "", 10);
|
|
2619
|
-
if (!isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
|
|
2690
|
+
if (!Number.isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
|
|
2620
2691
|
i++;
|
|
2621
2692
|
} else if (arg === "--cooldown") {
|
|
2622
2693
|
i++;
|
|
@@ -2631,20 +2702,20 @@ function parseWatchArgs(argv) {
|
|
|
2631
2702
|
recursive = true;
|
|
2632
2703
|
i++;
|
|
2633
2704
|
} else if (!arg.startsWith("-")) {
|
|
2634
|
-
dirs.push((0,
|
|
2705
|
+
dirs.push((0, import_node_path6.resolve)(arg));
|
|
2635
2706
|
i++;
|
|
2636
2707
|
} else {
|
|
2637
2708
|
i++;
|
|
2638
2709
|
}
|
|
2639
2710
|
}
|
|
2640
|
-
if (dirs.length === 0) dirs.push((0,
|
|
2711
|
+
if (dirs.length === 0) dirs.push((0, import_node_path6.resolve)("."));
|
|
2641
2712
|
if (alertConditions.length === 0) {
|
|
2642
2713
|
alertConditions.push({ type: "error" });
|
|
2643
2714
|
alertConditions.push({ type: "recovery" });
|
|
2644
2715
|
}
|
|
2645
2716
|
notifyChannels.unshift({ type: "stdout" });
|
|
2646
2717
|
if (!stateFilePath) {
|
|
2647
|
-
stateFilePath = (0,
|
|
2718
|
+
stateFilePath = (0, import_node_path6.join)(dirs[0], ".agentflow-watch-state.json");
|
|
2648
2719
|
}
|
|
2649
2720
|
return {
|
|
2650
2721
|
dirs,
|
|
@@ -2652,7 +2723,7 @@ function parseWatchArgs(argv) {
|
|
|
2652
2723
|
pollIntervalMs,
|
|
2653
2724
|
alertConditions,
|
|
2654
2725
|
notifyChannels,
|
|
2655
|
-
stateFilePath: (0,
|
|
2726
|
+
stateFilePath: (0, import_node_path6.resolve)(stateFilePath),
|
|
2656
2727
|
cooldownMs
|
|
2657
2728
|
};
|
|
2658
2729
|
}
|
|
@@ -2732,7 +2803,7 @@ agentflow watch started`);
|
|
|
2732
2803
|
console.log(` Poll: ${config.pollIntervalMs / 1e3}s`);
|
|
2733
2804
|
console.log(` Alert on: ${condLabels.join(", ")}`);
|
|
2734
2805
|
console.log(
|
|
2735
|
-
` Notify: stdout${channelLabels.length > 0 ?
|
|
2806
|
+
` Notify: stdout${channelLabels.length > 0 ? `, ${channelLabels.join(", ")}` : ""}`
|
|
2736
2807
|
);
|
|
2737
2808
|
console.log(` Cooldown: ${Math.floor(config.cooldownMs / 6e4)}m`);
|
|
2738
2809
|
console.log(` State: ${config.stateFilePath}`);
|
|
@@ -2936,11 +3007,11 @@ async function runCommand(argv) {
|
|
|
2936
3007
|
if (result.tracePaths.length > 0) {
|
|
2937
3008
|
console.log("\u{1F4DD} Traces saved:");
|
|
2938
3009
|
const orchPath = result.tracePaths[0];
|
|
2939
|
-
const orchName = (0,
|
|
3010
|
+
const orchName = (0, import_node_path7.basename)(orchPath, ".json").split("-")[0] ?? "orchestrator";
|
|
2940
3011
|
console.log(` ${orchName.padEnd(14)} \u2192 ${orchPath}`);
|
|
2941
3012
|
for (let i = 1; i < result.tracePaths.length; i++) {
|
|
2942
3013
|
const tPath = result.tracePaths[i];
|
|
2943
|
-
const name = (0,
|
|
3014
|
+
const name = (0, import_node_path7.basename)(tPath, ".json").replace(/-\d{4}-.*$/, "");
|
|
2944
3015
|
const isLast = i === result.tracePaths.length - 1;
|
|
2945
3016
|
const prefix = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
2946
3017
|
console.log(` ${prefix} ${name.padEnd(12)} \u2192 ${tPath} (state changed)`);
|
|
@@ -2989,7 +3060,7 @@ function parseAuditArgs(argv) {
|
|
|
2989
3060
|
systemdUnit = null;
|
|
2990
3061
|
i++;
|
|
2991
3062
|
} else if (!arg.startsWith("-")) {
|
|
2992
|
-
discoverDirs.push((0,
|
|
3063
|
+
discoverDirs.push((0, import_node_path7.resolve)(arg));
|
|
2993
3064
|
i++;
|
|
2994
3065
|
} else {
|
|
2995
3066
|
i++;
|
|
@@ -2998,12 +3069,16 @@ function parseAuditArgs(argv) {
|
|
|
2998
3069
|
if (!processName && !pidFile && !workersFile && discoverDirs.length > 0) {
|
|
2999
3070
|
const discovered = discoverProcessConfig(discoverDirs);
|
|
3000
3071
|
if (discovered) {
|
|
3001
|
-
console.log(
|
|
3072
|
+
console.log(
|
|
3073
|
+
`Auto-discovered: process="${discovered.processName}"${discovered.pidFile ? ` pid-file=${discovered.pidFile}` : ""}${discovered.workersFile ? ` workers=${discovered.workersFile}` : ""}`
|
|
3074
|
+
);
|
|
3002
3075
|
return { ...discovered, systemdUnit };
|
|
3003
3076
|
}
|
|
3004
3077
|
}
|
|
3005
3078
|
if (!processName) {
|
|
3006
|
-
console.error(
|
|
3079
|
+
console.error(
|
|
3080
|
+
"Error: --process <name> is required, or provide directories for auto-discovery."
|
|
3081
|
+
);
|
|
3007
3082
|
console.error("Examples:");
|
|
3008
3083
|
console.error(" agentflow audit --process alfred --pid-file ./data/alfred.pid");
|
|
3009
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");
|