agentflow-dashboard 0.8.0 → 0.8.2
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-EG254FLY.js} +196 -10
- package/dist/cli.cjs +195 -8
- package/dist/cli.js +1 -1
- package/dist/client/assets/index-DCSWGDzI.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 +199 -12
- package/dist/index.js +1 -1
- package/dist/server.cjs +199 -12
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/client/assets/index-Cb5C1Pah.js +0 -50
package/dist/server.cjs
CHANGED
|
@@ -36,7 +36,7 @@ var import_node_child_process = require("child_process");
|
|
|
36
36
|
var fs3 = __toESM(require("fs"), 1);
|
|
37
37
|
var import_node_http = require("http");
|
|
38
38
|
var path3 = __toESM(require("path"), 1);
|
|
39
|
-
var
|
|
39
|
+
var import_node_url2 = require("url");
|
|
40
40
|
|
|
41
41
|
// src/config.ts
|
|
42
42
|
var import_node_fs = require("fs");
|
|
@@ -111,6 +111,7 @@ function getProcessPreference(config) {
|
|
|
111
111
|
|
|
112
112
|
// src/server.ts
|
|
113
113
|
var import_agentflow_core3 = require("agentflow-core");
|
|
114
|
+
var import_chokidar2 = __toESM(require("chokidar"), 1);
|
|
114
115
|
var import_express = __toESM(require("express"), 1);
|
|
115
116
|
var import_ws = require("ws");
|
|
116
117
|
|
|
@@ -885,7 +886,7 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
885
886
|
...getSkipFiles(this.userConfig)
|
|
886
887
|
]);
|
|
887
888
|
this.userSkipDirs = new Set(getSkipDirectories(this.userConfig));
|
|
888
|
-
this.allWatchDirs = [this.tracesDir, ...this.dataDirs];
|
|
889
|
+
this.allWatchDirs = [...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))];
|
|
889
890
|
this.ensureTracesDir();
|
|
890
891
|
this.loadExistingFiles();
|
|
891
892
|
this.archiveOldTraces();
|
|
@@ -2066,8 +2067,8 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
2066
2067
|
return (b.lastModified || b.startTime) - (a.lastModified || a.startTime);
|
|
2067
2068
|
});
|
|
2068
2069
|
}
|
|
2069
|
-
getTrace(filename) {
|
|
2070
|
-
|
|
2070
|
+
getTrace(filename, agentId) {
|
|
2071
|
+
let candidates = [];
|
|
2071
2072
|
const exact = this.traces.get(filename);
|
|
2072
2073
|
if (exact) candidates.push(exact);
|
|
2073
2074
|
if (filename.includes("::")) {
|
|
@@ -2092,6 +2093,13 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
2092
2093
|
}
|
|
2093
2094
|
if (candidates.length === 0) return void 0;
|
|
2094
2095
|
if (candidates.length === 1) return candidates[0];
|
|
2096
|
+
if (agentId) {
|
|
2097
|
+
const agentMatches = candidates.filter((c) => c.agentId === agentId);
|
|
2098
|
+
if (agentMatches.length > 0) {
|
|
2099
|
+
candidates = agentMatches;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
if (candidates.length === 1) return candidates[0];
|
|
2095
2103
|
let best = candidates[0];
|
|
2096
2104
|
let bestNodeCount = best.nodes instanceof Map ? best.nodes.size : Object.keys(best.nodes ?? {}).length;
|
|
2097
2105
|
for (let i = 1; i < candidates.length; i++) {
|
|
@@ -2149,7 +2157,10 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
2149
2157
|
var fs2 = __toESM(require("fs"), 1);
|
|
2150
2158
|
var os = __toESM(require("os"), 1);
|
|
2151
2159
|
var path2 = __toESM(require("path"), 1);
|
|
2152
|
-
var
|
|
2160
|
+
var import_node_url = require("url");
|
|
2161
|
+
var import_meta = {};
|
|
2162
|
+
var __cliDirname = path2.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
2163
|
+
var VERSION = JSON.parse(fs2.readFileSync(path2.resolve(__cliDirname, "../package.json"), "utf-8")).version;
|
|
2153
2164
|
function getLanAddress() {
|
|
2154
2165
|
const interfaces = os.networkInterfaces();
|
|
2155
2166
|
for (const name of Object.keys(interfaces)) {
|
|
@@ -2189,7 +2200,9 @@ function printBanner(config, traceCount, stats, configPath) {
|
|
|
2189
2200
|
\u2192 http://localhost:${port}${isPublic && lan ? `
|
|
2190
2201
|
\u2192 http://${lan}:${port} (LAN)` : ""}
|
|
2191
2202
|
|
|
2192
|
-
|
|
2203
|
+
Pages: Agents \xB7 SOMA
|
|
2204
|
+
Agent: Profile \xB7 Execution Detail
|
|
2205
|
+
SOMA: Intelligence \xB7 Review \xB7 Policies \xB7 Knowledge \xB7 Activity
|
|
2193
2206
|
Tabs: Flame Chart \xB7 Agent Flow \xB7 Metrics \xB7 Dependencies
|
|
2194
2207
|
State Machine \xB7 Summary \xB7 Transcript
|
|
2195
2208
|
|
|
@@ -2324,8 +2337,8 @@ Examples:
|
|
|
2324
2337
|
}
|
|
2325
2338
|
|
|
2326
2339
|
// src/server.ts
|
|
2327
|
-
var
|
|
2328
|
-
var __filename = (0,
|
|
2340
|
+
var import_meta2 = {};
|
|
2341
|
+
var __filename = (0, import_node_url2.fileURLToPath)(import_meta2.url);
|
|
2329
2342
|
var __dirname = path3.dirname(__filename);
|
|
2330
2343
|
function serializeTrace(trace) {
|
|
2331
2344
|
if (!trace) return trace;
|
|
@@ -2375,6 +2388,7 @@ var DashboardServer = class {
|
|
|
2375
2388
|
this.setupExpress();
|
|
2376
2389
|
this.setupWebSocket();
|
|
2377
2390
|
this.setupTraceWatcher();
|
|
2391
|
+
this.setupSomaReportWatcher();
|
|
2378
2392
|
let knowledgeCount = 0;
|
|
2379
2393
|
for (const trace of this.watcher.getAllTraces()) {
|
|
2380
2394
|
this.stats.processTrace(trace);
|
|
@@ -2430,6 +2444,10 @@ var DashboardServer = class {
|
|
|
2430
2444
|
if (fs3.existsSync(clientDir)) {
|
|
2431
2445
|
this.app.use(import_express.default.static(clientDir));
|
|
2432
2446
|
}
|
|
2447
|
+
const pkgVersion = JSON.parse(fs3.readFileSync(path3.resolve(__dirname, "../package.json"), "utf-8")).version;
|
|
2448
|
+
this.app.get("/api/version", (_req, res) => {
|
|
2449
|
+
res.json({ version: pkgVersion });
|
|
2450
|
+
});
|
|
2433
2451
|
this.app.get("/api/traces", (req, res) => {
|
|
2434
2452
|
try {
|
|
2435
2453
|
const limit = Math.min(Math.max(parseInt(req.query.limit, 10) || 50, 1), 200);
|
|
@@ -2449,7 +2467,8 @@ var DashboardServer = class {
|
|
|
2449
2467
|
});
|
|
2450
2468
|
this.app.get("/api/traces/:filename", (req, res) => {
|
|
2451
2469
|
try {
|
|
2452
|
-
const
|
|
2470
|
+
const agentId = typeof req.query.agent === "string" ? req.query.agent : void 0;
|
|
2471
|
+
const trace = this.watcher.getTrace(req.params.filename, agentId);
|
|
2453
2472
|
if (!trace) {
|
|
2454
2473
|
return res.status(404).json({ error: "Trace not found" });
|
|
2455
2474
|
}
|
|
@@ -2733,6 +2752,27 @@ var DashboardServer = class {
|
|
|
2733
2752
|
res.status(500).json({ error: "Failed to load agent statistics" });
|
|
2734
2753
|
}
|
|
2735
2754
|
});
|
|
2755
|
+
this.app.get("/api/soma/tier", (_req, res) => {
|
|
2756
|
+
const somaVault = this.config.somaVault;
|
|
2757
|
+
if (!somaVault) {
|
|
2758
|
+
return res.json({ tier: "teaser", somaVault: false, governanceAvailable: false });
|
|
2759
|
+
}
|
|
2760
|
+
try {
|
|
2761
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2762
|
+
if (!fs3.existsSync(reportPath)) {
|
|
2763
|
+
return res.json({ tier: "free", somaVault: true, governanceAvailable: false });
|
|
2764
|
+
}
|
|
2765
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2766
|
+
const hasGovernance = report.governance && typeof report.governance.pending === "number";
|
|
2767
|
+
return res.json({
|
|
2768
|
+
tier: hasGovernance ? "pro" : "free",
|
|
2769
|
+
somaVault: true,
|
|
2770
|
+
governanceAvailable: !!hasGovernance
|
|
2771
|
+
});
|
|
2772
|
+
} catch {
|
|
2773
|
+
return res.json({ tier: "free", somaVault: true, governanceAvailable: false });
|
|
2774
|
+
}
|
|
2775
|
+
});
|
|
2736
2776
|
this.app.get("/api/soma/report", (_req, res) => {
|
|
2737
2777
|
const somaVault = this.config.somaVault;
|
|
2738
2778
|
if (!somaVault) {
|
|
@@ -2829,6 +2869,118 @@ var DashboardServer = class {
|
|
|
2829
2869
|
res.status(404).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2830
2870
|
}
|
|
2831
2871
|
});
|
|
2872
|
+
this.app.get("/api/soma/policies", (_req, res) => {
|
|
2873
|
+
const somaVault = this.config.somaVault;
|
|
2874
|
+
if (!somaVault) return res.json({ policies: [] });
|
|
2875
|
+
try {
|
|
2876
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2877
|
+
if (!fs3.existsSync(reportPath)) return res.json({ policies: [] });
|
|
2878
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2879
|
+
res.json({ policies: report.policies ?? [] });
|
|
2880
|
+
} catch {
|
|
2881
|
+
res.json({ policies: [] });
|
|
2882
|
+
}
|
|
2883
|
+
});
|
|
2884
|
+
this.app.post("/api/soma/policies", import_express.default.json(), (req, res) => {
|
|
2885
|
+
var _a;
|
|
2886
|
+
const somaVault = this.config.somaVault;
|
|
2887
|
+
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2888
|
+
const { name, enforcement, scope, conditions } = req.body ?? {};
|
|
2889
|
+
if (!name) return res.status(400).json({ error: "name required" });
|
|
2890
|
+
try {
|
|
2891
|
+
const safeName = sanitizeArg(String(name));
|
|
2892
|
+
const safeEnf = sanitizeArg(String(enforcement || "warn"));
|
|
2893
|
+
const safeScope = sanitizeReason(String(scope || "all"));
|
|
2894
|
+
const safeCond = sanitizeReason(String(conditions || ""));
|
|
2895
|
+
const result = (0, import_node_child_process.execSync)(
|
|
2896
|
+
`npx soma policy create "${safeName}" --enforcement ${safeEnf} --scope "${safeScope}" --conditions "${safeCond}" --vault "${somaVault}"`,
|
|
2897
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
2898
|
+
);
|
|
2899
|
+
res.json({ success: true, message: result.trim() });
|
|
2900
|
+
} catch (error) {
|
|
2901
|
+
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2902
|
+
}
|
|
2903
|
+
});
|
|
2904
|
+
this.app.delete("/api/soma/policies/:name", (req, res) => {
|
|
2905
|
+
var _a;
|
|
2906
|
+
const somaVault = this.config.somaVault;
|
|
2907
|
+
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2908
|
+
try {
|
|
2909
|
+
const safeName = sanitizeArg(String(req.params.name));
|
|
2910
|
+
const result = (0, import_node_child_process.execSync)(
|
|
2911
|
+
`npx soma policy delete "${safeName}" --vault "${somaVault}"`,
|
|
2912
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
2913
|
+
);
|
|
2914
|
+
res.json({ success: true, message: result.trim() });
|
|
2915
|
+
} catch (error) {
|
|
2916
|
+
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
2917
|
+
}
|
|
2918
|
+
});
|
|
2919
|
+
this.app.get("/api/soma/vault/entities", (req, res) => {
|
|
2920
|
+
const somaVault = this.config.somaVault;
|
|
2921
|
+
if (!somaVault) return res.json({ entities: [], total: 0 });
|
|
2922
|
+
try {
|
|
2923
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2924
|
+
if (!fs3.existsSync(reportPath)) return res.json({ entities: [], total: 0 });
|
|
2925
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2926
|
+
let entities = [
|
|
2927
|
+
...(report.agents ?? []).map((a) => ({ ...a, type: "agent", id: a.name })),
|
|
2928
|
+
...(report.insights ?? []).map((i, idx) => {
|
|
2929
|
+
var _a;
|
|
2930
|
+
return { ...i, type: i.type || "insight", id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}` };
|
|
2931
|
+
}),
|
|
2932
|
+
...(report.policies ?? []).map((p) => ({ ...p, type: "policy", id: p.name }))
|
|
2933
|
+
];
|
|
2934
|
+
const { type, layer, q, limit: limitStr, offset: offsetStr } = req.query;
|
|
2935
|
+
if (type) entities = entities.filter((e) => e.type === type);
|
|
2936
|
+
if (layer) entities = entities.filter((e) => e.layer === layer);
|
|
2937
|
+
if (q) {
|
|
2938
|
+
const lq = q.toLowerCase();
|
|
2939
|
+
entities = entities.filter((e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq));
|
|
2940
|
+
}
|
|
2941
|
+
const total = entities.length;
|
|
2942
|
+
const offset = parseInt(offsetStr || "0", 10);
|
|
2943
|
+
const limit = Math.min(parseInt(limitStr || "50", 10), 200);
|
|
2944
|
+
entities = entities.slice(offset, offset + limit);
|
|
2945
|
+
res.json({ entities, total });
|
|
2946
|
+
} catch (error) {
|
|
2947
|
+
console.error("Vault entities error:", error);
|
|
2948
|
+
res.json({ entities: [], total: 0 });
|
|
2949
|
+
}
|
|
2950
|
+
});
|
|
2951
|
+
this.app.get("/api/soma/vault/entities/:type/:id", (req, res) => {
|
|
2952
|
+
const somaVault = this.config.somaVault;
|
|
2953
|
+
if (!somaVault) return res.status(404).json({ error: "Soma vault not configured" });
|
|
2954
|
+
try {
|
|
2955
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
2956
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
2957
|
+
const { type, id } = req.params;
|
|
2958
|
+
let entity = null;
|
|
2959
|
+
if (type === "agent") {
|
|
2960
|
+
entity = (report.agents ?? []).find((a) => a.name === id);
|
|
2961
|
+
} else if (type === "policy") {
|
|
2962
|
+
entity = (report.policies ?? []).find((p) => p.name === id);
|
|
2963
|
+
} else {
|
|
2964
|
+
entity = (report.insights ?? []).find(
|
|
2965
|
+
(i) => {
|
|
2966
|
+
var _a;
|
|
2967
|
+
return (((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || "") === id || i.title === id;
|
|
2968
|
+
}
|
|
2969
|
+
);
|
|
2970
|
+
}
|
|
2971
|
+
if (!entity) return res.status(404).json({ error: "Entity not found" });
|
|
2972
|
+
res.json({
|
|
2973
|
+
...entity,
|
|
2974
|
+
type,
|
|
2975
|
+
id,
|
|
2976
|
+
body: entity.claim || entity.conditions || "",
|
|
2977
|
+
tags: entity.tags ?? [],
|
|
2978
|
+
related: entity.related ?? []
|
|
2979
|
+
});
|
|
2980
|
+
} catch {
|
|
2981
|
+
res.status(404).json({ error: "Entity not found" });
|
|
2982
|
+
}
|
|
2983
|
+
});
|
|
2832
2984
|
this.app.get("/api/process-health", (_req, res) => {
|
|
2833
2985
|
var _a, _b;
|
|
2834
2986
|
try {
|
|
@@ -2932,11 +3084,11 @@ var DashboardServer = class {
|
|
|
2932
3084
|
}
|
|
2933
3085
|
} catch {
|
|
2934
3086
|
}
|
|
2935
|
-
const watched = [
|
|
3087
|
+
const watched = [...new Set([
|
|
2936
3088
|
this.config.tracesDir,
|
|
2937
3089
|
...this.config.dataDirs || [],
|
|
2938
3090
|
...extraDirs
|
|
2939
|
-
];
|
|
3091
|
+
].map((w) => path3.resolve(w)))];
|
|
2940
3092
|
const discovered = [];
|
|
2941
3093
|
const svcNames = getSystemdServices(this.userConfig);
|
|
2942
3094
|
if (svcNames.length > 0) {
|
|
@@ -3090,6 +3242,41 @@ var DashboardServer = class {
|
|
|
3090
3242
|
});
|
|
3091
3243
|
});
|
|
3092
3244
|
}
|
|
3245
|
+
/** Watch soma-report.json for changes and broadcast updates via WebSocket. */
|
|
3246
|
+
setupSomaReportWatcher() {
|
|
3247
|
+
const somaVault = this.config.somaVault;
|
|
3248
|
+
if (!somaVault) return;
|
|
3249
|
+
const reportPath = path3.join(somaVault, "..", "soma-report.json");
|
|
3250
|
+
const reportDir = path3.dirname(reportPath);
|
|
3251
|
+
if (!fs3.existsSync(reportDir)) return;
|
|
3252
|
+
let debounceTimer = null;
|
|
3253
|
+
const watcher = import_chokidar2.default.watch(reportPath, {
|
|
3254
|
+
ignoreInitial: true,
|
|
3255
|
+
persistent: true,
|
|
3256
|
+
awaitWriteFinish: { stabilityThreshold: 500 }
|
|
3257
|
+
});
|
|
3258
|
+
watcher.on("change", () => {
|
|
3259
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
3260
|
+
debounceTimer = setTimeout(() => {
|
|
3261
|
+
var _a, _b;
|
|
3262
|
+
try {
|
|
3263
|
+
const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
|
|
3264
|
+
this.broadcast({ type: "soma-report-updated", data: report });
|
|
3265
|
+
if (report.generatedAt) {
|
|
3266
|
+
this.broadcast({
|
|
3267
|
+
type: "soma-activity",
|
|
3268
|
+
data: {
|
|
3269
|
+
action: "report-updated",
|
|
3270
|
+
description: `Report updated: ${((_a = report.totals) == null ? void 0 : _a.agents) ?? 0} agents, ${((_b = report.totals) == null ? void 0 : _b.insights) ?? 0} insights`,
|
|
3271
|
+
timestamp: report.generatedAt
|
|
3272
|
+
}
|
|
3273
|
+
});
|
|
3274
|
+
}
|
|
3275
|
+
} catch {
|
|
3276
|
+
}
|
|
3277
|
+
}, 500);
|
|
3278
|
+
});
|
|
3279
|
+
}
|
|
3093
3280
|
/**
|
|
3094
3281
|
* Filter an agent's traces to valid ExecutionGraphs and convert via loadGraph().
|
|
3095
3282
|
* Returns only traces with proper nodes (Map or non-empty object), skipping session-only traces.
|
|
@@ -3361,7 +3548,7 @@ var DashboardServer = class {
|
|
|
3361
3548
|
return this.watcher.getAllTraces();
|
|
3362
3549
|
}
|
|
3363
3550
|
};
|
|
3364
|
-
if (
|
|
3551
|
+
if (import_meta2.url === `file://${process.argv[1]}`) {
|
|
3365
3552
|
startDashboard().catch(console.error);
|
|
3366
3553
|
}
|
|
3367
3554
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/server.js
CHANGED
package/package.json
CHANGED