agentflow-core 0.6.1 → 0.6.3

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.
@@ -321,18 +321,36 @@ function auditWorkers(config) {
321
321
  return null;
322
322
  }
323
323
  }
324
+ function readCmdline(pid) {
325
+ try {
326
+ return readFileSync(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
327
+ } catch {
328
+ return "";
329
+ }
330
+ }
324
331
  function getOsProcesses(processName) {
325
332
  try {
326
- const raw = execSync(`ps aux`, { encoding: "utf8", timeout: 5e3 });
327
- return raw.split("\n").filter((line) => line.includes(processName) && !line.includes("process-audit") && !line.includes("grep")).map((line) => {
328
- const parts = line.trim().split(/\s+/);
329
- return {
330
- pid: parseInt(parts[1] ?? "0", 10),
331
- cpu: parts[2] ?? "0",
332
- mem: parts[3] ?? "0",
333
- command: parts.slice(10).join(" ")
334
- };
335
- }).filter((p) => !isNaN(p.pid) && p.pid > 0);
333
+ const raw = execSync(
334
+ `ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`,
335
+ { encoding: "utf8", timeout: 5e3 }
336
+ );
337
+ const results = [];
338
+ for (const line of raw.split("\n")) {
339
+ if (!line.includes(processName)) continue;
340
+ if (line.includes("process-audit") || line.includes(" grep ")) continue;
341
+ const trimmed = line.trim();
342
+ const parts = trimmed.split(/\s+/);
343
+ const pid = parseInt(parts[0] ?? "0", 10);
344
+ if (isNaN(pid) || pid <= 0) continue;
345
+ const cpu = parts[1] ?? "0";
346
+ const mem = parts[2] ?? "0";
347
+ const elapsed = parts[3] ?? "";
348
+ const started = parts.slice(4, 9).join(" ");
349
+ const command = parts.slice(9).join(" ");
350
+ const cmdline = readCmdline(pid);
351
+ results.push({ pid, cpu, mem, elapsed, started, command, cmdline });
352
+ }
353
+ return results;
336
354
  } catch {
337
355
  return [];
338
356
  }
@@ -372,7 +390,19 @@ function discoverProcessConfig(dirs) {
372
390
  }
373
391
  if (!processName && !pidFile && !workersFile) return null;
374
392
  if (!processName) processName = "agent";
375
- return { processName, pidFile, workersFile };
393
+ let systemdUnit;
394
+ try {
395
+ const unitName = `${processName}.service`;
396
+ const result = execSync(
397
+ `systemctl --user show ${unitName} --property=LoadState --no-pager 2>/dev/null`,
398
+ { encoding: "utf8", timeout: 3e3 }
399
+ );
400
+ if (result.includes("LoadState=loaded")) {
401
+ systemdUnit = unitName;
402
+ }
403
+ } catch {
404
+ }
405
+ return { processName, pidFile, workersFile, systemdUnit };
376
406
  }
377
407
  function auditProcesses(config) {
378
408
  const pidFile = auditPidFile(config);
@@ -388,7 +418,11 @@ function auditProcesses(config) {
388
418
  }
389
419
  }
390
420
  if (systemd?.mainPid) knownPids.add(systemd.mainPid);
391
- const orphans = osProcesses.filter((p) => !knownPids.has(p.pid));
421
+ const selfPid = process.pid;
422
+ const selfPpid = process.ppid;
423
+ const orphans = osProcesses.filter(
424
+ (p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
425
+ );
392
426
  const problems = [];
393
427
  if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
394
428
  if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
@@ -439,14 +473,16 @@ function formatAuditReport(result) {
439
473
  lines.push(`
440
474
  OS Processes (${result.osProcesses.length} total)`);
441
475
  for (const p of result.osProcesses) {
442
- lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} ${p.command.substring(0, 55)}`);
476
+ lines.push(` 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)}`);
443
477
  }
444
478
  }
445
479
  if (result.orphans.length > 0) {
446
480
  lines.push(`
447
481
  \u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
448
482
  for (const p of result.orphans) {
449
- lines.push(` PID ${p.pid} \u2014 not tracked by PID file or workers registry`);
483
+ lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`);
484
+ lines.push(` Started: ${p.started}`);
485
+ lines.push(` Command: ${p.cmdline || p.command}`);
450
486
  }
451
487
  }
452
488
  lines.push("");
@@ -1061,7 +1097,9 @@ function render(config) {
1061
1097
  auditResult = auditProcesses(cachedAuditConfig);
1062
1098
  cachedAuditResult = auditResult;
1063
1099
  lastAuditTime = now;
1064
- } catch {
1100
+ } catch (err) {
1101
+ process.stderr.write(`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
1102
+ `);
1065
1103
  }
1066
1104
  }
1067
1105
  } else {
@@ -1181,6 +1219,15 @@ function render(config) {
1181
1219
  writeLine(L, ` ${C.red}\u2022${C.reset} ${C.dim}${p}${C.reset}`);
1182
1220
  }
1183
1221
  }
1222
+ if (ar.orphans.length > 0) {
1223
+ for (const o of ar.orphans.slice(0, 5)) {
1224
+ const cmd = (o.cmdline || o.command).substring(0, detailWidth);
1225
+ writeLine(L, ` ${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}`);
1226
+ }
1227
+ if (ar.orphans.length > 5) {
1228
+ writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
1229
+ }
1230
+ }
1184
1231
  }
1185
1232
  writeLine(L, "");
1186
1233
  writeLine(
package/dist/cli.cjs CHANGED
@@ -360,18 +360,36 @@ function auditWorkers(config) {
360
360
  return null;
361
361
  }
362
362
  }
363
+ function readCmdline(pid) {
364
+ try {
365
+ return (0, import_node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
366
+ } catch {
367
+ return "";
368
+ }
369
+ }
363
370
  function getOsProcesses(processName) {
364
371
  try {
365
- const raw = (0, import_node_child_process.execSync)(`ps aux`, { encoding: "utf8", timeout: 5e3 });
366
- return raw.split("\n").filter((line) => line.includes(processName) && !line.includes("process-audit") && !line.includes("grep")).map((line) => {
367
- const parts = line.trim().split(/\s+/);
368
- return {
369
- pid: parseInt(parts[1] ?? "0", 10),
370
- cpu: parts[2] ?? "0",
371
- mem: parts[3] ?? "0",
372
- command: parts.slice(10).join(" ")
373
- };
374
- }).filter((p) => !isNaN(p.pid) && p.pid > 0);
372
+ const raw = (0, import_node_child_process.execSync)(
373
+ `ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`,
374
+ { encoding: "utf8", timeout: 5e3 }
375
+ );
376
+ const results = [];
377
+ for (const line of raw.split("\n")) {
378
+ if (!line.includes(processName)) continue;
379
+ if (line.includes("process-audit") || line.includes(" grep ")) continue;
380
+ const trimmed = line.trim();
381
+ const parts = trimmed.split(/\s+/);
382
+ const pid = parseInt(parts[0] ?? "0", 10);
383
+ if (isNaN(pid) || pid <= 0) continue;
384
+ const cpu = parts[1] ?? "0";
385
+ const mem = parts[2] ?? "0";
386
+ const elapsed = parts[3] ?? "";
387
+ const started = parts.slice(4, 9).join(" ");
388
+ const command = parts.slice(9).join(" ");
389
+ const cmdline = readCmdline(pid);
390
+ results.push({ pid, cpu, mem, elapsed, started, command, cmdline });
391
+ }
392
+ return results;
375
393
  } catch {
376
394
  return [];
377
395
  }
@@ -411,7 +429,19 @@ function discoverProcessConfig(dirs) {
411
429
  }
412
430
  if (!processName && !pidFile && !workersFile) return null;
413
431
  if (!processName) processName = "agent";
414
- return { processName, pidFile, workersFile };
432
+ let systemdUnit;
433
+ try {
434
+ const unitName = `${processName}.service`;
435
+ const result = (0, import_node_child_process.execSync)(
436
+ `systemctl --user show ${unitName} --property=LoadState --no-pager 2>/dev/null`,
437
+ { encoding: "utf8", timeout: 3e3 }
438
+ );
439
+ if (result.includes("LoadState=loaded")) {
440
+ systemdUnit = unitName;
441
+ }
442
+ } catch {
443
+ }
444
+ return { processName, pidFile, workersFile, systemdUnit };
415
445
  }
416
446
  function auditProcesses(config) {
417
447
  const pidFile = auditPidFile(config);
@@ -427,7 +457,11 @@ function auditProcesses(config) {
427
457
  }
428
458
  }
429
459
  if (systemd?.mainPid) knownPids.add(systemd.mainPid);
430
- const orphans = osProcesses.filter((p) => !knownPids.has(p.pid));
460
+ const selfPid = process.pid;
461
+ const selfPpid = process.ppid;
462
+ const orphans = osProcesses.filter(
463
+ (p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
464
+ );
431
465
  const problems = [];
432
466
  if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
433
467
  if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
@@ -478,14 +512,16 @@ function formatAuditReport(result) {
478
512
  lines.push(`
479
513
  OS Processes (${result.osProcesses.length} total)`);
480
514
  for (const p of result.osProcesses) {
481
- lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} ${p.command.substring(0, 55)}`);
515
+ lines.push(` 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)}`);
482
516
  }
483
517
  }
484
518
  if (result.orphans.length > 0) {
485
519
  lines.push(`
486
520
  \u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
487
521
  for (const p of result.orphans) {
488
- lines.push(` PID ${p.pid} \u2014 not tracked by PID file or workers registry`);
522
+ lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`);
523
+ lines.push(` Started: ${p.started}`);
524
+ lines.push(` Command: ${p.cmdline || p.command}`);
489
525
  }
490
526
  }
491
527
  lines.push("");
@@ -1098,7 +1134,9 @@ function render(config) {
1098
1134
  auditResult = auditProcesses(cachedAuditConfig);
1099
1135
  cachedAuditResult = auditResult;
1100
1136
  lastAuditTime = now;
1101
- } catch {
1137
+ } catch (err) {
1138
+ process.stderr.write(`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
1139
+ `);
1102
1140
  }
1103
1141
  }
1104
1142
  } else {
@@ -1218,6 +1256,15 @@ function render(config) {
1218
1256
  writeLine(L, ` ${C.red}\u2022${C.reset} ${C.dim}${p}${C.reset}`);
1219
1257
  }
1220
1258
  }
1259
+ if (ar.orphans.length > 0) {
1260
+ for (const o of ar.orphans.slice(0, 5)) {
1261
+ const cmd = (o.cmdline || o.command).substring(0, detailWidth);
1262
+ writeLine(L, ` ${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}`);
1263
+ }
1264
+ if (ar.orphans.length > 5) {
1265
+ writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
1266
+ }
1267
+ }
1221
1268
  }
1222
1269
  writeLine(L, "");
1223
1270
  writeLine(
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  startWatch,
10
10
  toAsciiTree,
11
11
  toTimeline
12
- } from "./chunk-BOSYI5YM.js";
12
+ } from "./chunk-R5SNE2HB.js";
13
13
  import "./chunk-DY7YHFIB.js";
14
14
 
15
15
  // src/cli.ts
package/dist/index.cjs CHANGED
@@ -809,18 +809,36 @@ function auditWorkers(config) {
809
809
  return null;
810
810
  }
811
811
  }
812
+ function readCmdline(pid) {
813
+ try {
814
+ return (0, import_node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
815
+ } catch {
816
+ return "";
817
+ }
818
+ }
812
819
  function getOsProcesses(processName) {
813
820
  try {
814
- const raw = (0, import_node_child_process.execSync)(`ps aux`, { encoding: "utf8", timeout: 5e3 });
815
- return raw.split("\n").filter((line) => line.includes(processName) && !line.includes("process-audit") && !line.includes("grep")).map((line) => {
816
- const parts = line.trim().split(/\s+/);
817
- return {
818
- pid: parseInt(parts[1] ?? "0", 10),
819
- cpu: parts[2] ?? "0",
820
- mem: parts[3] ?? "0",
821
- command: parts.slice(10).join(" ")
822
- };
823
- }).filter((p) => !isNaN(p.pid) && p.pid > 0);
821
+ const raw = (0, import_node_child_process.execSync)(
822
+ `ps -eo pid,pcpu,pmem,etime,lstart,args --no-headers`,
823
+ { encoding: "utf8", timeout: 5e3 }
824
+ );
825
+ const results = [];
826
+ for (const line of raw.split("\n")) {
827
+ if (!line.includes(processName)) continue;
828
+ if (line.includes("process-audit") || line.includes(" grep ")) continue;
829
+ const trimmed = line.trim();
830
+ const parts = trimmed.split(/\s+/);
831
+ const pid = parseInt(parts[0] ?? "0", 10);
832
+ if (isNaN(pid) || pid <= 0) continue;
833
+ const cpu = parts[1] ?? "0";
834
+ const mem = parts[2] ?? "0";
835
+ const elapsed = parts[3] ?? "";
836
+ const started = parts.slice(4, 9).join(" ");
837
+ const command = parts.slice(9).join(" ");
838
+ const cmdline = readCmdline(pid);
839
+ results.push({ pid, cpu, mem, elapsed, started, command, cmdline });
840
+ }
841
+ return results;
824
842
  } catch {
825
843
  return [];
826
844
  }
@@ -860,7 +878,19 @@ function discoverProcessConfig(dirs) {
860
878
  }
861
879
  if (!processName && !pidFile && !workersFile) return null;
862
880
  if (!processName) processName = "agent";
863
- return { processName, pidFile, workersFile };
881
+ let systemdUnit;
882
+ try {
883
+ const unitName = `${processName}.service`;
884
+ const result = (0, import_node_child_process.execSync)(
885
+ `systemctl --user show ${unitName} --property=LoadState --no-pager 2>/dev/null`,
886
+ { encoding: "utf8", timeout: 3e3 }
887
+ );
888
+ if (result.includes("LoadState=loaded")) {
889
+ systemdUnit = unitName;
890
+ }
891
+ } catch {
892
+ }
893
+ return { processName, pidFile, workersFile, systemdUnit };
864
894
  }
865
895
  function auditProcesses(config) {
866
896
  const pidFile = auditPidFile(config);
@@ -876,7 +906,11 @@ function auditProcesses(config) {
876
906
  }
877
907
  }
878
908
  if (systemd?.mainPid) knownPids.add(systemd.mainPid);
879
- const orphans = osProcesses.filter((p) => !knownPids.has(p.pid));
909
+ const selfPid = process.pid;
910
+ const selfPpid = process.ppid;
911
+ const orphans = osProcesses.filter(
912
+ (p) => !knownPids.has(p.pid) && p.pid !== selfPid && p.pid !== selfPpid
913
+ );
880
914
  const problems = [];
881
915
  if (pidFile?.stale) problems.push(`Stale PID file: ${pidFile.reason}`);
882
916
  if (systemd?.crashLooping) problems.push("Systemd unit is crash-looping (auto-restart)");
@@ -927,14 +961,16 @@ function formatAuditReport(result) {
927
961
  lines.push(`
928
962
  OS Processes (${result.osProcesses.length} total)`);
929
963
  for (const p of result.osProcesses) {
930
- lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} ${p.command.substring(0, 55)}`);
964
+ lines.push(` 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)}`);
931
965
  }
932
966
  }
933
967
  if (result.orphans.length > 0) {
934
968
  lines.push(`
935
969
  \u26A0\uFE0F ${result.orphans.length} ORPHAN PROCESS(ES):`);
936
970
  for (const p of result.orphans) {
937
- lines.push(` PID ${p.pid} \u2014 not tracked by PID file or workers registry`);
971
+ lines.push(` PID ${String(p.pid).padEnd(8)} CPU=${p.cpu.padEnd(6)} MEM=${p.mem.padEnd(6)} Up=${p.elapsed}`);
972
+ lines.push(` Started: ${p.started}`);
973
+ lines.push(` Command: ${p.cmdline || p.command}`);
938
974
  }
939
975
  }
940
976
  lines.push("");
@@ -1547,7 +1583,9 @@ function render(config) {
1547
1583
  auditResult = auditProcesses(cachedAuditConfig);
1548
1584
  cachedAuditResult = auditResult;
1549
1585
  lastAuditTime = now;
1550
- } catch {
1586
+ } catch (err) {
1587
+ process.stderr.write(`[agentflow] process audit error: ${err instanceof Error ? err.message : err}
1588
+ `);
1551
1589
  }
1552
1590
  }
1553
1591
  } else {
@@ -1667,6 +1705,15 @@ function render(config) {
1667
1705
  writeLine(L, ` ${C.red}\u2022${C.reset} ${C.dim}${p}${C.reset}`);
1668
1706
  }
1669
1707
  }
1708
+ if (ar.orphans.length > 0) {
1709
+ for (const o of ar.orphans.slice(0, 5)) {
1710
+ const cmd = (o.cmdline || o.command).substring(0, detailWidth);
1711
+ writeLine(L, ` ${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}`);
1712
+ }
1713
+ if (ar.orphans.length > 5) {
1714
+ writeLine(L, ` ${C.dim}... +${ar.orphans.length - 5} more orphans${C.reset}`);
1715
+ }
1716
+ }
1670
1717
  }
1671
1718
  writeLine(L, "");
1672
1719
  writeLine(
package/dist/index.d.cts CHANGED
@@ -542,7 +542,13 @@ interface OsProcess {
542
542
  pid: number;
543
543
  cpu: string;
544
544
  mem: string;
545
+ /** Elapsed time since process started (e.g. "2:31:05", "03:14"). */
546
+ elapsed: string;
547
+ /** Process start time (e.g. "Mar18", "17:37"). */
548
+ started: string;
545
549
  command: string;
550
+ /** Full command line from /proc (Linux only). */
551
+ cmdline: string;
546
552
  }
547
553
  interface ProcessAuditResult {
548
554
  pidFile: PidFileResult | null;
package/dist/index.d.ts CHANGED
@@ -542,7 +542,13 @@ interface OsProcess {
542
542
  pid: number;
543
543
  cpu: string;
544
544
  mem: string;
545
+ /** Elapsed time since process started (e.g. "2:31:05", "03:14"). */
546
+ elapsed: string;
547
+ /** Process start time (e.g. "Mar18", "17:37"). */
548
+ started: string;
545
549
  command: string;
550
+ /** Full command line from /proc (Linux only). */
551
+ cmdline: string;
546
552
  }
547
553
  interface ProcessAuditResult {
548
554
  pidFile: PidFileResult | null;
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  stitchTrace,
24
24
  toAsciiTree,
25
25
  toTimeline
26
- } from "./chunk-BOSYI5YM.js";
26
+ } from "./chunk-R5SNE2HB.js";
27
27
  import {
28
28
  graphToJson,
29
29
  loadGraph
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentflow-core",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Monitor any AI agent system. Auto-detects failures, sends alerts, audits OS processes. Zero config, zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",