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
|
@@ -155,7 +155,7 @@ function groupByTraceId(graphs) {
|
|
|
155
155
|
}
|
|
156
156
|
function stitchTrace(graphs) {
|
|
157
157
|
if (graphs.length === 0) throw new Error("No graphs to stitch");
|
|
158
|
-
const traceId = graphs[0]
|
|
158
|
+
const traceId = graphs[0]?.traceId ?? "";
|
|
159
159
|
const graphsBySpan = /* @__PURE__ */ new Map();
|
|
160
160
|
const childMap = /* @__PURE__ */ new Map();
|
|
161
161
|
let rootGraph = null;
|
|
@@ -229,7 +229,7 @@ function pidMatchesName(pid, name) {
|
|
|
229
229
|
function readPidFile(path) {
|
|
230
230
|
try {
|
|
231
231
|
const pid = parseInt(readFileSync(path, "utf8").trim(), 10);
|
|
232
|
-
return isNaN(pid) ? null : pid;
|
|
232
|
+
return Number.isNaN(pid) ? null : pid;
|
|
233
233
|
} catch {
|
|
234
234
|
return null;
|
|
235
235
|
}
|
|
@@ -273,11 +273,11 @@ function auditSystemd(config) {
|
|
|
273
273
|
const [k, ...v] = line.split("=");
|
|
274
274
|
if (k) props[k.trim()] = v.join("=").trim();
|
|
275
275
|
}
|
|
276
|
-
const activeState = props
|
|
277
|
-
const subState = props
|
|
278
|
-
const mainPid = parseInt(props
|
|
279
|
-
const restarts = parseInt(props
|
|
280
|
-
const result = props
|
|
276
|
+
const activeState = props.ActiveState ?? "unknown";
|
|
277
|
+
const subState = props.SubState ?? "unknown";
|
|
278
|
+
const mainPid = parseInt(props.MainPID ?? "0", 10);
|
|
279
|
+
const restarts = parseInt(props.NRestarts ?? "0", 10);
|
|
280
|
+
const result = props.Result ?? "unknown";
|
|
281
281
|
return {
|
|
282
282
|
unit,
|
|
283
283
|
activeState,
|
|
@@ -330,10 +330,10 @@ function readCmdline(pid) {
|
|
|
330
330
|
}
|
|
331
331
|
function getOsProcesses(processName) {
|
|
332
332
|
try {
|
|
333
|
-
const raw = execSync(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
);
|
|
333
|
+
const raw = execSync(`ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`, {
|
|
334
|
+
encoding: "utf8",
|
|
335
|
+
timeout: 5e3
|
|
336
|
+
});
|
|
337
337
|
const results = [];
|
|
338
338
|
for (const line of raw.split("\n")) {
|
|
339
339
|
if (!line.includes(processName)) continue;
|
|
@@ -341,7 +341,7 @@ function getOsProcesses(processName) {
|
|
|
341
341
|
const trimmed = line.trim();
|
|
342
342
|
const parts = trimmed.split(/\s+/);
|
|
343
343
|
const pid = parseInt(parts[0] ?? "0", 10);
|
|
344
|
-
if (isNaN(pid) || pid <= 0) continue;
|
|
344
|
+
if (Number.isNaN(pid) || pid <= 0) continue;
|
|
345
345
|
const cpu = parts[1] ?? "0";
|
|
346
346
|
const mem = parts[2] ?? "0";
|
|
347
347
|
const elapsed = parts[3] ?? "";
|
|
@@ -418,16 +418,45 @@ function auditProcesses(config) {
|
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
if (systemd?.mainPid) knownPids.add(systemd.mainPid);
|
|
421
|
+
const childPids = /* @__PURE__ */ new Set();
|
|
422
|
+
for (const knownPid of knownPids) {
|
|
423
|
+
try {
|
|
424
|
+
const childrenRaw = readFileSync(
|
|
425
|
+
`/proc/${knownPid}/task/${knownPid}/children`,
|
|
426
|
+
"utf8"
|
|
427
|
+
).trim();
|
|
428
|
+
if (childrenRaw) {
|
|
429
|
+
for (const c of childrenRaw.split(/\s+/)) {
|
|
430
|
+
const cp = parseInt(c, 10);
|
|
431
|
+
if (!Number.isNaN(cp)) childPids.add(cp);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
for (const p of osProcesses) {
|
|
438
|
+
if (knownPids.has(p.pid)) continue;
|
|
439
|
+
try {
|
|
440
|
+
const statusContent = readFileSync(`/proc/${p.pid}/status`, "utf8");
|
|
441
|
+
const ppidMatch = statusContent.match(/^PPid:\s+(\d+)/m);
|
|
442
|
+
if (ppidMatch) {
|
|
443
|
+
const ppid = parseInt(ppidMatch[1] ?? "0", 10);
|
|
444
|
+
if (knownPids.has(ppid)) childPids.add(p.pid);
|
|
445
|
+
}
|
|
446
|
+
} catch {
|
|
447
|
+
}
|
|
448
|
+
}
|
|
421
449
|
const selfPid = process.pid;
|
|
422
450
|
const selfPpid = process.ppid;
|
|
423
451
|
const orphans = osProcesses.filter(
|
|
424
|
-
(p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
|
|
452
|
+
(p) => !knownPids.has(p.pid) && !childPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
|
|
425
453
|
);
|
|
426
454
|
const problems = [];
|
|
427
455
|
if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
|
|
428
456
|
if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
|
|
429
457
|
if (systemd?.failed) problems.push("Systemd unit has failed");
|
|
430
|
-
if (systemd && systemd.restarts > 10)
|
|
458
|
+
if (systemd && systemd.restarts > 10)
|
|
459
|
+
problems.push(`High systemd restart count: ${systemd.restarts}`);
|
|
431
460
|
if (pidFile?.pid && systemd?.mainPid && pidFile.pid !== systemd.mainPid) {
|
|
432
461
|
problems.push(`PID mismatch: file says ${pidFile.pid}, systemd says ${systemd.mainPid}`);
|
|
433
462
|
}
|
|
@@ -436,15 +465,24 @@ function auditProcesses(config) {
|
|
|
436
465
|
if (w.stale) problems.push(`Worker "${w.name}" (pid ${w.pid}) declares running but is dead`);
|
|
437
466
|
}
|
|
438
467
|
}
|
|
439
|
-
if (orphans.length > 0)
|
|
468
|
+
if (orphans.length > 0)
|
|
469
|
+
problems.push(
|
|
470
|
+
`${orphans.length} orphan process(es) not tracked by PID file or workers registry`
|
|
471
|
+
);
|
|
440
472
|
return { pidFile, systemd, workers, osProcesses, orphans, problems };
|
|
441
473
|
}
|
|
442
474
|
function formatAuditReport(result) {
|
|
443
475
|
const lines = [];
|
|
444
476
|
lines.push("");
|
|
445
|
-
lines.push(
|
|
446
|
-
|
|
447
|
-
|
|
477
|
+
lines.push(
|
|
478
|
+
"\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"
|
|
479
|
+
);
|
|
480
|
+
lines.push(
|
|
481
|
+
"\u2551 \u{1F50D} P R O C E S S A U D I T \u2551"
|
|
482
|
+
);
|
|
483
|
+
lines.push(
|
|
484
|
+
"\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"
|
|
485
|
+
);
|
|
448
486
|
if (result.pidFile) {
|
|
449
487
|
const pf = result.pidFile;
|
|
450
488
|
const icon = pf.pid && pf.alive && pf.matchesProcess ? "\u2705" : pf.stale ? "\u26A0\uFE0F " : "\u2139\uFE0F ";
|
|
@@ -462,25 +500,33 @@ function formatAuditReport(result) {
|
|
|
462
500
|
}
|
|
463
501
|
if (result.workers) {
|
|
464
502
|
const w = result.workers;
|
|
465
|
-
lines.push(
|
|
466
|
-
|
|
503
|
+
lines.push(
|
|
504
|
+
`
|
|
505
|
+
Workers (orchestrator pid ${w.orchestratorPid ?? "unknown"} ${w.orchestratorAlive ? "\u2705" : "\u274C"})`
|
|
506
|
+
);
|
|
467
507
|
for (const worker of w.workers) {
|
|
468
508
|
const icon = worker.declaredStatus === "running" && worker.alive ? "\u{1F7E2}" : worker.stale ? "\u{1F534} STALE" : "\u26AA";
|
|
469
|
-
lines.push(
|
|
509
|
+
lines.push(
|
|
510
|
+
` ${icon} ${worker.name.padEnd(14)} pid=${String(worker.pid ?? "-").padEnd(8)} status=${worker.declaredStatus}`
|
|
511
|
+
);
|
|
470
512
|
}
|
|
471
513
|
}
|
|
472
514
|
if (result.osProcesses.length > 0) {
|
|
473
515
|
lines.push(`
|
|
474
516
|
OS Processes (${result.osProcesses.length} total)`);
|
|
475
517
|
for (const p of result.osProcesses) {
|
|
476
|
-
lines.push(
|
|
518
|
+
lines.push(
|
|
519
|
+
` 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)}`
|
|
520
|
+
);
|
|
477
521
|
}
|
|
478
522
|
}
|
|
479
523
|
if (result.orphans.length > 0) {
|
|
480
524
|
lines.push(`
|
|
481
525
|
\u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
|
|
482
526
|
for (const p of result.orphans) {
|
|
483
|
-
lines.push(
|
|
527
|
+
lines.push(
|
|
528
|
+
` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`
|
|
529
|
+
);
|
|
484
530
|
lines.push(` Started: ${p.started}`);
|
|
485
531
|
lines.push(` Command: ${p.cmdline || p.command}`);
|
|
486
532
|
}
|
|
@@ -527,7 +573,7 @@ function parseArgs(argv) {
|
|
|
527
573
|
} else if (arg === "--refresh" || arg === "-r") {
|
|
528
574
|
i++;
|
|
529
575
|
const v = parseInt(args[i] ?? "", 10);
|
|
530
|
-
if (!isNaN(v) && v > 0) config.refreshMs = v * 1e3;
|
|
576
|
+
if (!Number.isNaN(v) && v > 0) config.refreshMs = v * 1e3;
|
|
531
577
|
i++;
|
|
532
578
|
} else if (arg === "--recursive" || arg === "-R") {
|
|
533
579
|
config.recursive = true;
|
|
@@ -608,12 +654,22 @@ function scanFiles(dirs, recursive) {
|
|
|
608
654
|
if (!stat.isFile()) continue;
|
|
609
655
|
if (f.endsWith(".json")) {
|
|
610
656
|
seen.add(fp);
|
|
611
|
-
const entry = {
|
|
657
|
+
const entry = {
|
|
658
|
+
filename: f,
|
|
659
|
+
path: fp,
|
|
660
|
+
mtime: stat.mtime.getTime(),
|
|
661
|
+
ext: ".json"
|
|
662
|
+
};
|
|
612
663
|
results.push(entry);
|
|
613
664
|
dirResults.push(entry);
|
|
614
665
|
} else if (f.endsWith(".jsonl")) {
|
|
615
666
|
seen.add(fp);
|
|
616
|
-
const entry = {
|
|
667
|
+
const entry = {
|
|
668
|
+
filename: f,
|
|
669
|
+
path: fp,
|
|
670
|
+
mtime: stat.mtime.getTime(),
|
|
671
|
+
ext: ".jsonl"
|
|
672
|
+
};
|
|
617
673
|
results.push(entry);
|
|
618
674
|
dirResults.push(entry);
|
|
619
675
|
}
|
|
@@ -675,7 +731,7 @@ function findTimestamp(obj) {
|
|
|
675
731
|
if (typeof val === "number") return val > 1e12 ? val : val * 1e3;
|
|
676
732
|
if (typeof val === "string") {
|
|
677
733
|
const d = Date.parse(val);
|
|
678
|
-
if (!isNaN(d)) return d;
|
|
734
|
+
if (!Number.isNaN(d)) return d;
|
|
679
735
|
}
|
|
680
736
|
}
|
|
681
737
|
return 0;
|
|
@@ -707,7 +763,7 @@ function extractDetail(obj) {
|
|
|
707
763
|
}
|
|
708
764
|
return parts.join(" | ") || "";
|
|
709
765
|
}
|
|
710
|
-
function tryLoadTrace(
|
|
766
|
+
function tryLoadTrace(_fp, raw) {
|
|
711
767
|
if (typeof raw !== "object" || raw === null) return null;
|
|
712
768
|
const obj = raw;
|
|
713
769
|
if (!("nodes" in obj)) return null;
|
|
@@ -958,7 +1014,8 @@ function writeLine(lines, text) {
|
|
|
958
1014
|
}
|
|
959
1015
|
function flushLines(lines) {
|
|
960
1016
|
process.stdout.write("\x1B[H");
|
|
961
|
-
process.stdout.write(lines.join("\n")
|
|
1017
|
+
process.stdout.write(`${lines.join("\n")}
|
|
1018
|
+
`);
|
|
962
1019
|
process.stdout.write("\x1B[J");
|
|
963
1020
|
}
|
|
964
1021
|
var prevFileCount = 0;
|
|
@@ -1052,7 +1109,7 @@ function render(config) {
|
|
|
1052
1109
|
const status = fail > 0 ? "error" : running > 0 ? "running" : ok > 0 ? "ok" : "unknown";
|
|
1053
1110
|
groups.push({
|
|
1054
1111
|
name: groupName,
|
|
1055
|
-
source: records[0]
|
|
1112
|
+
source: records[0]?.source ?? "trace",
|
|
1056
1113
|
status,
|
|
1057
1114
|
lastTs,
|
|
1058
1115
|
detail: `${records.length} agents`,
|
|
@@ -1098,8 +1155,10 @@ function render(config) {
|
|
|
1098
1155
|
cachedAuditResult = auditResult;
|
|
1099
1156
|
lastAuditTime = now;
|
|
1100
1157
|
} catch (err) {
|
|
1101
|
-
process.stderr.write(
|
|
1102
|
-
`
|
|
1158
|
+
process.stderr.write(
|
|
1159
|
+
`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
|
|
1160
|
+
`
|
|
1161
|
+
);
|
|
1103
1162
|
}
|
|
1104
1163
|
}
|
|
1105
1164
|
} else {
|
|
@@ -1156,7 +1215,7 @@ function render(config) {
|
|
|
1156
1215
|
return new Date(ts).toLocaleTimeString();
|
|
1157
1216
|
}
|
|
1158
1217
|
function truncate(s, max) {
|
|
1159
|
-
return s.length > max ? s.slice(0, max - 1)
|
|
1218
|
+
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
1160
1219
|
}
|
|
1161
1220
|
const termWidth = process.stdout.columns || 120;
|
|
1162
1221
|
const detailWidth = Math.max(20, termWidth - 60);
|
|
@@ -1197,7 +1256,8 @@ function render(config) {
|
|
|
1197
1256
|
if (ar.systemd) {
|
|
1198
1257
|
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}`;
|
|
1199
1258
|
sysdLabel = ` ${C.bold}Systemd${C.reset} ${si} ${ar.systemd.activeState}`;
|
|
1200
|
-
if (ar.systemd.restarts > 0)
|
|
1259
|
+
if (ar.systemd.restarts > 0)
|
|
1260
|
+
sysdLabel += ` ${C.dim}(${ar.systemd.restarts} restarts)${C.reset}`;
|
|
1201
1261
|
}
|
|
1202
1262
|
let pidLabel = "";
|
|
1203
1263
|
if (ar.pidFile?.pid) {
|
|
@@ -1206,7 +1266,10 @@ function render(config) {
|
|
|
1206
1266
|
}
|
|
1207
1267
|
writeLine(L, "");
|
|
1208
1268
|
writeLine(L, ` ${C.bold}${C.under}Process Health${C.reset}`);
|
|
1209
|
-
writeLine(
|
|
1269
|
+
writeLine(
|
|
1270
|
+
L,
|
|
1271
|
+
` ${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}`}`
|
|
1272
|
+
);
|
|
1210
1273
|
if (workerParts.length > 0) {
|
|
1211
1274
|
writeLine(L, ` ${C.dim}Workers${C.reset} ${workerParts.join(" ")}`);
|
|
1212
1275
|
}
|
|
@@ -1218,7 +1281,10 @@ function render(config) {
|
|
|
1218
1281
|
if (ar.orphans.length > 0) {
|
|
1219
1282
|
for (const o of ar.orphans.slice(0, 5)) {
|
|
1220
1283
|
const cmd = (o.cmdline || o.command).substring(0, detailWidth);
|
|
1221
|
-
writeLine(
|
|
1284
|
+
writeLine(
|
|
1285
|
+
L,
|
|
1286
|
+
` ${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}`
|
|
1287
|
+
);
|
|
1222
1288
|
}
|
|
1223
1289
|
if (ar.orphans.length > 5) {
|
|
1224
1290
|
writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
|
|
@@ -1292,7 +1358,7 @@ function render(config) {
|
|
|
1292
1358
|
for (let i = 0; i < Math.min(tree.length, 6); i++) {
|
|
1293
1359
|
const tg = tree[i];
|
|
1294
1360
|
const depth = getDistDepth(dt, tg.spanId);
|
|
1295
|
-
const indent =
|
|
1361
|
+
const indent = ` ${"\u2502 ".repeat(Math.max(0, depth - 1))}`;
|
|
1296
1362
|
const isLast = i === tree.length - 1 || getDistDepth(dt, tree[i + 1]?.spanId) <= depth;
|
|
1297
1363
|
const conn = depth === 0 ? " " : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
1298
1364
|
const gs = tg.status === "completed" ? `${C.green}\u2713${C.reset}` : tg.status === "failed" ? `${C.red}\u2717${C.reset}` : `${C.yellow}\u23F3${C.reset}`;
|
|
@@ -1313,7 +1379,7 @@ function render(config) {
|
|
|
1313
1379
|
const t = new Date(r.lastActive).toLocaleTimeString();
|
|
1314
1380
|
const agent = truncate(r.id, 26).padEnd(26);
|
|
1315
1381
|
const age = Math.floor((Date.now() - r.lastActive) / 1e3);
|
|
1316
|
-
const ageStr = age < 60 ? age
|
|
1382
|
+
const ageStr = age < 60 ? `${age}s ago` : age < 3600 ? `${Math.floor(age / 60)}m ago` : `${Math.floor(age / 3600)}h ago`;
|
|
1317
1383
|
const det = truncate(r.detail, detailWidth);
|
|
1318
1384
|
writeLine(
|
|
1319
1385
|
L,
|
|
@@ -1378,7 +1444,7 @@ function startLive(argv) {
|
|
|
1378
1444
|
};
|
|
1379
1445
|
process.on("SIGINT", () => {
|
|
1380
1446
|
cleanup();
|
|
1381
|
-
console.log(C.dim
|
|
1447
|
+
console.log(`${C.dim}Monitor stopped.${C.reset}`);
|
|
1382
1448
|
process.exit(0);
|
|
1383
1449
|
});
|
|
1384
1450
|
process.on("SIGTERM", () => {
|
|
@@ -1642,7 +1708,7 @@ function agentIdFromFilename(filePath) {
|
|
|
1642
1708
|
const cleaned = base.replace(/-state$/, "");
|
|
1643
1709
|
return `alfred-${cleaned}`;
|
|
1644
1710
|
}
|
|
1645
|
-
function deriveAgentId(
|
|
1711
|
+
function deriveAgentId(_command) {
|
|
1646
1712
|
return "orchestrator";
|
|
1647
1713
|
}
|
|
1648
1714
|
function fileTimestamp() {
|
|
@@ -1752,14 +1818,18 @@ async function runTraced(config) {
|
|
|
1752
1818
|
const filename = `${graph.agentId}-${ts}.json`;
|
|
1753
1819
|
const outPath = join3(resolvedTracesDir, filename);
|
|
1754
1820
|
const resolvedOut = resolve2(outPath);
|
|
1755
|
-
if (!resolvedOut.startsWith(resolvedTracesDir
|
|
1756
|
-
throw new Error(
|
|
1821
|
+
if (!resolvedOut.startsWith(`${resolvedTracesDir}/`) && resolvedOut !== resolvedTracesDir) {
|
|
1822
|
+
throw new Error(
|
|
1823
|
+
`Path traversal detected: agentId "${graph.agentId}" escapes traces directory`
|
|
1824
|
+
);
|
|
1757
1825
|
}
|
|
1758
1826
|
writeFileSync(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
|
|
1759
1827
|
tracePaths.push(outPath);
|
|
1760
1828
|
}
|
|
1761
1829
|
if (tracePaths.length > 0) {
|
|
1762
|
-
console.log(
|
|
1830
|
+
console.log(
|
|
1831
|
+
`\u{1F50D} Run "agentflow trace show ${orchestratorGraph.id} --traces-dir ${resolvedTracesDir}" to inspect`
|
|
1832
|
+
);
|
|
1763
1833
|
}
|
|
1764
1834
|
return {
|
|
1765
1835
|
exitCode,
|
|
@@ -1805,7 +1875,7 @@ function createTraceStore(dir) {
|
|
|
1805
1875
|
const filePath = join4(dir, `${graph.id}.json`);
|
|
1806
1876
|
const resolvedBase = resolve3(dir);
|
|
1807
1877
|
const resolvedPath = resolve3(filePath);
|
|
1808
|
-
if (!resolvedPath.startsWith(resolvedBase
|
|
1878
|
+
if (!resolvedPath.startsWith(`${resolvedBase}/`) && resolvedPath !== resolvedBase) {
|
|
1809
1879
|
throw new Error(`Path traversal detected: "${graph.id}" escapes base directory`);
|
|
1810
1880
|
}
|
|
1811
1881
|
await writeFile(filePath, JSON.stringify(json, null, 2), "utf-8");
|
|
@@ -1935,7 +2005,8 @@ function toAsciiTree(graph) {
|
|
|
1935
2005
|
const children = getChildren(graph, nodeId);
|
|
1936
2006
|
const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
1937
2007
|
for (let i = 0; i < children.length; i++) {
|
|
1938
|
-
|
|
2008
|
+
const childId = children[i]?.id;
|
|
2009
|
+
if (childId) renderNode(childId, childPrefix, i === children.length - 1, false);
|
|
1939
2010
|
}
|
|
1940
2011
|
}
|
|
1941
2012
|
renderNode(graph.rootNodeId, "", true, true);
|
|
@@ -2134,10 +2205,10 @@ function parseDuration(input) {
|
|
|
2134
2205
|
const match = input.match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i);
|
|
2135
2206
|
if (!match) {
|
|
2136
2207
|
const n = parseInt(input, 10);
|
|
2137
|
-
return isNaN(n) ? 0 : n * 1e3;
|
|
2208
|
+
return Number.isNaN(n) ? 0 : n * 1e3;
|
|
2138
2209
|
}
|
|
2139
2210
|
const value = parseFloat(match[1]);
|
|
2140
|
-
switch (match[2]
|
|
2211
|
+
switch (match[2]?.toLowerCase()) {
|
|
2141
2212
|
case "s":
|
|
2142
2213
|
return value * 1e3;
|
|
2143
2214
|
case "m":
|
|
@@ -2164,7 +2235,7 @@ function loadWatchState(filePath) {
|
|
|
2164
2235
|
}
|
|
2165
2236
|
}
|
|
2166
2237
|
function saveWatchState(filePath, state) {
|
|
2167
|
-
const tmp = filePath
|
|
2238
|
+
const tmp = `${filePath}.tmp`;
|
|
2168
2239
|
try {
|
|
2169
2240
|
writeFileSync2(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
2170
2241
|
renameSync(tmp, filePath);
|
|
@@ -2365,8 +2436,8 @@ function parseWatchArgs(argv) {
|
|
|
2365
2436
|
i++;
|
|
2366
2437
|
const val = args[i] ?? "";
|
|
2367
2438
|
if (val === "telegram") {
|
|
2368
|
-
const botToken = process.env
|
|
2369
|
-
const chatId = process.env
|
|
2439
|
+
const botToken = process.env.AGENTFLOW_TELEGRAM_BOT_TOKEN ?? "";
|
|
2440
|
+
const chatId = process.env.AGENTFLOW_TELEGRAM_CHAT_ID ?? "";
|
|
2370
2441
|
if (botToken && chatId) {
|
|
2371
2442
|
notifyChannels.push({ type: "telegram", botToken, chatId });
|
|
2372
2443
|
} else {
|
|
@@ -2383,7 +2454,7 @@ function parseWatchArgs(argv) {
|
|
|
2383
2454
|
} else if (arg === "--poll") {
|
|
2384
2455
|
i++;
|
|
2385
2456
|
const v = parseInt(args[i] ?? "", 10);
|
|
2386
|
-
if (!isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
|
|
2457
|
+
if (!Number.isNaN(v) && v > 0) pollIntervalMs = v * 1e3;
|
|
2387
2458
|
i++;
|
|
2388
2459
|
} else if (arg === "--cooldown") {
|
|
2389
2460
|
i++;
|
|
@@ -2499,7 +2570,7 @@ agentflow watch started`);
|
|
|
2499
2570
|
console.log(` Poll: ${config.pollIntervalMs / 1e3}s`);
|
|
2500
2571
|
console.log(` Alert on: ${condLabels.join(", ")}`);
|
|
2501
2572
|
console.log(
|
|
2502
|
-
` Notify: stdout${channelLabels.length > 0 ?
|
|
2573
|
+
` Notify: stdout${channelLabels.length > 0 ? `, ${channelLabels.join(", ")}` : ""}`
|
|
2503
2574
|
);
|
|
2504
2575
|
console.log(` Cooldown: ${Math.floor(config.cooldownMs / 6e4)}m`);
|
|
2505
2576
|
console.log(` State: ${config.stateFilePath}`);
|