agentflow-dashboard 0.8.3 → 0.8.4
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-E5RJCBK2.js → chunk-YLQ5MVCW.js} +204 -100
- package/dist/cli.cjs +193 -82
- package/dist/cli.js +1 -1
- package/dist/client/assets/index-BQBa4cES.css +1 -0
- package/dist/client/assets/{index-BgEw2MGK.js → index-DA9m90ZC.js} +7 -7
- package/dist/client/index.html +2 -2
- package/dist/index.cjs +193 -82
- package/dist/index.js +1 -1
- package/dist/server.cjs +193 -82
- package/dist/server.js +1 -1
- package/package.json +21 -7
- package/dist/client/assets/index-DHcSpTgM.css +0 -1
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/server.ts
|
|
9
|
-
import { execSync } from "child_process";
|
|
2
|
+
import { execFileSync, execSync } from "child_process";
|
|
10
3
|
import * as fs3 from "fs";
|
|
11
4
|
import { createServer } from "http";
|
|
12
5
|
import * as path3 from "path";
|
|
13
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
+
import {
|
|
8
|
+
auditProcesses,
|
|
9
|
+
createExecutionEvent,
|
|
10
|
+
createKnowledgeStore,
|
|
11
|
+
discoverAllProcessConfigs,
|
|
12
|
+
discoverProcess,
|
|
13
|
+
findVariants,
|
|
14
|
+
getBottlenecks,
|
|
15
|
+
loadGraph as loadGraph2
|
|
16
|
+
} from "agentflow-core";
|
|
17
|
+
import chokidar2 from "chokidar";
|
|
18
|
+
import express from "express";
|
|
19
|
+
import rateLimit from "express-rate-limit";
|
|
20
|
+
import { WebSocketServer } from "ws";
|
|
14
21
|
|
|
15
22
|
// src/config.ts
|
|
16
23
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -83,21 +90,6 @@ function getProcessPreference(config) {
|
|
|
83
90
|
return config.processPreference ?? null;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
// src/server.ts
|
|
87
|
-
import {
|
|
88
|
-
auditProcesses,
|
|
89
|
-
createExecutionEvent,
|
|
90
|
-
createKnowledgeStore,
|
|
91
|
-
discoverAllProcessConfigs,
|
|
92
|
-
discoverProcess,
|
|
93
|
-
findVariants,
|
|
94
|
-
getBottlenecks,
|
|
95
|
-
loadGraph as loadGraph2
|
|
96
|
-
} from "agentflow-core";
|
|
97
|
-
import chokidar2 from "chokidar";
|
|
98
|
-
import express from "express";
|
|
99
|
-
import { WebSocketServer } from "ws";
|
|
100
|
-
|
|
101
93
|
// src/adapters/agentflow.ts
|
|
102
94
|
var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
103
95
|
"workers.json",
|
|
@@ -109,7 +101,14 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
|
109
101
|
"models.json",
|
|
110
102
|
"config.json"
|
|
111
103
|
]);
|
|
112
|
-
var SKIP_SUFFIXES = [
|
|
104
|
+
var SKIP_SUFFIXES = [
|
|
105
|
+
"-state.json",
|
|
106
|
+
"-config.json",
|
|
107
|
+
"-watch-state.json",
|
|
108
|
+
".tmp",
|
|
109
|
+
".bak",
|
|
110
|
+
".backup"
|
|
111
|
+
];
|
|
113
112
|
var AgentFlowAdapter = class {
|
|
114
113
|
name = "agentflow";
|
|
115
114
|
detect(_dirPath) {
|
|
@@ -234,7 +233,7 @@ var OpenClawAdapter = class {
|
|
|
234
233
|
};
|
|
235
234
|
|
|
236
235
|
// src/adapters/otel.ts
|
|
237
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3
|
|
236
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
238
237
|
import { join as join3 } from "path";
|
|
239
238
|
var SPAN_TYPE_MAP = {
|
|
240
239
|
"gen_ai.chat": "llm",
|
|
@@ -386,8 +385,14 @@ registerAdapter(new AgentFlowAdapter());
|
|
|
386
385
|
var PURPOSE_KEYWORDS = [
|
|
387
386
|
{ keywords: ["email", "mail", "inbox", "smtp"], group: "Email Processors" },
|
|
388
387
|
{ keywords: ["monitor", "watch", "alert", "surveillance"], group: "Monitors" },
|
|
389
|
-
{
|
|
390
|
-
|
|
388
|
+
{
|
|
389
|
+
keywords: ["digest", "newsletter", "summary", "report", "briefing"],
|
|
390
|
+
group: "Digests & Reports"
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
keywords: ["curator", "janitor", "distiller", "surveyor", "worker", "indexer"],
|
|
394
|
+
group: "Workers"
|
|
395
|
+
},
|
|
391
396
|
{ keywords: ["cron", "schedule", "timer", "periodic"], group: "Scheduled Jobs" },
|
|
392
397
|
{ keywords: ["search", "scrape", "crawl", "fetch"], group: "Data Collection" },
|
|
393
398
|
{ keywords: ["embed", "vector", "index"], group: "Embeddings" }
|
|
@@ -419,6 +424,7 @@ function capitalize(s) {
|
|
|
419
424
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
420
425
|
}
|
|
421
426
|
function deduplicateAgents(agents) {
|
|
427
|
+
var _a, _b, _c, _d;
|
|
422
428
|
const tagged = agents.map((a) => ({
|
|
423
429
|
...a,
|
|
424
430
|
...extractSource(a.agentId)
|
|
@@ -435,14 +441,14 @@ function deduplicateAgents(agents) {
|
|
|
435
441
|
const mergedIds = /* @__PURE__ */ new Set();
|
|
436
442
|
const mergedAgents = [];
|
|
437
443
|
for (const [_key, group] of suffixGroups) {
|
|
438
|
-
const suffix = extractSuffix(group[0].localId);
|
|
444
|
+
const suffix = extractSuffix((_a = group[0]) == null ? void 0 : _a.localId);
|
|
439
445
|
if (group.length < 2) continue;
|
|
440
446
|
const prefixes = new Set(group.map((a) => a.localId.split("-")[0]));
|
|
441
447
|
if (prefixes.size < 2) continue;
|
|
442
448
|
const longPrefixes = [...prefixes].filter((p) => p !== suffix && p.length > 2);
|
|
443
449
|
if (longPrefixes.length >= 2) continue;
|
|
444
450
|
const merged = {
|
|
445
|
-
agentId: group[0].source === "agentflow" ? suffix : `${group[0].source}:${suffix}`,
|
|
451
|
+
agentId: ((_b = group[0]) == null ? void 0 : _b.source) === "agentflow" ? suffix : `${(_c = group[0]) == null ? void 0 : _c.source}:${suffix}`,
|
|
446
452
|
displayName: suffix,
|
|
447
453
|
totalExecutions: group.reduce((s, a) => s + a.totalExecutions, 0),
|
|
448
454
|
successfulExecutions: group.reduce((s, a) => s + a.successfulExecutions, 0),
|
|
@@ -453,7 +459,7 @@ function deduplicateAgents(agents) {
|
|
|
453
459
|
triggers: {},
|
|
454
460
|
recentActivity: group.flatMap((a) => a.recentActivity).sort((a, b) => b.timestamp - a.timestamp).slice(0, 50),
|
|
455
461
|
sources: group.map((a) => a.agentId),
|
|
456
|
-
adapterSource: group[0].source
|
|
462
|
+
adapterSource: (_d = group[0]) == null ? void 0 : _d.source
|
|
457
463
|
};
|
|
458
464
|
merged.successRate = merged.totalExecutions > 0 ? merged.successfulExecutions / merged.totalExecutions * 100 : 0;
|
|
459
465
|
const totalExecTime = group.reduce((s, a) => s + a.avgExecutionTime * a.totalExecutions, 0);
|
|
@@ -869,7 +875,9 @@ var TraceWatcher = class _TraceWatcher extends EventEmitter {
|
|
|
869
875
|
...getSkipFiles(this.userConfig)
|
|
870
876
|
]);
|
|
871
877
|
this.userSkipDirs = new Set(getSkipDirectories(this.userConfig));
|
|
872
|
-
this.allWatchDirs = [
|
|
878
|
+
this.allWatchDirs = [
|
|
879
|
+
...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))
|
|
880
|
+
];
|
|
873
881
|
this.ensureTracesDir();
|
|
874
882
|
this.loadExistingFiles();
|
|
875
883
|
this.archiveOldTraces();
|
|
@@ -879,7 +887,7 @@ var TraceWatcher = class _TraceWatcher extends EventEmitter {
|
|
|
879
887
|
/** Move trace files older than maxAgeMs into archive/YYYY-MM/ subdirectories. */
|
|
880
888
|
archiveOldTraces() {
|
|
881
889
|
const cutoff = Date.now() - this.maxAgeMs;
|
|
882
|
-
|
|
890
|
+
const _archived = 0;
|
|
883
891
|
for (const dir of this.allWatchDirs) {
|
|
884
892
|
if (!fs.existsSync(dir)) continue;
|
|
885
893
|
try {
|
|
@@ -896,7 +904,8 @@ var TraceWatcher = class _TraceWatcher extends EventEmitter {
|
|
|
896
904
|
try {
|
|
897
905
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
898
906
|
for (const entry of entries) {
|
|
899
|
-
if (entry.name.startsWith(".") || entry.name === "archive" || this.userSkipDirs.has(entry.name))
|
|
907
|
+
if (entry.name.startsWith(".") || entry.name === "archive" || this.userSkipDirs.has(entry.name))
|
|
908
|
+
continue;
|
|
900
909
|
const fullPath = path.join(dir, entry.name);
|
|
901
910
|
if (entry.isDirectory()) {
|
|
902
911
|
archived += this.archiveDirectory(fullPath, cutoff, depth + 1);
|
|
@@ -2142,7 +2151,9 @@ import * as os from "os";
|
|
|
2142
2151
|
import * as path2 from "path";
|
|
2143
2152
|
import { fileURLToPath } from "url";
|
|
2144
2153
|
var __cliDirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
2145
|
-
var VERSION = JSON.parse(
|
|
2154
|
+
var VERSION = JSON.parse(
|
|
2155
|
+
fs2.readFileSync(path2.resolve(__cliDirname, "../package.json"), "utf-8")
|
|
2156
|
+
).version;
|
|
2146
2157
|
function getLanAddress() {
|
|
2147
2158
|
const interfaces = os.networkInterfaces();
|
|
2148
2159
|
for (const name of Object.keys(interfaces)) {
|
|
@@ -2399,6 +2410,17 @@ var DashboardServer = class {
|
|
|
2399
2410
|
userConfig;
|
|
2400
2411
|
configPath;
|
|
2401
2412
|
setupExpress() {
|
|
2413
|
+
this.app.use(
|
|
2414
|
+
"/api/",
|
|
2415
|
+
rateLimit({
|
|
2416
|
+
windowMs: 60 * 1e3,
|
|
2417
|
+
// 1 minute
|
|
2418
|
+
max: 300,
|
|
2419
|
+
// 300 requests per minute per IP
|
|
2420
|
+
standardHeaders: true,
|
|
2421
|
+
legacyHeaders: false
|
|
2422
|
+
})
|
|
2423
|
+
);
|
|
2402
2424
|
if (this.config.enableCors) {
|
|
2403
2425
|
this.app.use((_req, res, next) => {
|
|
2404
2426
|
res.header("Access-Control-Allow-Origin", "*");
|
|
@@ -2641,6 +2663,7 @@ var DashboardServer = class {
|
|
|
2641
2663
|
}
|
|
2642
2664
|
});
|
|
2643
2665
|
this.app.get("/api/process-model/:agentId", (req, res) => {
|
|
2666
|
+
var _a, _b;
|
|
2644
2667
|
try {
|
|
2645
2668
|
const agentId = req.params.agentId;
|
|
2646
2669
|
const allTraces = this.watcher.getTracesByAgent(agentId);
|
|
@@ -2658,8 +2681,8 @@ var DashboardServer = class {
|
|
|
2658
2681
|
const nodeArr = Object.values(nodes);
|
|
2659
2682
|
const sorted = nodeArr.filter((n) => n.name && typeof n.startTime === "number" && n.startTime > 0).sort((a, b) => (a.startTime ?? 0) - (b.startTime ?? 0));
|
|
2660
2683
|
for (let i = 0; i < sorted.length - 1; i++) {
|
|
2661
|
-
const from = sorted[i].name;
|
|
2662
|
-
const to = sorted[i + 1].name;
|
|
2684
|
+
const from = (_a = sorted[i]) == null ? void 0 : _a.name;
|
|
2685
|
+
const to = (_b = sorted[i + 1]) == null ? void 0 : _b.name;
|
|
2663
2686
|
const key = `${from}|||${to}`;
|
|
2664
2687
|
transMap.set(key, (transMap.get(key) ?? 0) + 1);
|
|
2665
2688
|
}
|
|
@@ -2758,7 +2781,11 @@ var DashboardServer = class {
|
|
|
2758
2781
|
try {
|
|
2759
2782
|
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2760
2783
|
if (!fs3.existsSync(reportPath)) {
|
|
2761
|
-
return res.json({
|
|
2784
|
+
return res.json({
|
|
2785
|
+
available: false,
|
|
2786
|
+
teaser: false,
|
|
2787
|
+
message: "No report file yet. Run soma watch."
|
|
2788
|
+
});
|
|
2762
2789
|
}
|
|
2763
2790
|
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2764
2791
|
res.json(report);
|
|
@@ -2782,7 +2809,9 @@ var DashboardServer = class {
|
|
|
2782
2809
|
available: true,
|
|
2783
2810
|
layers: report.layers ?? { archive: 0, working: 0, emerging: 0, canon: 0 },
|
|
2784
2811
|
governance: report.governance ?? { pending: 0, promoted: 0, rejected: 0 },
|
|
2785
|
-
insights: (report.insights ?? []).filter(
|
|
2812
|
+
insights: (report.insights ?? []).filter(
|
|
2813
|
+
(i) => i.layer === "emerging" && i.proposal_status === "pending"
|
|
2814
|
+
),
|
|
2786
2815
|
canon: (report.insights ?? []).filter((i) => i.layer === "canon"),
|
|
2787
2816
|
generatedAt: report.generatedAt
|
|
2788
2817
|
});
|
|
@@ -2791,21 +2820,23 @@ var DashboardServer = class {
|
|
|
2791
2820
|
res.status(500).json({ available: false, message: "Failed to read governance data" });
|
|
2792
2821
|
}
|
|
2793
2822
|
});
|
|
2794
|
-
const
|
|
2795
|
-
const sanitizeReason = (s) => s.replace(/["`$\\]/g, "").slice(0, 500);
|
|
2823
|
+
const isValidId = (s) => /^[a-zA-Z0-9_\-.:]+$/.test(s);
|
|
2796
2824
|
this.app.post("/api/soma/governance/promote", (req, res) => {
|
|
2797
2825
|
var _a;
|
|
2798
2826
|
const somaVault = this.config.somaVault;
|
|
2799
2827
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2800
2828
|
const { entryId } = req.body ?? {};
|
|
2801
|
-
if (!entryId
|
|
2829
|
+
if (!entryId || !isValidId(String(entryId)))
|
|
2830
|
+
return res.status(400).json({ error: "Invalid entryId" });
|
|
2802
2831
|
try {
|
|
2803
|
-
const
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2832
|
+
const result = execFileSync(
|
|
2833
|
+
"npx",
|
|
2834
|
+
["soma", "governance", "promote", String(entryId), "--vault", somaVault],
|
|
2835
|
+
{
|
|
2836
|
+
encoding: "utf-8",
|
|
2837
|
+
timeout: 1e4
|
|
2838
|
+
}
|
|
2839
|
+
);
|
|
2809
2840
|
res.json({ success: true, message: result.trim() });
|
|
2810
2841
|
} catch (error) {
|
|
2811
2842
|
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2816,15 +2847,27 @@ var DashboardServer = class {
|
|
|
2816
2847
|
const somaVault = this.config.somaVault;
|
|
2817
2848
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2818
2849
|
const { entryId, reason } = req.body ?? {};
|
|
2819
|
-
if (!entryId || !
|
|
2850
|
+
if (!entryId || !isValidId(String(entryId)))
|
|
2851
|
+
return res.status(400).json({ error: "Invalid entryId" });
|
|
2852
|
+
if (!reason || typeof reason !== "string")
|
|
2853
|
+
return res.status(400).json({ error: "reason required" });
|
|
2820
2854
|
try {
|
|
2821
|
-
const
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2855
|
+
const result = execFileSync(
|
|
2856
|
+
"npx",
|
|
2857
|
+
[
|
|
2858
|
+
"soma",
|
|
2859
|
+
"governance",
|
|
2860
|
+
"reject",
|
|
2861
|
+
String(entryId),
|
|
2862
|
+
String(reason).slice(0, 500),
|
|
2863
|
+
"--vault",
|
|
2864
|
+
somaVault
|
|
2865
|
+
],
|
|
2866
|
+
{
|
|
2867
|
+
encoding: "utf-8",
|
|
2868
|
+
timeout: 1e4
|
|
2869
|
+
}
|
|
2870
|
+
);
|
|
2828
2871
|
res.json({ success: true, message: result.trim() });
|
|
2829
2872
|
} catch (error) {
|
|
2830
2873
|
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2834,13 +2877,16 @@ var DashboardServer = class {
|
|
|
2834
2877
|
var _a;
|
|
2835
2878
|
const somaVault = this.config.somaVault;
|
|
2836
2879
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2880
|
+
if (!isValidId(String(req.params.id))) return res.status(400).json({ error: "Invalid id" });
|
|
2837
2881
|
try {
|
|
2838
|
-
const
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2882
|
+
const result = execFileSync(
|
|
2883
|
+
"npx",
|
|
2884
|
+
["soma", "governance", "show", String(req.params.id), "--vault", somaVault],
|
|
2885
|
+
{
|
|
2886
|
+
encoding: "utf-8",
|
|
2887
|
+
timeout: 1e4
|
|
2888
|
+
}
|
|
2889
|
+
);
|
|
2844
2890
|
res.json({ available: true, output: result.trim() });
|
|
2845
2891
|
} catch (error) {
|
|
2846
2892
|
res.status(404).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2863,16 +2909,24 @@ var DashboardServer = class {
|
|
|
2863
2909
|
const somaVault = this.config.somaVault;
|
|
2864
2910
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2865
2911
|
const { name, enforcement, scope, conditions } = req.body ?? {};
|
|
2866
|
-
if (!name
|
|
2912
|
+
if (!name || !isValidId(String(name)))
|
|
2913
|
+
return res.status(400).json({ error: "Invalid policy name" });
|
|
2914
|
+
const enf = String(enforcement || "warn");
|
|
2915
|
+
if (!isValidId(enf)) return res.status(400).json({ error: "Invalid enforcement value" });
|
|
2867
2916
|
try {
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2917
|
+
const args = [
|
|
2918
|
+
"soma",
|
|
2919
|
+
"policy",
|
|
2920
|
+
"create",
|
|
2921
|
+
String(name),
|
|
2922
|
+
"--enforcement",
|
|
2923
|
+
enf,
|
|
2924
|
+
"--vault",
|
|
2925
|
+
somaVault
|
|
2926
|
+
];
|
|
2927
|
+
if (scope) args.push("--scope", String(scope).slice(0, 500));
|
|
2928
|
+
if (conditions) args.push("--conditions", String(conditions).slice(0, 500));
|
|
2929
|
+
const result = execFileSync("npx", args, { encoding: "utf-8", timeout: 1e4 });
|
|
2876
2930
|
res.json({ success: true, message: result.trim() });
|
|
2877
2931
|
} catch (error) {
|
|
2878
2932
|
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2882,11 +2936,16 @@ var DashboardServer = class {
|
|
|
2882
2936
|
var _a;
|
|
2883
2937
|
const somaVault = this.config.somaVault;
|
|
2884
2938
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2939
|
+
if (!isValidId(String(req.params.name)))
|
|
2940
|
+
return res.status(400).json({ error: "Invalid policy name" });
|
|
2885
2941
|
try {
|
|
2886
|
-
const
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
{
|
|
2942
|
+
const result = execFileSync(
|
|
2943
|
+
"npx",
|
|
2944
|
+
["soma", "policy", "delete", String(req.params.name), "--vault", somaVault],
|
|
2945
|
+
{
|
|
2946
|
+
encoding: "utf-8",
|
|
2947
|
+
timeout: 1e4
|
|
2948
|
+
}
|
|
2890
2949
|
);
|
|
2891
2950
|
res.json({ success: true, message: result.trim() });
|
|
2892
2951
|
} catch (error) {
|
|
@@ -2904,16 +2963,28 @@ var DashboardServer = class {
|
|
|
2904
2963
|
...(report.agents ?? []).map((a) => ({ ...a, type: "agent", id: a.name })),
|
|
2905
2964
|
...(report.insights ?? []).map((i, idx) => {
|
|
2906
2965
|
var _a;
|
|
2907
|
-
return {
|
|
2966
|
+
return {
|
|
2967
|
+
...i,
|
|
2968
|
+
type: i.type || "insight",
|
|
2969
|
+
id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}`
|
|
2970
|
+
};
|
|
2908
2971
|
}),
|
|
2909
2972
|
...(report.policies ?? []).map((p) => ({ ...p, type: "policy", id: p.name }))
|
|
2910
2973
|
];
|
|
2911
|
-
const {
|
|
2974
|
+
const {
|
|
2975
|
+
type,
|
|
2976
|
+
layer,
|
|
2977
|
+
q,
|
|
2978
|
+
limit: limitStr,
|
|
2979
|
+
offset: offsetStr
|
|
2980
|
+
} = req.query;
|
|
2912
2981
|
if (type) entities = entities.filter((e) => e.type === type);
|
|
2913
2982
|
if (layer) entities = entities.filter((e) => e.layer === layer);
|
|
2914
2983
|
if (q) {
|
|
2915
2984
|
const lq = q.toLowerCase();
|
|
2916
|
-
entities = entities.filter(
|
|
2985
|
+
entities = entities.filter(
|
|
2986
|
+
(e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq)
|
|
2987
|
+
);
|
|
2917
2988
|
}
|
|
2918
2989
|
const total = entities.length;
|
|
2919
2990
|
const offset = parseInt(offsetStr || "0", 10);
|
|
@@ -3004,9 +3075,7 @@ var DashboardServer = class {
|
|
|
3004
3075
|
const orphans = uniqueProcesses.filter(
|
|
3005
3076
|
(p) => !allKnownPids.has(p.pid) && p.pid !== process.pid && p.pid !== process.ppid
|
|
3006
3077
|
);
|
|
3007
|
-
const problems = services.flatMap(
|
|
3008
|
-
(s) => s.audit.problems.map((p) => `[${s.name}] ${p}`)
|
|
3009
|
-
);
|
|
3078
|
+
const problems = services.flatMap((s) => s.audit.problems.map((p) => `[${s.name}] ${p}`));
|
|
3010
3079
|
const result = {
|
|
3011
3080
|
// Backward-compatible fields from primary service
|
|
3012
3081
|
pidFile: (primary == null ? void 0 : primary.audit.pidFile) ?? null,
|
|
@@ -3061,19 +3130,24 @@ var DashboardServer = class {
|
|
|
3061
3130
|
}
|
|
3062
3131
|
} catch {
|
|
3063
3132
|
}
|
|
3064
|
-
const watched = [
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3133
|
+
const watched = [
|
|
3134
|
+
...new Set(
|
|
3135
|
+
[this.config.tracesDir, ...this.config.dataDirs || [], ...extraDirs].map(
|
|
3136
|
+
(w) => path3.resolve(w)
|
|
3137
|
+
)
|
|
3138
|
+
)
|
|
3139
|
+
];
|
|
3069
3140
|
const discovered = [];
|
|
3070
3141
|
const svcNames = getSystemdServices(this.userConfig);
|
|
3071
3142
|
if (svcNames.length > 0) {
|
|
3072
3143
|
try {
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
{
|
|
3144
|
+
const raw = execFileSync(
|
|
3145
|
+
"systemctl",
|
|
3146
|
+
["--user", "show", "--property=ExecStart", "--no-pager", ...svcNames],
|
|
3147
|
+
{
|
|
3148
|
+
encoding: "utf8",
|
|
3149
|
+
timeout: 5e3
|
|
3150
|
+
}
|
|
3077
3151
|
);
|
|
3078
3152
|
for (const line of raw.split("\n")) {
|
|
3079
3153
|
const match = line.match(/path=([^\s;]+)/);
|
|
@@ -3105,10 +3179,19 @@ var DashboardServer = class {
|
|
|
3105
3179
|
this.app.post("/api/directories", express.json(), (req, res) => {
|
|
3106
3180
|
try {
|
|
3107
3181
|
const { add, remove } = req.body;
|
|
3108
|
-
if (add
|
|
3109
|
-
|
|
3182
|
+
if (add) {
|
|
3183
|
+
const resolved = path3.resolve(add);
|
|
3184
|
+
if (resolved !== add || add.includes("..")) {
|
|
3185
|
+
return res.status(400).json({ error: "Invalid directory path" });
|
|
3186
|
+
}
|
|
3187
|
+
if (!fs3.existsSync(resolved)) {
|
|
3188
|
+
return res.status(400).json({ error: `Directory does not exist: ${add}` });
|
|
3189
|
+
}
|
|
3110
3190
|
}
|
|
3111
|
-
const configPath = path3.join(
|
|
3191
|
+
const configPath = path3.join(
|
|
3192
|
+
process.env.HOME ?? "/home/trader",
|
|
3193
|
+
".agentflow/dashboard-config.json"
|
|
3194
|
+
);
|
|
3112
3195
|
let config = {};
|
|
3113
3196
|
try {
|
|
3114
3197
|
if (fs3.existsSync(configPath)) {
|
|
@@ -3318,13 +3401,31 @@ var DashboardServer = class {
|
|
|
3318
3401
|
isVirtual: false
|
|
3319
3402
|
});
|
|
3320
3403
|
}
|
|
3321
|
-
const
|
|
3322
|
-
const
|
|
3323
|
-
const
|
|
3324
|
-
for (const
|
|
3325
|
-
}
|
|
3326
|
-
nodes.push({
|
|
3327
|
-
|
|
3404
|
+
const _rootSteps = new Set(model.steps);
|
|
3405
|
+
const _childSteps = new Set(model.transitions.map((t) => t.to));
|
|
3406
|
+
const _leafSteps = new Set(model.steps);
|
|
3407
|
+
for (const _t of model.transitions) {
|
|
3408
|
+
}
|
|
3409
|
+
nodes.push({
|
|
3410
|
+
id: "[START]",
|
|
3411
|
+
label: "[START]",
|
|
3412
|
+
count: model.totalGraphs,
|
|
3413
|
+
frequency: 1,
|
|
3414
|
+
avgDuration: 0,
|
|
3415
|
+
failRate: 0,
|
|
3416
|
+
p95Duration: 0,
|
|
3417
|
+
isVirtual: true
|
|
3418
|
+
});
|
|
3419
|
+
nodes.push({
|
|
3420
|
+
id: "[END]",
|
|
3421
|
+
label: "[END]",
|
|
3422
|
+
count: model.totalGraphs,
|
|
3423
|
+
frequency: 1,
|
|
3424
|
+
avgDuration: 0,
|
|
3425
|
+
failRate: 0,
|
|
3426
|
+
p95Duration: 0,
|
|
3427
|
+
isVirtual: true
|
|
3428
|
+
});
|
|
3328
3429
|
const edges = model.transitions.map((t) => ({
|
|
3329
3430
|
source: t.from,
|
|
3330
3431
|
target: t.to,
|
|
@@ -3344,7 +3445,10 @@ var DashboardServer = class {
|
|
|
3344
3445
|
}
|
|
3345
3446
|
}
|
|
3346
3447
|
const maxEdgeCount = Math.max(...edges.map((e) => e.count), 1);
|
|
3347
|
-
const maxNodeCount = Math.max(
|
|
3448
|
+
const maxNodeCount = Math.max(
|
|
3449
|
+
...nodes.filter((n) => !n.isVirtual).map((n) => n.count),
|
|
3450
|
+
1
|
|
3451
|
+
);
|
|
3348
3452
|
return { agentId, totalTraces: model.totalGraphs, nodes, edges, maxEdgeCount, maxNodeCount };
|
|
3349
3453
|
}
|
|
3350
3454
|
/**
|