agentflow-core 0.8.0 → 0.8.1
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/README.md +99 -0
- package/dist/{chunk-DY7YHFIB.js → chunk-BYWLDTZK.js} +2 -1
- package/dist/{chunk-6X5HU5LB.js → chunk-NVFWBTAZ.js} +75 -37
- package/dist/cli.cjs +76 -38
- package/dist/cli.js +6 -6
- package/dist/index.cjs +945 -740
- package/dist/index.d.cts +344 -137
- package/dist/index.d.ts +344 -137
- package/dist/index.js +380 -215
- package/dist/{loader-LYRR6LMM.js → loader-JMFEFI3Q.js} +1 -1
- package/package.json +7 -3
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# agentflow-core
|
|
2
|
+
|
|
3
|
+
**v0.8.0** — Monitor any AI agent system. Auto-detects failures, sends alerts, audits OS processes. Zero config, zero dependencies.
|
|
4
|
+
|
|
5
|
+
Works with any agent framework: OpenAI, Anthropic, LangChain, CrewAI, AutoGen, or hand-rolled agents.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install agentflow-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js >= 20.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
### Step 1 — Build an execution graph
|
|
18
|
+
|
|
19
|
+
Wrap your agent's work with `createGraphBuilder`. Each logical unit of work is a node; the graph captures the full execution tree.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createGraphBuilder, getStats } from 'agentflow-core';
|
|
23
|
+
|
|
24
|
+
const builder = createGraphBuilder({ agentId: 'my-agent' });
|
|
25
|
+
|
|
26
|
+
const rootId = builder.startNode({ type: 'agent', name: 'main' });
|
|
27
|
+
const toolId = builder.startNode({ type: 'tool', name: 'fetch', parentId: rootId });
|
|
28
|
+
builder.endNode(toolId);
|
|
29
|
+
builder.endNode(rootId);
|
|
30
|
+
|
|
31
|
+
const graph = builder.build();
|
|
32
|
+
console.log(getStats(graph));
|
|
33
|
+
// { totalNodes: 2, failedNodes: 0, duration: 42, status: 'completed' }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Step 2 — Mine patterns across runs
|
|
37
|
+
|
|
38
|
+
Accumulate graphs over time and use process mining to find variants, bottlenecks, and conformance drift.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { discoverProcess, findVariants, getBottlenecks, checkConformance } from 'agentflow-core';
|
|
42
|
+
|
|
43
|
+
const model = discoverProcess(graphs); // build a process model from observed runs
|
|
44
|
+
const variants = findVariants(graphs); // group runs by their execution path
|
|
45
|
+
const bottlenecks = getBottlenecks(graphs); // rank nodes by cumulative wait time
|
|
46
|
+
const report = checkConformance(graph, model); // score a new run against the baseline
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 3 — Add guards
|
|
50
|
+
|
|
51
|
+
Guards detect runaway loops, spawn explosions, and policy violations at runtime. Wrap your builder with `withGuards` to activate them.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { createGraphBuilder, withGuards, createSomaPolicySource } from 'agentflow-core';
|
|
55
|
+
|
|
56
|
+
const raw = createGraphBuilder({ agentId: 'my-agent' });
|
|
57
|
+
const guarded = withGuards(raw, {
|
|
58
|
+
maxDepth: 8,
|
|
59
|
+
maxReasoningSteps: 20,
|
|
60
|
+
onViolation: 'warn', // 'warn' | 'error' | 'abort'
|
|
61
|
+
policySource: myPolicySource, // optional: adaptive thresholds from Soma
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API highlights
|
|
66
|
+
|
|
67
|
+
| Export | Kind | Description |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `createGraphBuilder` | factory | Build and mutate an execution graph during a run |
|
|
70
|
+
| `withGuards` | wrapper | Add runtime guard checks to any GraphBuilder |
|
|
71
|
+
| `checkGuards` | fn | Pure guard check on a graph snapshot |
|
|
72
|
+
| `getStats` | fn | Summary stats: node counts, status, duration |
|
|
73
|
+
| `getCriticalPath` | fn | Longest path through the graph by duration |
|
|
74
|
+
| `getFailures` | fn | All failed nodes with error metadata |
|
|
75
|
+
| `getHungNodes` | fn | Nodes that are running beyond their timeout |
|
|
76
|
+
| `discoverProcess` | fn | Build a process model from a run corpus |
|
|
77
|
+
| `findVariants` | fn | Group runs by execution path signature |
|
|
78
|
+
| `getBottlenecks` | fn | Rank nodes by cumulative elapsed time |
|
|
79
|
+
| `checkConformance` | fn | Score a run against a reference process model |
|
|
80
|
+
| `createInsightEngine` | factory | Tier-2 LLM analysis: anomaly, failure, and fix prompts |
|
|
81
|
+
| `createTraceStore` | factory | Persist and load graphs from disk |
|
|
82
|
+
| `createEventEmitter` | factory | Emit structured events during execution |
|
|
83
|
+
| `createJsonEventWriter` | factory | Write events to newline-delimited JSON |
|
|
84
|
+
| `createSomaEventWriter` | factory | Write events to a Soma inbox for ingestion |
|
|
85
|
+
| `createKnowledgeStore` | factory | Lightweight in-process key/value knowledge store |
|
|
86
|
+
| `createPolicySource` | factory | Static policy source for guard thresholds |
|
|
87
|
+
| `stitchTrace` | fn | Reconstruct a distributed trace from span events |
|
|
88
|
+
| `startLive` | fn | Live terminal monitor for a running agent |
|
|
89
|
+
| `startWatch` | fn | Headless watcher with alerting via notify channels |
|
|
90
|
+
| `auditProcesses` | fn | Audit OS processes, PIDs, and systemd units |
|
|
91
|
+
| `runTraced` | fn | Run a shell command with full execution tracing |
|
|
92
|
+
| `toAsciiTree` | fn | Render a graph as an ASCII tree |
|
|
93
|
+
| `toTimeline` | fn | Render a graph as a text timeline |
|
|
94
|
+
|
|
95
|
+
Full type definitions are bundled. All functions are pure unless noted as factory.
|
|
96
|
+
|
|
97
|
+
## Docs
|
|
98
|
+
|
|
99
|
+
[https://github.com/ClemenceChee/AgentFlow#readme](https://github.com/ClemenceChee/AgentFlow#readme)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
graphToJson,
|
|
3
3
|
loadGraph
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BYWLDTZK.js";
|
|
5
5
|
|
|
6
6
|
// src/graph-query.ts
|
|
7
7
|
function getNode(graph, nodeId) {
|
|
@@ -207,7 +207,7 @@ function getTraceTree(trace) {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
// src/process-audit.ts
|
|
210
|
-
import { execSync } from "child_process";
|
|
210
|
+
import { execFileSync, execSync } from "child_process";
|
|
211
211
|
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
212
212
|
import { basename, join } from "path";
|
|
213
213
|
function isPidAlive(pid) {
|
|
@@ -264,8 +264,15 @@ function auditSystemd(config) {
|
|
|
264
264
|
if (config.systemdUnit === null || config.systemdUnit === void 0) return null;
|
|
265
265
|
const unit = config.systemdUnit;
|
|
266
266
|
try {
|
|
267
|
-
const raw =
|
|
268
|
-
|
|
267
|
+
const raw = execFileSync(
|
|
268
|
+
"systemctl",
|
|
269
|
+
[
|
|
270
|
+
"--user",
|
|
271
|
+
"show",
|
|
272
|
+
unit,
|
|
273
|
+
"--property=ActiveState,SubState,MainPID,NRestarts,Result",
|
|
274
|
+
"--no-pager"
|
|
275
|
+
],
|
|
269
276
|
{ encoding: "utf8", timeout: 5e3 }
|
|
270
277
|
);
|
|
271
278
|
const props = {};
|
|
@@ -356,9 +363,11 @@ function getOsProcesses(processName) {
|
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
function discoverProcessConfig(dirs) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
366
|
+
const configs = discoverAllProcessConfigs(dirs);
|
|
367
|
+
return configs.length > 0 ? configs[0] ?? null : null;
|
|
368
|
+
}
|
|
369
|
+
function discoverAllProcessConfigs(dirs) {
|
|
370
|
+
const configs = /* @__PURE__ */ new Map();
|
|
362
371
|
for (const dir of dirs) {
|
|
363
372
|
if (!existsSync(dir)) continue;
|
|
364
373
|
let entries;
|
|
@@ -374,35 +383,63 @@ function discoverProcessConfig(dirs) {
|
|
|
374
383
|
} catch {
|
|
375
384
|
continue;
|
|
376
385
|
}
|
|
377
|
-
if (f.endsWith(".pid")
|
|
378
|
-
|
|
379
|
-
if (!
|
|
380
|
-
processName
|
|
386
|
+
if (f.endsWith(".pid")) {
|
|
387
|
+
const name = basename(f, ".pid");
|
|
388
|
+
if (!configs.has(name)) {
|
|
389
|
+
configs.set(name, { processName: name });
|
|
381
390
|
}
|
|
391
|
+
const cfg = configs.get(name) ?? { processName: name };
|
|
392
|
+
if (!cfg.pidFile) cfg.pidFile = fp;
|
|
382
393
|
}
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
if (
|
|
386
|
-
processName
|
|
394
|
+
if (f === "workers.json" || f.endsWith("-workers.json")) {
|
|
395
|
+
const name = f === "workers.json" ? "" : basename(f, "-workers.json");
|
|
396
|
+
if (name && !configs.has(name)) {
|
|
397
|
+
configs.set(name, { processName: name });
|
|
398
|
+
}
|
|
399
|
+
if (name) {
|
|
400
|
+
const cfg = configs.get(name) ?? { processName: name };
|
|
401
|
+
if (!cfg.workersFile) cfg.workersFile = fp;
|
|
387
402
|
}
|
|
388
403
|
}
|
|
389
404
|
}
|
|
390
405
|
}
|
|
391
|
-
if (!processName && !pidFile && !workersFile) return null;
|
|
392
|
-
if (!processName) processName = "agent";
|
|
393
|
-
let systemdUnit;
|
|
394
406
|
try {
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
407
|
+
const raw = execSync(
|
|
408
|
+
"systemctl --user list-units --type=service --all --no-legend --no-pager 2>/dev/null",
|
|
409
|
+
{ encoding: "utf8", timeout: 5e3 }
|
|
399
410
|
);
|
|
400
|
-
|
|
401
|
-
|
|
411
|
+
for (const line of raw.trim().split("\n")) {
|
|
412
|
+
if (!line.trim()) continue;
|
|
413
|
+
const parts = line.trim().split(/\s+/);
|
|
414
|
+
const unitName = parts[0] ?? "";
|
|
415
|
+
const loadState = parts[1] ?? "";
|
|
416
|
+
if (!unitName.endsWith(".service") || loadState !== "loaded") continue;
|
|
417
|
+
const name = unitName.replace(".service", "");
|
|
418
|
+
if (/^(dbus|gpg-agent|dirmngr|keyboxd|snapd\.|pk-|launchpadlib-)/.test(name)) continue;
|
|
419
|
+
if (!configs.has(name)) {
|
|
420
|
+
configs.set(name, { processName: name });
|
|
421
|
+
}
|
|
422
|
+
const cfg = configs.get(name) ?? { processName: name };
|
|
423
|
+
cfg.systemdUnit = unitName;
|
|
402
424
|
}
|
|
403
425
|
} catch {
|
|
404
426
|
}
|
|
405
|
-
|
|
427
|
+
for (const cfg of configs.values()) {
|
|
428
|
+
if (cfg.systemdUnit !== void 0) continue;
|
|
429
|
+
try {
|
|
430
|
+
const unitName = `${cfg.processName}.service`;
|
|
431
|
+
const result = execFileSync(
|
|
432
|
+
"systemctl",
|
|
433
|
+
["--user", "show", unitName, "--property=LoadState", "--no-pager"],
|
|
434
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
435
|
+
);
|
|
436
|
+
if (result.includes("LoadState=loaded")) {
|
|
437
|
+
cfg.systemdUnit = unitName;
|
|
438
|
+
}
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return [...configs.values()];
|
|
406
443
|
}
|
|
407
444
|
function auditProcesses(config) {
|
|
408
445
|
const pidFile = auditPidFile(config);
|
|
@@ -566,7 +603,7 @@ function parseArgs(argv) {
|
|
|
566
603
|
if (args[0] === "live") args.shift();
|
|
567
604
|
let i = 0;
|
|
568
605
|
while (i < args.length) {
|
|
569
|
-
const arg = args[i];
|
|
606
|
+
const arg = args[i] ?? "";
|
|
570
607
|
if (arg === "--help" || arg === "-h") {
|
|
571
608
|
printUsage();
|
|
572
609
|
process.exit(0);
|
|
@@ -873,7 +910,7 @@ function processJsonlFile(file) {
|
|
|
873
910
|
if (!content) return [];
|
|
874
911
|
const lines = content.split("\n");
|
|
875
912
|
const lineCount = lines.length;
|
|
876
|
-
const lastObj = JSON.parse(lines[lines.length - 1]);
|
|
913
|
+
const lastObj = JSON.parse(lines[lines.length - 1] ?? "{}");
|
|
877
914
|
const name = lastObj.jobId ?? lastObj.agentId ?? lastObj.name ?? lastObj.id ?? nameFromFile(file.filename);
|
|
878
915
|
if (lastObj.action !== void 0 || lastObj.jobId !== void 0) {
|
|
879
916
|
const status2 = findStatus(lastObj);
|
|
@@ -969,7 +1006,7 @@ function processJsonlFile(file) {
|
|
|
969
1006
|
}
|
|
970
1007
|
const parts = [];
|
|
971
1008
|
if (model) {
|
|
972
|
-
const shortModel = model.includes("/") ? model.split("/").pop() : model;
|
|
1009
|
+
const shortModel = model.includes("/") ? model.split("/").pop() ?? model : model;
|
|
973
1010
|
parts.push(shortModel.slice(0, 20));
|
|
974
1011
|
}
|
|
975
1012
|
if (toolCalls.length > 0) {
|
|
@@ -1134,15 +1171,15 @@ function render(config) {
|
|
|
1134
1171
|
if (age > 36e5 || age < 0) continue;
|
|
1135
1172
|
const idx = 11 - Math.floor(age / 3e5);
|
|
1136
1173
|
if (idx >= 0 && idx < 12) {
|
|
1137
|
-
buckets[idx]
|
|
1138
|
-
if (r.status === "error") failBuckets[idx]
|
|
1174
|
+
buckets[idx] = (buckets[idx] ?? 0) + 1;
|
|
1175
|
+
if (r.status === "error") failBuckets[idx] = (failBuckets[idx] ?? 0) + 1;
|
|
1139
1176
|
}
|
|
1140
1177
|
}
|
|
1141
1178
|
const maxBucket = Math.max(...buckets, 1);
|
|
1142
1179
|
const sparkChars = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1143
1180
|
const spark = buckets.map((v, i) => {
|
|
1144
1181
|
const level = Math.round(v / maxBucket * 8);
|
|
1145
|
-
return (failBuckets[i] > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
|
|
1182
|
+
return ((failBuckets[i] ?? 0) > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
|
|
1146
1183
|
}).join("");
|
|
1147
1184
|
let auditResult = null;
|
|
1148
1185
|
if (now - lastAuditTime > 1e4) {
|
|
@@ -1393,7 +1430,7 @@ function render(config) {
|
|
|
1393
1430
|
for (const d of config.dirs) writeLine(L, ` ${C.dim} ${d}${C.reset}`);
|
|
1394
1431
|
}
|
|
1395
1432
|
writeLine(L, "");
|
|
1396
|
-
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
1433
|
+
const dirLabel = config.dirs.length === 1 ? config.dirs[0] ?? "" : `${config.dirs.length} directories`;
|
|
1397
1434
|
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1398
1435
|
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1399
1436
|
flushLines(L);
|
|
@@ -2207,7 +2244,7 @@ function parseDuration(input) {
|
|
|
2207
2244
|
const n = parseInt(input, 10);
|
|
2208
2245
|
return Number.isNaN(n) ? 0 : n * 1e3;
|
|
2209
2246
|
}
|
|
2210
|
-
const value = parseFloat(match[1]);
|
|
2247
|
+
const value = parseFloat(match[1] ?? "0");
|
|
2211
2248
|
switch (match[2]?.toLowerCase()) {
|
|
2212
2249
|
case "s":
|
|
2213
2250
|
return value * 1e3;
|
|
@@ -2251,12 +2288,12 @@ function estimateInterval(history) {
|
|
|
2251
2288
|
const sorted = [...history].sort((a, b) => a - b);
|
|
2252
2289
|
const deltas = [];
|
|
2253
2290
|
for (let i = 1; i < sorted.length; i++) {
|
|
2254
|
-
const d = sorted[i] - sorted[i - 1];
|
|
2291
|
+
const d = (sorted[i] ?? 0) - (sorted[i - 1] ?? 0);
|
|
2255
2292
|
if (d > 0) deltas.push(d);
|
|
2256
2293
|
}
|
|
2257
2294
|
if (deltas.length === 0) return 0;
|
|
2258
2295
|
deltas.sort((a, b) => a - b);
|
|
2259
|
-
return deltas[Math.floor(deltas.length / 2)];
|
|
2296
|
+
return deltas[Math.floor(deltas.length / 2)] ?? 0;
|
|
2260
2297
|
}
|
|
2261
2298
|
function detectTransitions(previous, currentRecords, config, now) {
|
|
2262
2299
|
const alerts = [];
|
|
@@ -2413,7 +2450,7 @@ function parseWatchArgs(argv) {
|
|
|
2413
2450
|
if (args[0] === "watch") args.shift();
|
|
2414
2451
|
let i = 0;
|
|
2415
2452
|
while (i < args.length) {
|
|
2416
|
-
const arg = args[i];
|
|
2453
|
+
const arg = args[i] ?? "";
|
|
2417
2454
|
if (arg === "--help" || arg === "-h") {
|
|
2418
2455
|
printWatchUsage();
|
|
2419
2456
|
process.exit(0);
|
|
@@ -2482,7 +2519,7 @@ function parseWatchArgs(argv) {
|
|
|
2482
2519
|
}
|
|
2483
2520
|
notifyChannels.unshift({ type: "stdout" });
|
|
2484
2521
|
if (!stateFilePath) {
|
|
2485
|
-
stateFilePath = join5(dirs[0], ".agentflow-watch-state.json");
|
|
2522
|
+
stateFilePath = join5(dirs[0] ?? ".", ".agentflow-watch-state.json");
|
|
2486
2523
|
}
|
|
2487
2524
|
return {
|
|
2488
2525
|
dirs,
|
|
@@ -2643,6 +2680,7 @@ export {
|
|
|
2643
2680
|
stitchTrace,
|
|
2644
2681
|
getTraceTree,
|
|
2645
2682
|
discoverProcessConfig,
|
|
2683
|
+
discoverAllProcessConfigs,
|
|
2646
2684
|
auditProcesses,
|
|
2647
2685
|
formatAuditReport,
|
|
2648
2686
|
startLive,
|
package/dist/cli.cjs
CHANGED
|
@@ -62,7 +62,8 @@ function loadGraph(input) {
|
|
|
62
62
|
events: raw.events ?? [],
|
|
63
63
|
traceId: raw.traceId,
|
|
64
64
|
spanId: raw.spanId,
|
|
65
|
-
parentSpanId: raw.parentSpanId
|
|
65
|
+
parentSpanId: raw.parentSpanId,
|
|
66
|
+
metadata: raw.metadata ?? {}
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
69
|
function graphToJson(graph) {
|
|
@@ -303,8 +304,15 @@ function auditSystemd(config) {
|
|
|
303
304
|
if (config.systemdUnit === null || config.systemdUnit === void 0) return null;
|
|
304
305
|
const unit = config.systemdUnit;
|
|
305
306
|
try {
|
|
306
|
-
const raw = (0, import_node_child_process.
|
|
307
|
-
|
|
307
|
+
const raw = (0, import_node_child_process.execFileSync)(
|
|
308
|
+
"systemctl",
|
|
309
|
+
[
|
|
310
|
+
"--user",
|
|
311
|
+
"show",
|
|
312
|
+
unit,
|
|
313
|
+
"--property=ActiveState,SubState,MainPID,NRestarts,Result",
|
|
314
|
+
"--no-pager"
|
|
315
|
+
],
|
|
308
316
|
{ encoding: "utf8", timeout: 5e3 }
|
|
309
317
|
);
|
|
310
318
|
const props = {};
|
|
@@ -395,9 +403,11 @@ function getOsProcesses(processName) {
|
|
|
395
403
|
}
|
|
396
404
|
}
|
|
397
405
|
function discoverProcessConfig(dirs) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
406
|
+
const configs = discoverAllProcessConfigs(dirs);
|
|
407
|
+
return configs.length > 0 ? configs[0] ?? null : null;
|
|
408
|
+
}
|
|
409
|
+
function discoverAllProcessConfigs(dirs) {
|
|
410
|
+
const configs = /* @__PURE__ */ new Map();
|
|
401
411
|
for (const dir of dirs) {
|
|
402
412
|
if (!(0, import_node_fs.existsSync)(dir)) continue;
|
|
403
413
|
let entries;
|
|
@@ -413,35 +423,63 @@ function discoverProcessConfig(dirs) {
|
|
|
413
423
|
} catch {
|
|
414
424
|
continue;
|
|
415
425
|
}
|
|
416
|
-
if (f.endsWith(".pid")
|
|
417
|
-
|
|
418
|
-
if (!
|
|
419
|
-
|
|
426
|
+
if (f.endsWith(".pid")) {
|
|
427
|
+
const name = (0, import_node_path.basename)(f, ".pid");
|
|
428
|
+
if (!configs.has(name)) {
|
|
429
|
+
configs.set(name, { processName: name });
|
|
420
430
|
}
|
|
431
|
+
const cfg = configs.get(name) ?? { processName: name };
|
|
432
|
+
if (!cfg.pidFile) cfg.pidFile = fp;
|
|
421
433
|
}
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
if (
|
|
425
|
-
|
|
434
|
+
if (f === "workers.json" || f.endsWith("-workers.json")) {
|
|
435
|
+
const name = f === "workers.json" ? "" : (0, import_node_path.basename)(f, "-workers.json");
|
|
436
|
+
if (name && !configs.has(name)) {
|
|
437
|
+
configs.set(name, { processName: name });
|
|
438
|
+
}
|
|
439
|
+
if (name) {
|
|
440
|
+
const cfg = configs.get(name) ?? { processName: name };
|
|
441
|
+
if (!cfg.workersFile) cfg.workersFile = fp;
|
|
426
442
|
}
|
|
427
443
|
}
|
|
428
444
|
}
|
|
429
445
|
}
|
|
430
|
-
if (!processName && !pidFile && !workersFile) return null;
|
|
431
|
-
if (!processName) processName = "agent";
|
|
432
|
-
let systemdUnit;
|
|
433
446
|
try {
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
447
|
+
const raw = (0, import_node_child_process.execSync)(
|
|
448
|
+
"systemctl --user list-units --type=service --all --no-legend --no-pager 2>/dev/null",
|
|
449
|
+
{ encoding: "utf8", timeout: 5e3 }
|
|
438
450
|
);
|
|
439
|
-
|
|
440
|
-
|
|
451
|
+
for (const line of raw.trim().split("\n")) {
|
|
452
|
+
if (!line.trim()) continue;
|
|
453
|
+
const parts = line.trim().split(/\s+/);
|
|
454
|
+
const unitName = parts[0] ?? "";
|
|
455
|
+
const loadState = parts[1] ?? "";
|
|
456
|
+
if (!unitName.endsWith(".service") || loadState !== "loaded") continue;
|
|
457
|
+
const name = unitName.replace(".service", "");
|
|
458
|
+
if (/^(dbus|gpg-agent|dirmngr|keyboxd|snapd\.|pk-|launchpadlib-)/.test(name)) continue;
|
|
459
|
+
if (!configs.has(name)) {
|
|
460
|
+
configs.set(name, { processName: name });
|
|
461
|
+
}
|
|
462
|
+
const cfg = configs.get(name) ?? { processName: name };
|
|
463
|
+
cfg.systemdUnit = unitName;
|
|
441
464
|
}
|
|
442
465
|
} catch {
|
|
443
466
|
}
|
|
444
|
-
|
|
467
|
+
for (const cfg of configs.values()) {
|
|
468
|
+
if (cfg.systemdUnit !== void 0) continue;
|
|
469
|
+
try {
|
|
470
|
+
const unitName = `${cfg.processName}.service`;
|
|
471
|
+
const result = (0, import_node_child_process.execFileSync)(
|
|
472
|
+
"systemctl",
|
|
473
|
+
["--user", "show", unitName, "--property=LoadState", "--no-pager"],
|
|
474
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
475
|
+
);
|
|
476
|
+
if (result.includes("LoadState=loaded")) {
|
|
477
|
+
cfg.systemdUnit = unitName;
|
|
478
|
+
}
|
|
479
|
+
} catch {
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return [...configs.values()];
|
|
445
483
|
}
|
|
446
484
|
function auditProcesses(config) {
|
|
447
485
|
const pidFile = auditPidFile(config);
|
|
@@ -603,7 +641,7 @@ function parseArgs(argv) {
|
|
|
603
641
|
if (args[0] === "live") args.shift();
|
|
604
642
|
let i = 0;
|
|
605
643
|
while (i < args.length) {
|
|
606
|
-
const arg = args[i];
|
|
644
|
+
const arg = args[i] ?? "";
|
|
607
645
|
if (arg === "--help" || arg === "-h") {
|
|
608
646
|
printUsage();
|
|
609
647
|
process.exit(0);
|
|
@@ -910,7 +948,7 @@ function processJsonlFile(file) {
|
|
|
910
948
|
if (!content) return [];
|
|
911
949
|
const lines = content.split("\n");
|
|
912
950
|
const lineCount = lines.length;
|
|
913
|
-
const lastObj = JSON.parse(lines[lines.length - 1]);
|
|
951
|
+
const lastObj = JSON.parse(lines[lines.length - 1] ?? "{}");
|
|
914
952
|
const name = lastObj.jobId ?? lastObj.agentId ?? lastObj.name ?? lastObj.id ?? nameFromFile(file.filename);
|
|
915
953
|
if (lastObj.action !== void 0 || lastObj.jobId !== void 0) {
|
|
916
954
|
const status2 = findStatus(lastObj);
|
|
@@ -1006,7 +1044,7 @@ function processJsonlFile(file) {
|
|
|
1006
1044
|
}
|
|
1007
1045
|
const parts = [];
|
|
1008
1046
|
if (model) {
|
|
1009
|
-
const shortModel = model.includes("/") ? model.split("/").pop() : model;
|
|
1047
|
+
const shortModel = model.includes("/") ? model.split("/").pop() ?? model : model;
|
|
1010
1048
|
parts.push(shortModel.slice(0, 20));
|
|
1011
1049
|
}
|
|
1012
1050
|
if (toolCalls.length > 0) {
|
|
@@ -1171,15 +1209,15 @@ function render(config) {
|
|
|
1171
1209
|
if (age > 36e5 || age < 0) continue;
|
|
1172
1210
|
const idx = 11 - Math.floor(age / 3e5);
|
|
1173
1211
|
if (idx >= 0 && idx < 12) {
|
|
1174
|
-
buckets[idx]
|
|
1175
|
-
if (r.status === "error") failBuckets[idx]
|
|
1212
|
+
buckets[idx] = (buckets[idx] ?? 0) + 1;
|
|
1213
|
+
if (r.status === "error") failBuckets[idx] = (failBuckets[idx] ?? 0) + 1;
|
|
1176
1214
|
}
|
|
1177
1215
|
}
|
|
1178
1216
|
const maxBucket = Math.max(...buckets, 1);
|
|
1179
1217
|
const sparkChars = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1180
1218
|
const spark = buckets.map((v, i) => {
|
|
1181
1219
|
const level = Math.round(v / maxBucket * 8);
|
|
1182
|
-
return (failBuckets[i] > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
|
|
1220
|
+
return ((failBuckets[i] ?? 0) > 0 ? C.red : C.green) + sparkChars[level] + C.reset;
|
|
1183
1221
|
}).join("");
|
|
1184
1222
|
let auditResult = null;
|
|
1185
1223
|
if (now - lastAuditTime > 1e4) {
|
|
@@ -1430,7 +1468,7 @@ function render(config) {
|
|
|
1430
1468
|
for (const d of config.dirs) writeLine(L, ` ${C.dim} ${d}${C.reset}`);
|
|
1431
1469
|
}
|
|
1432
1470
|
writeLine(L, "");
|
|
1433
|
-
const dirLabel = config.dirs.length === 1 ? config.dirs[0] : `${config.dirs.length} directories`;
|
|
1471
|
+
const dirLabel = config.dirs.length === 1 ? config.dirs[0] ?? "" : `${config.dirs.length} directories`;
|
|
1434
1472
|
writeLine(L, ` ${C.dim}Watching: ${dirLabel}${C.reset}`);
|
|
1435
1473
|
writeLine(L, ` ${C.dim}Press Ctrl+C to exit${C.reset}`);
|
|
1436
1474
|
flushLines(L);
|
|
@@ -2440,7 +2478,7 @@ function parseDuration(input) {
|
|
|
2440
2478
|
const n = parseInt(input, 10);
|
|
2441
2479
|
return Number.isNaN(n) ? 0 : n * 1e3;
|
|
2442
2480
|
}
|
|
2443
|
-
const value = parseFloat(match[1]);
|
|
2481
|
+
const value = parseFloat(match[1] ?? "0");
|
|
2444
2482
|
switch (match[2]?.toLowerCase()) {
|
|
2445
2483
|
case "s":
|
|
2446
2484
|
return value * 1e3;
|
|
@@ -2484,12 +2522,12 @@ function estimateInterval(history) {
|
|
|
2484
2522
|
const sorted = [...history].sort((a, b) => a - b);
|
|
2485
2523
|
const deltas = [];
|
|
2486
2524
|
for (let i = 1; i < sorted.length; i++) {
|
|
2487
|
-
const d = sorted[i] - sorted[i - 1];
|
|
2525
|
+
const d = (sorted[i] ?? 0) - (sorted[i - 1] ?? 0);
|
|
2488
2526
|
if (d > 0) deltas.push(d);
|
|
2489
2527
|
}
|
|
2490
2528
|
if (deltas.length === 0) return 0;
|
|
2491
2529
|
deltas.sort((a, b) => a - b);
|
|
2492
|
-
return deltas[Math.floor(deltas.length / 2)];
|
|
2530
|
+
return deltas[Math.floor(deltas.length / 2)] ?? 0;
|
|
2493
2531
|
}
|
|
2494
2532
|
function detectTransitions(previous, currentRecords, config, now) {
|
|
2495
2533
|
const alerts = [];
|
|
@@ -2646,7 +2684,7 @@ function parseWatchArgs(argv) {
|
|
|
2646
2684
|
if (args[0] === "watch") args.shift();
|
|
2647
2685
|
let i = 0;
|
|
2648
2686
|
while (i < args.length) {
|
|
2649
|
-
const arg = args[i];
|
|
2687
|
+
const arg = args[i] ?? "";
|
|
2650
2688
|
if (arg === "--help" || arg === "-h") {
|
|
2651
2689
|
printWatchUsage();
|
|
2652
2690
|
process.exit(0);
|
|
@@ -2715,7 +2753,7 @@ function parseWatchArgs(argv) {
|
|
|
2715
2753
|
}
|
|
2716
2754
|
notifyChannels.unshift({ type: "stdout" });
|
|
2717
2755
|
if (!stateFilePath) {
|
|
2718
|
-
stateFilePath = (0, import_node_path6.join)(dirs[0], ".agentflow-watch-state.json");
|
|
2756
|
+
stateFilePath = (0, import_node_path6.join)(dirs[0] ?? ".", ".agentflow-watch-state.json");
|
|
2719
2757
|
}
|
|
2720
2758
|
return {
|
|
2721
2759
|
dirs,
|
|
@@ -3036,7 +3074,7 @@ function parseAuditArgs(argv) {
|
|
|
3036
3074
|
if (args[0] === "audit") args.shift();
|
|
3037
3075
|
let i = 0;
|
|
3038
3076
|
while (i < args.length) {
|
|
3039
|
-
const arg = args[i];
|
|
3077
|
+
const arg = args[i] ?? "";
|
|
3040
3078
|
if (arg === "--help" || arg === "-h") {
|
|
3041
3079
|
printAuditUsage();
|
|
3042
3080
|
process.exit(0);
|
|
@@ -3126,7 +3164,7 @@ function runAudit(argv) {
|
|
|
3126
3164
|
async function main() {
|
|
3127
3165
|
const argv = process.argv.slice(2);
|
|
3128
3166
|
const knownCommands = ["run", "live", "watch", "trace", "audit"];
|
|
3129
|
-
if (argv.length === 0 || !knownCommands.includes(argv[0]) && (argv.includes("--help") || argv.includes("-h"))) {
|
|
3167
|
+
if (argv.length === 0 || !knownCommands.includes(argv[0] ?? "") && (argv.includes("--help") || argv.includes("-h"))) {
|
|
3130
3168
|
printHelp();
|
|
3131
3169
|
process.exit(0);
|
|
3132
3170
|
}
|
package/dist/cli.js
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
startWatch,
|
|
10
10
|
toAsciiTree,
|
|
11
11
|
toTimeline
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-NVFWBTAZ.js";
|
|
13
|
+
import "./chunk-BYWLDTZK.js";
|
|
14
14
|
|
|
15
15
|
// src/cli.ts
|
|
16
16
|
import { basename, resolve as resolve2 } from "path";
|
|
@@ -95,7 +95,7 @@ async function traceShow(argv) {
|
|
|
95
95
|
const { join } = await import("path");
|
|
96
96
|
const fname = graphId.endsWith(".json") ? graphId : `${graphId}.json`;
|
|
97
97
|
try {
|
|
98
|
-
const { loadGraph } = await import("./loader-
|
|
98
|
+
const { loadGraph } = await import("./loader-JMFEFI3Q.js");
|
|
99
99
|
const content = await readFile(join(dir, fname), "utf-8");
|
|
100
100
|
graph = loadGraph(content);
|
|
101
101
|
} catch {
|
|
@@ -124,7 +124,7 @@ async function traceTimeline(argv) {
|
|
|
124
124
|
const { join } = await import("path");
|
|
125
125
|
const fname = graphId.endsWith(".json") ? graphId : `${graphId}.json`;
|
|
126
126
|
try {
|
|
127
|
-
const { loadGraph } = await import("./loader-
|
|
127
|
+
const { loadGraph } = await import("./loader-JMFEFI3Q.js");
|
|
128
128
|
const content = await readFile(join(dir, fname), "utf-8");
|
|
129
129
|
graph = loadGraph(content);
|
|
130
130
|
} catch {
|
|
@@ -381,7 +381,7 @@ function parseAuditArgs(argv) {
|
|
|
381
381
|
if (args[0] === "audit") args.shift();
|
|
382
382
|
let i = 0;
|
|
383
383
|
while (i < args.length) {
|
|
384
|
-
const arg = args[i];
|
|
384
|
+
const arg = args[i] ?? "";
|
|
385
385
|
if (arg === "--help" || arg === "-h") {
|
|
386
386
|
printAuditUsage();
|
|
387
387
|
process.exit(0);
|
|
@@ -471,7 +471,7 @@ function runAudit(argv) {
|
|
|
471
471
|
async function main() {
|
|
472
472
|
const argv = process.argv.slice(2);
|
|
473
473
|
const knownCommands = ["run", "live", "watch", "trace", "audit"];
|
|
474
|
-
if (argv.length === 0 || !knownCommands.includes(argv[0]) && (argv.includes("--help") || argv.includes("-h"))) {
|
|
474
|
+
if (argv.length === 0 || !knownCommands.includes(argv[0] ?? "") && (argv.includes("--help") || argv.includes("-h"))) {
|
|
475
475
|
printHelp();
|
|
476
476
|
process.exit(0);
|
|
477
477
|
}
|