agentflow-dashboard 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/dist/{chunk-NZFXRZYU.js → chunk-JRVE5NM3.js} +176 -4
- package/dist/cli.cjs +176 -4
- package/dist/cli.js +1 -1
- package/dist/client/assets/index-CyQ7qX-x.js +50 -0
- package/dist/client/assets/{index-CNZqCErb.css → index-DHcSpTgM.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/index.cjs +176 -4
- package/dist/index.js +1 -1
- package/dist/server.cjs +176 -4
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/client/assets/index-Cb5C1Pah.js +0 -50
|
@@ -94,6 +94,7 @@ import {
|
|
|
94
94
|
getBottlenecks,
|
|
95
95
|
loadGraph as loadGraph2
|
|
96
96
|
} from "agentflow-core";
|
|
97
|
+
import chokidar2 from "chokidar";
|
|
97
98
|
import express from "express";
|
|
98
99
|
import { WebSocketServer } from "ws";
|
|
99
100
|
|
|
@@ -868,7 +869,7 @@ var TraceWatcher = class _TraceWatcher extends EventEmitter {
|
|
|
868
869
|
...getSkipFiles(this.userConfig)
|
|
869
870
|
]);
|
|
870
871
|
this.userSkipDirs = new Set(getSkipDirectories(this.userConfig));
|
|
871
|
-
this.allWatchDirs = [this.tracesDir, ...this.dataDirs];
|
|
872
|
+
this.allWatchDirs = [...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))];
|
|
872
873
|
this.ensureTracesDir();
|
|
873
874
|
this.loadExistingFiles();
|
|
874
875
|
this.archiveOldTraces();
|
|
@@ -2172,7 +2173,9 @@ function printBanner(config, traceCount, stats, configPath) {
|
|
|
2172
2173
|
\u2192 http://localhost:${port}${isPublic && lan ? `
|
|
2173
2174
|
\u2192 http://${lan}:${port} (LAN)` : ""}
|
|
2174
2175
|
|
|
2175
|
-
|
|
2176
|
+
Pages: Agents \xB7 SOMA
|
|
2177
|
+
Agent: Profile \xB7 Execution Detail
|
|
2178
|
+
SOMA: Intelligence \xB7 Review \xB7 Policies \xB7 Knowledge \xB7 Activity
|
|
2176
2179
|
Tabs: Flame Chart \xB7 Agent Flow \xB7 Metrics \xB7 Dependencies
|
|
2177
2180
|
State Machine \xB7 Summary \xB7 Transcript
|
|
2178
2181
|
|
|
@@ -2357,6 +2360,7 @@ var DashboardServer = class {
|
|
|
2357
2360
|
this.setupExpress();
|
|
2358
2361
|
this.setupWebSocket();
|
|
2359
2362
|
this.setupTraceWatcher();
|
|
2363
|
+
this.setupSomaReportWatcher();
|
|
2360
2364
|
let knowledgeCount = 0;
|
|
2361
2365
|
for (const trace of this.watcher.getAllTraces()) {
|
|
2362
2366
|
this.stats.processTrace(trace);
|
|
@@ -2715,6 +2719,27 @@ var DashboardServer = class {
|
|
|
2715
2719
|
res.status(500).json({ error: "Failed to load agent statistics" });
|
|
2716
2720
|
}
|
|
2717
2721
|
});
|
|
2722
|
+
this.app.get("/api/soma/tier", (_req, res) => {
|
|
2723
|
+
const somaVault = this.config.somaVault;
|
|
2724
|
+
if (!somaVault) {
|
|
2725
|
+
return res.json({ tier: "teaser", somaVault: false, governanceAvailable: false });
|
|
2726
|
+
}
|
|
2727
|
+
try {
|
|
2728
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2729
|
+
if (!fs3.existsSync(reportPath)) {
|
|
2730
|
+
return res.json({ tier: "free", somaVault: true, governanceAvailable: false });
|
|
2731
|
+
}
|
|
2732
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2733
|
+
const hasGovernance = report.governance && typeof report.governance.pending === "number";
|
|
2734
|
+
return res.json({
|
|
2735
|
+
tier: hasGovernance ? "pro" : "free",
|
|
2736
|
+
somaVault: true,
|
|
2737
|
+
governanceAvailable: !!hasGovernance
|
|
2738
|
+
});
|
|
2739
|
+
} catch {
|
|
2740
|
+
return res.json({ tier: "free", somaVault: true, governanceAvailable: false });
|
|
2741
|
+
}
|
|
2742
|
+
});
|
|
2718
2743
|
this.app.get("/api/soma/report", (_req, res) => {
|
|
2719
2744
|
const somaVault = this.config.somaVault;
|
|
2720
2745
|
if (!somaVault) {
|
|
@@ -2811,6 +2836,118 @@ var DashboardServer = class {
|
|
|
2811
2836
|
res.status(404).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2812
2837
|
}
|
|
2813
2838
|
});
|
|
2839
|
+
this.app.get("/api/soma/policies", (_req, res) => {
|
|
2840
|
+
const somaVault = this.config.somaVault;
|
|
2841
|
+
if (!somaVault) return res.json({ policies: [] });
|
|
2842
|
+
try {
|
|
2843
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2844
|
+
if (!fs3.existsSync(reportPath)) return res.json({ policies: [] });
|
|
2845
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2846
|
+
res.json({ policies: report.policies ?? [] });
|
|
2847
|
+
} catch {
|
|
2848
|
+
res.json({ policies: [] });
|
|
2849
|
+
}
|
|
2850
|
+
});
|
|
2851
|
+
this.app.post("/api/soma/policies", express.json(), (req, res) => {
|
|
2852
|
+
var _a;
|
|
2853
|
+
const somaVault = this.config.somaVault;
|
|
2854
|
+
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2855
|
+
const { name, enforcement, scope, conditions } = req.body ?? {};
|
|
2856
|
+
if (!name) return res.status(400).json({ error: "name required" });
|
|
2857
|
+
try {
|
|
2858
|
+
const safeName = sanitizeArg(String(name));
|
|
2859
|
+
const safeEnf = sanitizeArg(String(enforcement || "warn"));
|
|
2860
|
+
const safeScope = sanitizeReason(String(scope || "all"));
|
|
2861
|
+
const safeCond = sanitizeReason(String(conditions || ""));
|
|
2862
|
+
const result = execSync(
|
|
2863
|
+
`npx soma policy create "${safeName}" --enforcement ${safeEnf} --scope "${safeScope}" --conditions "${safeCond}" --vault "${somaVault}"`,
|
|
2864
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
2865
|
+
);
|
|
2866
|
+
res.json({ success: true, message: result.trim() });
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2869
|
+
}
|
|
2870
|
+
});
|
|
2871
|
+
this.app.delete("/api/soma/policies/:name", (req, res) => {
|
|
2872
|
+
var _a;
|
|
2873
|
+
const somaVault = this.config.somaVault;
|
|
2874
|
+
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2875
|
+
try {
|
|
2876
|
+
const safeName = sanitizeArg(String(req.params.name));
|
|
2877
|
+
const result = execSync(
|
|
2878
|
+
`npx soma policy delete "${safeName}" --vault "${somaVault}"`,
|
|
2879
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
2880
|
+
);
|
|
2881
|
+
res.json({ success: true, message: result.trim() });
|
|
2882
|
+
} catch (error) {
|
|
2883
|
+
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2884
|
+
}
|
|
2885
|
+
});
|
|
2886
|
+
this.app.get("/api/soma/vault/entities", (req, res) => {
|
|
2887
|
+
const somaVault = this.config.somaVault;
|
|
2888
|
+
if (!somaVault) return res.json({ entities: [], total: 0 });
|
|
2889
|
+
try {
|
|
2890
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2891
|
+
if (!fs3.existsSync(reportPath)) return res.json({ entities: [], total: 0 });
|
|
2892
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2893
|
+
let entities = [
|
|
2894
|
+
...(report.agents ?? []).map((a) => ({ ...a, type: "agent", id: a.name })),
|
|
2895
|
+
...(report.insights ?? []).map((i, idx) => {
|
|
2896
|
+
var _a;
|
|
2897
|
+
return { ...i, type: i.type || "insight", id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}` };
|
|
2898
|
+
}),
|
|
2899
|
+
...(report.policies ?? []).map((p) => ({ ...p, type: "policy", id: p.name }))
|
|
2900
|
+
];
|
|
2901
|
+
const { type, layer, q, limit: limitStr, offset: offsetStr } = req.query;
|
|
2902
|
+
if (type) entities = entities.filter((e) => e.type === type);
|
|
2903
|
+
if (layer) entities = entities.filter((e) => e.layer === layer);
|
|
2904
|
+
if (q) {
|
|
2905
|
+
const lq = q.toLowerCase();
|
|
2906
|
+
entities = entities.filter((e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq));
|
|
2907
|
+
}
|
|
2908
|
+
const total = entities.length;
|
|
2909
|
+
const offset = parseInt(offsetStr || "0", 10);
|
|
2910
|
+
const limit = Math.min(parseInt(limitStr || "50", 10), 200);
|
|
2911
|
+
entities = entities.slice(offset, offset + limit);
|
|
2912
|
+
res.json({ entities, total });
|
|
2913
|
+
} catch (error) {
|
|
2914
|
+
console.error("Vault entities error:", error);
|
|
2915
|
+
res.json({ entities: [], total: 0 });
|
|
2916
|
+
}
|
|
2917
|
+
});
|
|
2918
|
+
this.app.get("/api/soma/vault/entities/:type/:id", (req, res) => {
|
|
2919
|
+
const somaVault = this.config.somaVault;
|
|
2920
|
+
if (!somaVault) return res.status(404).json({ error: "Soma vault not configured" });
|
|
2921
|
+
try {
|
|
2922
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2923
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2924
|
+
const { type, id } = req.params;
|
|
2925
|
+
let entity = null;
|
|
2926
|
+
if (type === "agent") {
|
|
2927
|
+
entity = (report.agents ?? []).find((a) => a.name === id);
|
|
2928
|
+
} else if (type === "policy") {
|
|
2929
|
+
entity = (report.policies ?? []).find((p) => p.name === id);
|
|
2930
|
+
} else {
|
|
2931
|
+
entity = (report.insights ?? []).find(
|
|
2932
|
+
(i) => {
|
|
2933
|
+
var _a;
|
|
2934
|
+
return (((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || "") === id || i.title === id;
|
|
2935
|
+
}
|
|
2936
|
+
);
|
|
2937
|
+
}
|
|
2938
|
+
if (!entity) return res.status(404).json({ error: "Entity not found" });
|
|
2939
|
+
res.json({
|
|
2940
|
+
...entity,
|
|
2941
|
+
type,
|
|
2942
|
+
id,
|
|
2943
|
+
body: entity.claim || entity.conditions || "",
|
|
2944
|
+
tags: entity.tags ?? [],
|
|
2945
|
+
related: entity.related ?? []
|
|
2946
|
+
});
|
|
2947
|
+
} catch {
|
|
2948
|
+
res.status(404).json({ error: "Entity not found" });
|
|
2949
|
+
}
|
|
2950
|
+
});
|
|
2814
2951
|
this.app.get("/api/process-health", (_req, res) => {
|
|
2815
2952
|
var _a, _b;
|
|
2816
2953
|
try {
|
|
@@ -2914,11 +3051,11 @@ var DashboardServer = class {
|
|
|
2914
3051
|
}
|
|
2915
3052
|
} catch {
|
|
2916
3053
|
}
|
|
2917
|
-
const watched = [
|
|
3054
|
+
const watched = [...new Set([
|
|
2918
3055
|
this.config.tracesDir,
|
|
2919
3056
|
...this.config.dataDirs || [],
|
|
2920
3057
|
...extraDirs
|
|
2921
|
-
];
|
|
3058
|
+
].map((w) => path3.resolve(w)))];
|
|
2922
3059
|
const discovered = [];
|
|
2923
3060
|
const svcNames = getSystemdServices(this.userConfig);
|
|
2924
3061
|
if (svcNames.length > 0) {
|
|
@@ -3072,6 +3209,41 @@ var DashboardServer = class {
|
|
|
3072
3209
|
});
|
|
3073
3210
|
});
|
|
3074
3211
|
}
|
|
3212
|
+
/** Watch soma-report.json for changes and broadcast updates via WebSocket. */
|
|
3213
|
+
setupSomaReportWatcher() {
|
|
3214
|
+
const somaVault = this.config.somaVault;
|
|
3215
|
+
if (!somaVault) return;
|
|
3216
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
3217
|
+
const reportDir = path3.dirname(reportPath);
|
|
3218
|
+
if (!fs3.existsSync(reportDir)) return;
|
|
3219
|
+
let debounceTimer = null;
|
|
3220
|
+
const watcher = chokidar2.watch(reportPath, {
|
|
3221
|
+
ignoreInitial: true,
|
|
3222
|
+
persistent: true,
|
|
3223
|
+
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
3224
|
+
});
|
|
3225
|
+
watcher.on("change", () => {
|
|
3226
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
3227
|
+
debounceTimer = setTimeout(() => {
|
|
3228
|
+
var _a, _b;
|
|
3229
|
+
try {
|
|
3230
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
3231
|
+
this.broadcast({ type: "soma-report-updated", data: report });
|
|
3232
|
+
if (report.generatedAt) {
|
|
3233
|
+
this.broadcast({
|
|
3234
|
+
type: "soma-activity",
|
|
3235
|
+
data: {
|
|
3236
|
+
action: "report-updated",
|
|
3237
|
+
description: `Report updated: ${((_a = report.totals) == null ? void 0 : _a.agents) ?? 0} agents, ${((_b = report.totals) == null ? void 0 : _b.insights) ?? 0} insights`,
|
|
3238
|
+
timestamp: report.generatedAt
|
|
3239
|
+
}
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
} catch {
|
|
3243
|
+
}
|
|
3244
|
+
}, 500);
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3075
3247
|
/**
|
|
3076
3248
|
* Filter an agent's traces to valid ExecutionGraphs and convert via loadGraph().
|
|
3077
3249
|
* Returns only traces with proper nodes (Map or non-empty object), skipping session-only traces.
|
package/dist/cli.cjs
CHANGED
|
@@ -116,6 +116,7 @@ function getProcessPreference(config) {
|
|
|
116
116
|
|
|
117
117
|
// src/server.ts
|
|
118
118
|
var import_agentflow_core3 = require("agentflow-core");
|
|
119
|
+
var import_chokidar2 = __toESM(require("chokidar"), 1);
|
|
119
120
|
var import_express = __toESM(require("express"), 1);
|
|
120
121
|
var import_ws = require("ws");
|
|
121
122
|
|
|
@@ -890,7 +891,7 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
890
891
|
...getSkipFiles(this.userConfig)
|
|
891
892
|
]);
|
|
892
893
|
this.userSkipDirs = new Set(getSkipDirectories(this.userConfig));
|
|
893
|
-
this.allWatchDirs = [this.tracesDir, ...this.dataDirs];
|
|
894
|
+
this.allWatchDirs = [...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))];
|
|
894
895
|
this.ensureTracesDir();
|
|
895
896
|
this.loadExistingFiles();
|
|
896
897
|
this.archiveOldTraces();
|
|
@@ -2202,6 +2203,7 @@ var DashboardServer = class {
|
|
|
2202
2203
|
this.setupExpress();
|
|
2203
2204
|
this.setupWebSocket();
|
|
2204
2205
|
this.setupTraceWatcher();
|
|
2206
|
+
this.setupSomaReportWatcher();
|
|
2205
2207
|
let knowledgeCount = 0;
|
|
2206
2208
|
for (const trace of this.watcher.getAllTraces()) {
|
|
2207
2209
|
this.stats.processTrace(trace);
|
|
@@ -2560,6 +2562,27 @@ var DashboardServer = class {
|
|
|
2560
2562
|
res.status(500).json({ error: "Failed to load agent statistics" });
|
|
2561
2563
|
}
|
|
2562
2564
|
});
|
|
2565
|
+
this.app.get("/api/soma/tier", (_req, res) => {
|
|
2566
|
+
const somaVault = this.config.somaVault;
|
|
2567
|
+
if (!somaVault) {
|
|
2568
|
+
return res.json({ tier: "teaser", somaVault: false, governanceAvailable: false });
|
|
2569
|
+
}
|
|
2570
|
+
try {
|
|
2571
|
+
const reportPath = path2.join(somaVault, "..", "soma-report.json");
|
|
2572
|
+
if (!fs2.existsSync(reportPath)) {
|
|
2573
|
+
return res.json({ tier: "free", somaVault: true, governanceAvailable: false });
|
|
2574
|
+
}
|
|
2575
|
+
const report = JSON.parse(fs2.readFileSync(reportPath, "utf-8"));
|
|
2576
|
+
const hasGovernance = report.governance && typeof report.governance.pending === "number";
|
|
2577
|
+
return res.json({
|
|
2578
|
+
tier: hasGovernance ? "pro" : "free",
|
|
2579
|
+
somaVault: true,
|
|
2580
|
+
governanceAvailable: !!hasGovernance
|
|
2581
|
+
});
|
|
2582
|
+
} catch {
|
|
2583
|
+
return res.json({ tier: "free", somaVault: true, governanceAvailable: false });
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2563
2586
|
this.app.get("/api/soma/report", (_req, res) => {
|
|
2564
2587
|
const somaVault = this.config.somaVault;
|
|
2565
2588
|
if (!somaVault) {
|
|
@@ -2656,6 +2679,118 @@ var DashboardServer = class {
|
|
|
2656
2679
|
res.status(404).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2657
2680
|
}
|
|
2658
2681
|
});
|
|
2682
|
+
this.app.get("/api/soma/policies", (_req, res) => {
|
|
2683
|
+
const somaVault = this.config.somaVault;
|
|
2684
|
+
if (!somaVault) return res.json({ policies: [] });
|
|
2685
|
+
try {
|
|
2686
|
+
const reportPath = path2.join(somaVault, "..", "soma-report.json");
|
|
2687
|
+
if (!fs2.existsSync(reportPath)) return res.json({ policies: [] });
|
|
2688
|
+
const report = JSON.parse(fs2.readFileSync(reportPath, "utf-8"));
|
|
2689
|
+
res.json({ policies: report.policies ?? [] });
|
|
2690
|
+
} catch {
|
|
2691
|
+
res.json({ policies: [] });
|
|
2692
|
+
}
|
|
2693
|
+
});
|
|
2694
|
+
this.app.post("/api/soma/policies", import_express.default.json(), (req, res) => {
|
|
2695
|
+
var _a;
|
|
2696
|
+
const somaVault = this.config.somaVault;
|
|
2697
|
+
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2698
|
+
const { name, enforcement, scope, conditions } = req.body ?? {};
|
|
2699
|
+
if (!name) return res.status(400).json({ error: "name required" });
|
|
2700
|
+
try {
|
|
2701
|
+
const safeName = sanitizeArg(String(name));
|
|
2702
|
+
const safeEnf = sanitizeArg(String(enforcement || "warn"));
|
|
2703
|
+
const safeScope = sanitizeReason(String(scope || "all"));
|
|
2704
|
+
const safeCond = sanitizeReason(String(conditions || ""));
|
|
2705
|
+
const result = (0, import_node_child_process.execSync)(
|
|
2706
|
+
`npx soma policy create "${safeName}" --enforcement ${safeEnf} --scope "${safeScope}" --conditions "${safeCond}" --vault "${somaVault}"`,
|
|
2707
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
2708
|
+
);
|
|
2709
|
+
res.json({ success: true, message: result.trim() });
|
|
2710
|
+
} catch (error) {
|
|
2711
|
+
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2712
|
+
}
|
|
2713
|
+
});
|
|
2714
|
+
this.app.delete("/api/soma/policies/:name", (req, res) => {
|
|
2715
|
+
var _a;
|
|
2716
|
+
const somaVault = this.config.somaVault;
|
|
2717
|
+
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2718
|
+
try {
|
|
2719
|
+
const safeName = sanitizeArg(String(req.params.name));
|
|
2720
|
+
const result = (0, import_node_child_process.execSync)(
|
|
2721
|
+
`npx soma policy delete "${safeName}" --vault "${somaVault}"`,
|
|
2722
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
2723
|
+
);
|
|
2724
|
+
res.json({ success: true, message: result.trim() });
|
|
2725
|
+
} catch (error) {
|
|
2726
|
+
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2727
|
+
}
|
|
2728
|
+
});
|
|
2729
|
+
this.app.get("/api/soma/vault/entities", (req, res) => {
|
|
2730
|
+
const somaVault = this.config.somaVault;
|
|
2731
|
+
if (!somaVault) return res.json({ entities: [], total: 0 });
|
|
2732
|
+
try {
|
|
2733
|
+
const reportPath = path2.join(somaVault, "..", "soma-report.json");
|
|
2734
|
+
if (!fs2.existsSync(reportPath)) return res.json({ entities: [], total: 0 });
|
|
2735
|
+
const report = JSON.parse(fs2.readFileSync(reportPath, "utf-8"));
|
|
2736
|
+
let entities = [
|
|
2737
|
+
...(report.agents ?? []).map((a) => ({ ...a, type: "agent", id: a.name })),
|
|
2738
|
+
...(report.insights ?? []).map((i, idx) => {
|
|
2739
|
+
var _a;
|
|
2740
|
+
return { ...i, type: i.type || "insight", id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}` };
|
|
2741
|
+
}),
|
|
2742
|
+
...(report.policies ?? []).map((p) => ({ ...p, type: "policy", id: p.name }))
|
|
2743
|
+
];
|
|
2744
|
+
const { type, layer, q, limit: limitStr, offset: offsetStr } = req.query;
|
|
2745
|
+
if (type) entities = entities.filter((e) => e.type === type);
|
|
2746
|
+
if (layer) entities = entities.filter((e) => e.layer === layer);
|
|
2747
|
+
if (q) {
|
|
2748
|
+
const lq = q.toLowerCase();
|
|
2749
|
+
entities = entities.filter((e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq));
|
|
2750
|
+
}
|
|
2751
|
+
const total = entities.length;
|
|
2752
|
+
const offset = parseInt(offsetStr || "0", 10);
|
|
2753
|
+
const limit = Math.min(parseInt(limitStr || "50", 10), 200);
|
|
2754
|
+
entities = entities.slice(offset, offset + limit);
|
|
2755
|
+
res.json({ entities, total });
|
|
2756
|
+
} catch (error) {
|
|
2757
|
+
console.error("Vault entities error:", error);
|
|
2758
|
+
res.json({ entities: [], total: 0 });
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
this.app.get("/api/soma/vault/entities/:type/:id", (req, res) => {
|
|
2762
|
+
const somaVault = this.config.somaVault;
|
|
2763
|
+
if (!somaVault) return res.status(404).json({ error: "Soma vault not configured" });
|
|
2764
|
+
try {
|
|
2765
|
+
const reportPath = path2.join(somaVault, "..", "soma-report.json");
|
|
2766
|
+
const report = JSON.parse(fs2.readFileSync(reportPath, "utf-8"));
|
|
2767
|
+
const { type, id } = req.params;
|
|
2768
|
+
let entity = null;
|
|
2769
|
+
if (type === "agent") {
|
|
2770
|
+
entity = (report.agents ?? []).find((a) => a.name === id);
|
|
2771
|
+
} else if (type === "policy") {
|
|
2772
|
+
entity = (report.policies ?? []).find((p) => p.name === id);
|
|
2773
|
+
} else {
|
|
2774
|
+
entity = (report.insights ?? []).find(
|
|
2775
|
+
(i) => {
|
|
2776
|
+
var _a;
|
|
2777
|
+
return (((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || "") === id || i.title === id;
|
|
2778
|
+
}
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2781
|
+
if (!entity) return res.status(404).json({ error: "Entity not found" });
|
|
2782
|
+
res.json({
|
|
2783
|
+
...entity,
|
|
2784
|
+
type,
|
|
2785
|
+
id,
|
|
2786
|
+
body: entity.claim || entity.conditions || "",
|
|
2787
|
+
tags: entity.tags ?? [],
|
|
2788
|
+
related: entity.related ?? []
|
|
2789
|
+
});
|
|
2790
|
+
} catch {
|
|
2791
|
+
res.status(404).json({ error: "Entity not found" });
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2659
2794
|
this.app.get("/api/process-health", (_req, res) => {
|
|
2660
2795
|
var _a, _b;
|
|
2661
2796
|
try {
|
|
@@ -2759,11 +2894,11 @@ var DashboardServer = class {
|
|
|
2759
2894
|
}
|
|
2760
2895
|
} catch {
|
|
2761
2896
|
}
|
|
2762
|
-
const watched = [
|
|
2897
|
+
const watched = [...new Set([
|
|
2763
2898
|
this.config.tracesDir,
|
|
2764
2899
|
...this.config.dataDirs || [],
|
|
2765
2900
|
...extraDirs
|
|
2766
|
-
];
|
|
2901
|
+
].map((w) => path2.resolve(w)))];
|
|
2767
2902
|
const discovered = [];
|
|
2768
2903
|
const svcNames = getSystemdServices(this.userConfig);
|
|
2769
2904
|
if (svcNames.length > 0) {
|
|
@@ -2917,6 +3052,41 @@ var DashboardServer = class {
|
|
|
2917
3052
|
});
|
|
2918
3053
|
});
|
|
2919
3054
|
}
|
|
3055
|
+
/** Watch soma-report.json for changes and broadcast updates via WebSocket. */
|
|
3056
|
+
setupSomaReportWatcher() {
|
|
3057
|
+
const somaVault = this.config.somaVault;
|
|
3058
|
+
if (!somaVault) return;
|
|
3059
|
+
const reportPath = path2.join(somaVault, "..", "soma-report.json");
|
|
3060
|
+
const reportDir = path2.dirname(reportPath);
|
|
3061
|
+
if (!fs2.existsSync(reportDir)) return;
|
|
3062
|
+
let debounceTimer = null;
|
|
3063
|
+
const watcher = import_chokidar2.default.watch(reportPath, {
|
|
3064
|
+
ignoreInitial: true,
|
|
3065
|
+
persistent: true,
|
|
3066
|
+
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
3067
|
+
});
|
|
3068
|
+
watcher.on("change", () => {
|
|
3069
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
3070
|
+
debounceTimer = setTimeout(() => {
|
|
3071
|
+
var _a, _b;
|
|
3072
|
+
try {
|
|
3073
|
+
const report = JSON.parse(fs2.readFileSync(reportPath, "utf-8"));
|
|
3074
|
+
this.broadcast({ type: "soma-report-updated", data: report });
|
|
3075
|
+
if (report.generatedAt) {
|
|
3076
|
+
this.broadcast({
|
|
3077
|
+
type: "soma-activity",
|
|
3078
|
+
data: {
|
|
3079
|
+
action: "report-updated",
|
|
3080
|
+
description: `Report updated: ${((_a = report.totals) == null ? void 0 : _a.agents) ?? 0} agents, ${((_b = report.totals) == null ? void 0 : _b.insights) ?? 0} insights`,
|
|
3081
|
+
timestamp: report.generatedAt
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
3085
|
+
} catch {
|
|
3086
|
+
}
|
|
3087
|
+
}, 500);
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
2920
3090
|
/**
|
|
2921
3091
|
* Filter an agent's traces to valid ExecutionGraphs and convert via loadGraph().
|
|
2922
3092
|
* Returns only traces with proper nodes (Map or non-empty object), skipping session-only traces.
|
|
@@ -3233,7 +3403,9 @@ function printBanner(config, traceCount, stats, configPath) {
|
|
|
3233
3403
|
\u2192 http://localhost:${port}${isPublic && lan ? `
|
|
3234
3404
|
\u2192 http://${lan}:${port} (LAN)` : ""}
|
|
3235
3405
|
|
|
3236
|
-
|
|
3406
|
+
Pages: Agents \xB7 SOMA
|
|
3407
|
+
Agent: Profile \xB7 Execution Detail
|
|
3408
|
+
SOMA: Intelligence \xB7 Review \xB7 Policies \xB7 Knowledge \xB7 Activity
|
|
3237
3409
|
Tabs: Flame Chart \xB7 Agent Flow \xB7 Metrics \xB7 Dependencies
|
|
3238
3410
|
State Machine \xB7 Summary \xB7 Transcript
|
|
3239
3411
|
|