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
package/dist/cli.cjs
CHANGED
|
@@ -43,6 +43,11 @@ var fs2 = __toESM(require("fs"), 1);
|
|
|
43
43
|
var import_node_http = require("http");
|
|
44
44
|
var path2 = __toESM(require("path"), 1);
|
|
45
45
|
var import_node_url = require("url");
|
|
46
|
+
var import_agentflow_core3 = require("agentflow-core");
|
|
47
|
+
var import_chokidar2 = __toESM(require("chokidar"), 1);
|
|
48
|
+
var import_express = __toESM(require("express"), 1);
|
|
49
|
+
var import_express_rate_limit = __toESM(require("express-rate-limit"), 1);
|
|
50
|
+
var import_ws = require("ws");
|
|
46
51
|
|
|
47
52
|
// src/config.ts
|
|
48
53
|
var import_node_fs = require("fs");
|
|
@@ -115,12 +120,6 @@ function getProcessPreference(config) {
|
|
|
115
120
|
return config.processPreference ?? null;
|
|
116
121
|
}
|
|
117
122
|
|
|
118
|
-
// src/server.ts
|
|
119
|
-
var import_agentflow_core3 = require("agentflow-core");
|
|
120
|
-
var import_chokidar2 = __toESM(require("chokidar"), 1);
|
|
121
|
-
var import_express = __toESM(require("express"), 1);
|
|
122
|
-
var import_ws = require("ws");
|
|
123
|
-
|
|
124
123
|
// src/adapters/agentflow.ts
|
|
125
124
|
var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
126
125
|
"workers.json",
|
|
@@ -132,7 +131,14 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
|
|
|
132
131
|
"models.json",
|
|
133
132
|
"config.json"
|
|
134
133
|
]);
|
|
135
|
-
var SKIP_SUFFIXES = [
|
|
134
|
+
var SKIP_SUFFIXES = [
|
|
135
|
+
"-state.json",
|
|
136
|
+
"-config.json",
|
|
137
|
+
"-watch-state.json",
|
|
138
|
+
".tmp",
|
|
139
|
+
".bak",
|
|
140
|
+
".backup"
|
|
141
|
+
];
|
|
136
142
|
var AgentFlowAdapter = class {
|
|
137
143
|
name = "agentflow";
|
|
138
144
|
detect(_dirPath) {
|
|
@@ -409,8 +415,14 @@ registerAdapter(new AgentFlowAdapter());
|
|
|
409
415
|
var PURPOSE_KEYWORDS = [
|
|
410
416
|
{ keywords: ["email", "mail", "inbox", "smtp"], group: "Email Processors" },
|
|
411
417
|
{ keywords: ["monitor", "watch", "alert", "surveillance"], group: "Monitors" },
|
|
412
|
-
{
|
|
413
|
-
|
|
418
|
+
{
|
|
419
|
+
keywords: ["digest", "newsletter", "summary", "report", "briefing"],
|
|
420
|
+
group: "Digests & Reports"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
keywords: ["curator", "janitor", "distiller", "surveyor", "worker", "indexer"],
|
|
424
|
+
group: "Workers"
|
|
425
|
+
},
|
|
414
426
|
{ keywords: ["cron", "schedule", "timer", "periodic"], group: "Scheduled Jobs" },
|
|
415
427
|
{ keywords: ["search", "scrape", "crawl", "fetch"], group: "Data Collection" },
|
|
416
428
|
{ keywords: ["embed", "vector", "index"], group: "Embeddings" }
|
|
@@ -442,6 +454,7 @@ function capitalize(s) {
|
|
|
442
454
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
443
455
|
}
|
|
444
456
|
function deduplicateAgents(agents) {
|
|
457
|
+
var _a, _b, _c, _d;
|
|
445
458
|
const tagged = agents.map((a) => ({
|
|
446
459
|
...a,
|
|
447
460
|
...extractSource(a.agentId)
|
|
@@ -458,14 +471,14 @@ function deduplicateAgents(agents) {
|
|
|
458
471
|
const mergedIds = /* @__PURE__ */ new Set();
|
|
459
472
|
const mergedAgents = [];
|
|
460
473
|
for (const [_key, group] of suffixGroups) {
|
|
461
|
-
const suffix = extractSuffix(group[0].localId);
|
|
474
|
+
const suffix = extractSuffix((_a = group[0]) == null ? void 0 : _a.localId);
|
|
462
475
|
if (group.length < 2) continue;
|
|
463
476
|
const prefixes = new Set(group.map((a) => a.localId.split("-")[0]));
|
|
464
477
|
if (prefixes.size < 2) continue;
|
|
465
478
|
const longPrefixes = [...prefixes].filter((p) => p !== suffix && p.length > 2);
|
|
466
479
|
if (longPrefixes.length >= 2) continue;
|
|
467
480
|
const merged = {
|
|
468
|
-
agentId: group[0].source === "agentflow" ? suffix : `${group[0].source}:${suffix}`,
|
|
481
|
+
agentId: ((_b = group[0]) == null ? void 0 : _b.source) === "agentflow" ? suffix : `${(_c = group[0]) == null ? void 0 : _c.source}:${suffix}`,
|
|
469
482
|
displayName: suffix,
|
|
470
483
|
totalExecutions: group.reduce((s, a) => s + a.totalExecutions, 0),
|
|
471
484
|
successfulExecutions: group.reduce((s, a) => s + a.successfulExecutions, 0),
|
|
@@ -476,7 +489,7 @@ function deduplicateAgents(agents) {
|
|
|
476
489
|
triggers: {},
|
|
477
490
|
recentActivity: group.flatMap((a) => a.recentActivity).sort((a, b) => b.timestamp - a.timestamp).slice(0, 50),
|
|
478
491
|
sources: group.map((a) => a.agentId),
|
|
479
|
-
adapterSource: group[0].source
|
|
492
|
+
adapterSource: (_d = group[0]) == null ? void 0 : _d.source
|
|
480
493
|
};
|
|
481
494
|
merged.successRate = merged.totalExecutions > 0 ? merged.successfulExecutions / merged.totalExecutions * 100 : 0;
|
|
482
495
|
const totalExecTime = group.reduce((s, a) => s + a.avgExecutionTime * a.totalExecutions, 0);
|
|
@@ -892,7 +905,9 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
892
905
|
...getSkipFiles(this.userConfig)
|
|
893
906
|
]);
|
|
894
907
|
this.userSkipDirs = new Set(getSkipDirectories(this.userConfig));
|
|
895
|
-
this.allWatchDirs = [
|
|
908
|
+
this.allWatchDirs = [
|
|
909
|
+
...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))
|
|
910
|
+
];
|
|
896
911
|
this.ensureTracesDir();
|
|
897
912
|
this.loadExistingFiles();
|
|
898
913
|
this.archiveOldTraces();
|
|
@@ -902,7 +917,7 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
902
917
|
/** Move trace files older than maxAgeMs into archive/YYYY-MM/ subdirectories. */
|
|
903
918
|
archiveOldTraces() {
|
|
904
919
|
const cutoff = Date.now() - this.maxAgeMs;
|
|
905
|
-
|
|
920
|
+
const _archived = 0;
|
|
906
921
|
for (const dir of this.allWatchDirs) {
|
|
907
922
|
if (!fs.existsSync(dir)) continue;
|
|
908
923
|
try {
|
|
@@ -919,7 +934,8 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
|
|
|
919
934
|
try {
|
|
920
935
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
921
936
|
for (const entry of entries) {
|
|
922
|
-
if (entry.name.startsWith(".") || entry.name === "archive" || this.userSkipDirs.has(entry.name))
|
|
937
|
+
if (entry.name.startsWith(".") || entry.name === "archive" || this.userSkipDirs.has(entry.name))
|
|
938
|
+
continue;
|
|
923
939
|
const fullPath = path.join(dir, entry.name);
|
|
924
940
|
if (entry.isDirectory()) {
|
|
925
941
|
archived += this.archiveDirectory(fullPath, cutoff, depth + 1);
|
|
@@ -2241,6 +2257,17 @@ var DashboardServer = class {
|
|
|
2241
2257
|
userConfig;
|
|
2242
2258
|
configPath;
|
|
2243
2259
|
setupExpress() {
|
|
2260
|
+
this.app.use(
|
|
2261
|
+
"/api/",
|
|
2262
|
+
(0, import_express_rate_limit.default)({
|
|
2263
|
+
windowMs: 60 * 1e3,
|
|
2264
|
+
// 1 minute
|
|
2265
|
+
max: 300,
|
|
2266
|
+
// 300 requests per minute per IP
|
|
2267
|
+
standardHeaders: true,
|
|
2268
|
+
legacyHeaders: false
|
|
2269
|
+
})
|
|
2270
|
+
);
|
|
2244
2271
|
if (this.config.enableCors) {
|
|
2245
2272
|
this.app.use((_req, res, next) => {
|
|
2246
2273
|
res.header("Access-Control-Allow-Origin", "*");
|
|
@@ -2483,6 +2510,7 @@ var DashboardServer = class {
|
|
|
2483
2510
|
}
|
|
2484
2511
|
});
|
|
2485
2512
|
this.app.get("/api/process-model/:agentId", (req, res) => {
|
|
2513
|
+
var _a, _b;
|
|
2486
2514
|
try {
|
|
2487
2515
|
const agentId = req.params.agentId;
|
|
2488
2516
|
const allTraces = this.watcher.getTracesByAgent(agentId);
|
|
@@ -2500,8 +2528,8 @@ var DashboardServer = class {
|
|
|
2500
2528
|
const nodeArr = Object.values(nodes);
|
|
2501
2529
|
const sorted = nodeArr.filter((n) => n.name && typeof n.startTime === "number" && n.startTime > 0).sort((a, b) => (a.startTime ?? 0) - (b.startTime ?? 0));
|
|
2502
2530
|
for (let i = 0; i < sorted.length - 1; i++) {
|
|
2503
|
-
const from = sorted[i].name;
|
|
2504
|
-
const to = sorted[i + 1].name;
|
|
2531
|
+
const from = (_a = sorted[i]) == null ? void 0 : _a.name;
|
|
2532
|
+
const to = (_b = sorted[i + 1]) == null ? void 0 : _b.name;
|
|
2505
2533
|
const key = `${from}|||${to}`;
|
|
2506
2534
|
transMap.set(key, (transMap.get(key) ?? 0) + 1);
|
|
2507
2535
|
}
|
|
@@ -2600,7 +2628,11 @@ var DashboardServer = class {
|
|
|
2600
2628
|
try {
|
|
2601
2629
|
const reportPath = path2.join(somaVault, "..", "soma-report.json");
|
|
2602
2630
|
if (!fs2.existsSync(reportPath)) {
|
|
2603
|
-
return res.json({
|
|
2631
|
+
return res.json({
|
|
2632
|
+
available: false,
|
|
2633
|
+
teaser: false,
|
|
2634
|
+
message: "No report file yet. Run soma watch."
|
|
2635
|
+
});
|
|
2604
2636
|
}
|
|
2605
2637
|
const report = JSON.parse(fs2.readFileSync(reportPath, "utf-8"));
|
|
2606
2638
|
res.json(report);
|
|
@@ -2624,7 +2656,9 @@ var DashboardServer = class {
|
|
|
2624
2656
|
available: true,
|
|
2625
2657
|
layers: report.layers ?? { archive: 0, working: 0, emerging: 0, canon: 0 },
|
|
2626
2658
|
governance: report.governance ?? { pending: 0, promoted: 0, rejected: 0 },
|
|
2627
|
-
insights: (report.insights ?? []).filter(
|
|
2659
|
+
insights: (report.insights ?? []).filter(
|
|
2660
|
+
(i) => i.layer === "emerging" && i.proposal_status === "pending"
|
|
2661
|
+
),
|
|
2628
2662
|
canon: (report.insights ?? []).filter((i) => i.layer === "canon"),
|
|
2629
2663
|
generatedAt: report.generatedAt
|
|
2630
2664
|
});
|
|
@@ -2633,21 +2667,23 @@ var DashboardServer = class {
|
|
|
2633
2667
|
res.status(500).json({ available: false, message: "Failed to read governance data" });
|
|
2634
2668
|
}
|
|
2635
2669
|
});
|
|
2636
|
-
const
|
|
2637
|
-
const sanitizeReason = (s) => s.replace(/["`$\\]/g, "").slice(0, 500);
|
|
2670
|
+
const isValidId = (s) => /^[a-zA-Z0-9_\-.:]+$/.test(s);
|
|
2638
2671
|
this.app.post("/api/soma/governance/promote", (req, res) => {
|
|
2639
2672
|
var _a;
|
|
2640
2673
|
const somaVault = this.config.somaVault;
|
|
2641
2674
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2642
2675
|
const { entryId } = req.body ?? {};
|
|
2643
|
-
if (!entryId
|
|
2676
|
+
if (!entryId || !isValidId(String(entryId)))
|
|
2677
|
+
return res.status(400).json({ error: "Invalid entryId" });
|
|
2644
2678
|
try {
|
|
2645
|
-
const
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2679
|
+
const result = (0, import_node_child_process.execFileSync)(
|
|
2680
|
+
"npx",
|
|
2681
|
+
["soma", "governance", "promote", String(entryId), "--vault", somaVault],
|
|
2682
|
+
{
|
|
2683
|
+
encoding: "utf-8",
|
|
2684
|
+
timeout: 1e4
|
|
2685
|
+
}
|
|
2686
|
+
);
|
|
2651
2687
|
res.json({ success: true, message: result.trim() });
|
|
2652
2688
|
} catch (error) {
|
|
2653
2689
|
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2658,15 +2694,27 @@ var DashboardServer = class {
|
|
|
2658
2694
|
const somaVault = this.config.somaVault;
|
|
2659
2695
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2660
2696
|
const { entryId, reason } = req.body ?? {};
|
|
2661
|
-
if (!entryId || !
|
|
2697
|
+
if (!entryId || !isValidId(String(entryId)))
|
|
2698
|
+
return res.status(400).json({ error: "Invalid entryId" });
|
|
2699
|
+
if (!reason || typeof reason !== "string")
|
|
2700
|
+
return res.status(400).json({ error: "reason required" });
|
|
2662
2701
|
try {
|
|
2663
|
-
const
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2702
|
+
const result = (0, import_node_child_process.execFileSync)(
|
|
2703
|
+
"npx",
|
|
2704
|
+
[
|
|
2705
|
+
"soma",
|
|
2706
|
+
"governance",
|
|
2707
|
+
"reject",
|
|
2708
|
+
String(entryId),
|
|
2709
|
+
String(reason).slice(0, 500),
|
|
2710
|
+
"--vault",
|
|
2711
|
+
somaVault
|
|
2712
|
+
],
|
|
2713
|
+
{
|
|
2714
|
+
encoding: "utf-8",
|
|
2715
|
+
timeout: 1e4
|
|
2716
|
+
}
|
|
2717
|
+
);
|
|
2670
2718
|
res.json({ success: true, message: result.trim() });
|
|
2671
2719
|
} catch (error) {
|
|
2672
2720
|
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2676,13 +2724,16 @@ var DashboardServer = class {
|
|
|
2676
2724
|
var _a;
|
|
2677
2725
|
const somaVault = this.config.somaVault;
|
|
2678
2726
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2727
|
+
if (!isValidId(String(req.params.id))) return res.status(400).json({ error: "Invalid id" });
|
|
2679
2728
|
try {
|
|
2680
|
-
const
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2729
|
+
const result = (0, import_node_child_process.execFileSync)(
|
|
2730
|
+
"npx",
|
|
2731
|
+
["soma", "governance", "show", String(req.params.id), "--vault", somaVault],
|
|
2732
|
+
{
|
|
2733
|
+
encoding: "utf-8",
|
|
2734
|
+
timeout: 1e4
|
|
2735
|
+
}
|
|
2736
|
+
);
|
|
2686
2737
|
res.json({ available: true, output: result.trim() });
|
|
2687
2738
|
} catch (error) {
|
|
2688
2739
|
res.status(404).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2705,16 +2756,24 @@ var DashboardServer = class {
|
|
|
2705
2756
|
const somaVault = this.config.somaVault;
|
|
2706
2757
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2707
2758
|
const { name, enforcement, scope, conditions } = req.body ?? {};
|
|
2708
|
-
if (!name
|
|
2759
|
+
if (!name || !isValidId(String(name)))
|
|
2760
|
+
return res.status(400).json({ error: "Invalid policy name" });
|
|
2761
|
+
const enf = String(enforcement || "warn");
|
|
2762
|
+
if (!isValidId(enf)) return res.status(400).json({ error: "Invalid enforcement value" });
|
|
2709
2763
|
try {
|
|
2710
|
-
const
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2764
|
+
const args = [
|
|
2765
|
+
"soma",
|
|
2766
|
+
"policy",
|
|
2767
|
+
"create",
|
|
2768
|
+
String(name),
|
|
2769
|
+
"--enforcement",
|
|
2770
|
+
enf,
|
|
2771
|
+
"--vault",
|
|
2772
|
+
somaVault
|
|
2773
|
+
];
|
|
2774
|
+
if (scope) args.push("--scope", String(scope).slice(0, 500));
|
|
2775
|
+
if (conditions) args.push("--conditions", String(conditions).slice(0, 500));
|
|
2776
|
+
const result = (0, import_node_child_process.execFileSync)("npx", args, { encoding: "utf-8", timeout: 1e4 });
|
|
2718
2777
|
res.json({ success: true, message: result.trim() });
|
|
2719
2778
|
} catch (error) {
|
|
2720
2779
|
res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
|
|
@@ -2724,11 +2783,16 @@ var DashboardServer = class {
|
|
|
2724
2783
|
var _a;
|
|
2725
2784
|
const somaVault = this.config.somaVault;
|
|
2726
2785
|
if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
|
|
2786
|
+
if (!isValidId(String(req.params.name)))
|
|
2787
|
+
return res.status(400).json({ error: "Invalid policy name" });
|
|
2727
2788
|
try {
|
|
2728
|
-
const
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
{
|
|
2789
|
+
const result = (0, import_node_child_process.execFileSync)(
|
|
2790
|
+
"npx",
|
|
2791
|
+
["soma", "policy", "delete", String(req.params.name), "--vault", somaVault],
|
|
2792
|
+
{
|
|
2793
|
+
encoding: "utf-8",
|
|
2794
|
+
timeout: 1e4
|
|
2795
|
+
}
|
|
2732
2796
|
);
|
|
2733
2797
|
res.json({ success: true, message: result.trim() });
|
|
2734
2798
|
} catch (error) {
|
|
@@ -2746,16 +2810,28 @@ var DashboardServer = class {
|
|
|
2746
2810
|
...(report.agents ?? []).map((a) => ({ ...a, type: "agent", id: a.name })),
|
|
2747
2811
|
...(report.insights ?? []).map((i, idx) => {
|
|
2748
2812
|
var _a;
|
|
2749
|
-
return {
|
|
2813
|
+
return {
|
|
2814
|
+
...i,
|
|
2815
|
+
type: i.type || "insight",
|
|
2816
|
+
id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}`
|
|
2817
|
+
};
|
|
2750
2818
|
}),
|
|
2751
2819
|
...(report.policies ?? []).map((p) => ({ ...p, type: "policy", id: p.name }))
|
|
2752
2820
|
];
|
|
2753
|
-
const {
|
|
2821
|
+
const {
|
|
2822
|
+
type,
|
|
2823
|
+
layer,
|
|
2824
|
+
q,
|
|
2825
|
+
limit: limitStr,
|
|
2826
|
+
offset: offsetStr
|
|
2827
|
+
} = req.query;
|
|
2754
2828
|
if (type) entities = entities.filter((e) => e.type === type);
|
|
2755
2829
|
if (layer) entities = entities.filter((e) => e.layer === layer);
|
|
2756
2830
|
if (q) {
|
|
2757
2831
|
const lq = q.toLowerCase();
|
|
2758
|
-
entities = entities.filter(
|
|
2832
|
+
entities = entities.filter(
|
|
2833
|
+
(e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq)
|
|
2834
|
+
);
|
|
2759
2835
|
}
|
|
2760
2836
|
const total = entities.length;
|
|
2761
2837
|
const offset = parseInt(offsetStr || "0", 10);
|
|
@@ -2846,9 +2922,7 @@ var DashboardServer = class {
|
|
|
2846
2922
|
const orphans = uniqueProcesses.filter(
|
|
2847
2923
|
(p) => !allKnownPids.has(p.pid) && p.pid !== process.pid && p.pid !== process.ppid
|
|
2848
2924
|
);
|
|
2849
|
-
const problems = services.flatMap(
|
|
2850
|
-
(s) => s.audit.problems.map((p) => `[${s.name}] ${p}`)
|
|
2851
|
-
);
|
|
2925
|
+
const problems = services.flatMap((s) => s.audit.problems.map((p) => `[${s.name}] ${p}`));
|
|
2852
2926
|
const result = {
|
|
2853
2927
|
// Backward-compatible fields from primary service
|
|
2854
2928
|
pidFile: (primary == null ? void 0 : primary.audit.pidFile) ?? null,
|
|
@@ -2903,19 +2977,24 @@ var DashboardServer = class {
|
|
|
2903
2977
|
}
|
|
2904
2978
|
} catch {
|
|
2905
2979
|
}
|
|
2906
|
-
const watched = [
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2980
|
+
const watched = [
|
|
2981
|
+
...new Set(
|
|
2982
|
+
[this.config.tracesDir, ...this.config.dataDirs || [], ...extraDirs].map(
|
|
2983
|
+
(w) => path2.resolve(w)
|
|
2984
|
+
)
|
|
2985
|
+
)
|
|
2986
|
+
];
|
|
2911
2987
|
const discovered = [];
|
|
2912
2988
|
const svcNames = getSystemdServices(this.userConfig);
|
|
2913
2989
|
if (svcNames.length > 0) {
|
|
2914
2990
|
try {
|
|
2915
|
-
const
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
{
|
|
2991
|
+
const raw = (0, import_node_child_process.execFileSync)(
|
|
2992
|
+
"systemctl",
|
|
2993
|
+
["--user", "show", "--property=ExecStart", "--no-pager", ...svcNames],
|
|
2994
|
+
{
|
|
2995
|
+
encoding: "utf8",
|
|
2996
|
+
timeout: 5e3
|
|
2997
|
+
}
|
|
2919
2998
|
);
|
|
2920
2999
|
for (const line of raw.split("\n")) {
|
|
2921
3000
|
const match = line.match(/path=([^\s;]+)/);
|
|
@@ -2947,10 +3026,19 @@ var DashboardServer = class {
|
|
|
2947
3026
|
this.app.post("/api/directories", import_express.default.json(), (req, res) => {
|
|
2948
3027
|
try {
|
|
2949
3028
|
const { add, remove } = req.body;
|
|
2950
|
-
if (add
|
|
2951
|
-
|
|
3029
|
+
if (add) {
|
|
3030
|
+
const resolved = path2.resolve(add);
|
|
3031
|
+
if (resolved !== add || add.includes("..")) {
|
|
3032
|
+
return res.status(400).json({ error: "Invalid directory path" });
|
|
3033
|
+
}
|
|
3034
|
+
if (!fs2.existsSync(resolved)) {
|
|
3035
|
+
return res.status(400).json({ error: `Directory does not exist: ${add}` });
|
|
3036
|
+
}
|
|
2952
3037
|
}
|
|
2953
|
-
const configPath = path2.join(
|
|
3038
|
+
const configPath = path2.join(
|
|
3039
|
+
process.env.HOME ?? "/home/trader",
|
|
3040
|
+
".agentflow/dashboard-config.json"
|
|
3041
|
+
);
|
|
2954
3042
|
let config = {};
|
|
2955
3043
|
try {
|
|
2956
3044
|
if (fs2.existsSync(configPath)) {
|
|
@@ -3160,13 +3248,31 @@ var DashboardServer = class {
|
|
|
3160
3248
|
isVirtual: false
|
|
3161
3249
|
});
|
|
3162
3250
|
}
|
|
3163
|
-
const
|
|
3164
|
-
const
|
|
3165
|
-
const
|
|
3166
|
-
for (const
|
|
3167
|
-
}
|
|
3168
|
-
nodes.push({
|
|
3169
|
-
|
|
3251
|
+
const _rootSteps = new Set(model.steps);
|
|
3252
|
+
const _childSteps = new Set(model.transitions.map((t) => t.to));
|
|
3253
|
+
const _leafSteps = new Set(model.steps);
|
|
3254
|
+
for (const _t of model.transitions) {
|
|
3255
|
+
}
|
|
3256
|
+
nodes.push({
|
|
3257
|
+
id: "[START]",
|
|
3258
|
+
label: "[START]",
|
|
3259
|
+
count: model.totalGraphs,
|
|
3260
|
+
frequency: 1,
|
|
3261
|
+
avgDuration: 0,
|
|
3262
|
+
failRate: 0,
|
|
3263
|
+
p95Duration: 0,
|
|
3264
|
+
isVirtual: true
|
|
3265
|
+
});
|
|
3266
|
+
nodes.push({
|
|
3267
|
+
id: "[END]",
|
|
3268
|
+
label: "[END]",
|
|
3269
|
+
count: model.totalGraphs,
|
|
3270
|
+
frequency: 1,
|
|
3271
|
+
avgDuration: 0,
|
|
3272
|
+
failRate: 0,
|
|
3273
|
+
p95Duration: 0,
|
|
3274
|
+
isVirtual: true
|
|
3275
|
+
});
|
|
3170
3276
|
const edges = model.transitions.map((t) => ({
|
|
3171
3277
|
source: t.from,
|
|
3172
3278
|
target: t.to,
|
|
@@ -3186,7 +3292,10 @@ var DashboardServer = class {
|
|
|
3186
3292
|
}
|
|
3187
3293
|
}
|
|
3188
3294
|
const maxEdgeCount = Math.max(...edges.map((e) => e.count), 1);
|
|
3189
|
-
const maxNodeCount = Math.max(
|
|
3295
|
+
const maxNodeCount = Math.max(
|
|
3296
|
+
...nodes.filter((n) => !n.isVirtual).map((n) => n.count),
|
|
3297
|
+
1
|
|
3298
|
+
);
|
|
3190
3299
|
return { agentId, totalTraces: model.totalGraphs, nodes, edges, maxEdgeCount, maxNodeCount };
|
|
3191
3300
|
}
|
|
3192
3301
|
/**
|
|
@@ -3374,7 +3483,9 @@ if (import_meta.url === `file://${process.argv[1]}`) {
|
|
|
3374
3483
|
// src/cli.ts
|
|
3375
3484
|
var import_meta2 = {};
|
|
3376
3485
|
var __cliDirname = path3.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
|
|
3377
|
-
var VERSION = JSON.parse(
|
|
3486
|
+
var VERSION = JSON.parse(
|
|
3487
|
+
fs3.readFileSync(path3.resolve(__cliDirname, "../package.json"), "utf-8")
|
|
3488
|
+
).version;
|
|
3378
3489
|
function getLanAddress() {
|
|
3379
3490
|
const interfaces = os.networkInterfaces();
|
|
3380
3491
|
for (const name of Object.keys(interfaces)) {
|
package/dist/cli.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg: #0d1117;--bg2: #161b22;--bg3: #1c2129;--bgh: #21262d;--bd: #30363d;--bdm: #21262d;--t1: #e6edf3;--t2: #8b949e;--t3: #6e7681;--ok: #3fb950;--warn: #d29922;--fail: #f85149;--info: #58a6ff;--f: sans-serif;--fm: "SF Mono", Menlo, monospace;--xs: .85rem;--sm: .9rem;--base: 1rem;--lg: 1.15rem;--xl: 1.35rem;--s1: 4px;--s2: 8px;--s3: 12px;--s4: 16px;--s5: 24px;--r: 5px}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}body{font-family:var(--f);font-size:var(--base);color:var(--t1);background:var(--bg);line-height:1.4;-webkit-font-smoothing:antialiased}.dashboard{display:flex;flex-direction:column;height:100vh;overflow:hidden}.workspace{display:flex;flex:1;min-height:0;overflow:hidden;border-top:1px solid var(--bd)}.workspace__main{flex:1;overflow-y:auto;overflow-x:hidden}.workspace__empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--t3);font-size:var(--sm)}.health-banner{display:flex;align-items:center;gap:var(--s4);padding:0 var(--s4);height:42px;background:var(--bg2);border-bottom:1px solid var(--bd);flex-shrink:0}.health-banner__title{font-family:var(--fm);font-size:var(--base);font-weight:700;margin-right:var(--s1)}.hb-version{font-size:11px;color:var(--t3);font-family:var(--fm);margin-right:var(--s3)}.hb-live{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;letter-spacing:.06em;padding:1px 6px;border-radius:3px;margin-right:var(--s3)}.hb-live--on{color:var(--ok);background:#3fb9501a}.hb-live--off{color:var(--fail);background:#f851491a}.hb-live__dot{width:6px;height:6px;border-radius:50%;background:currentColor}.hb-live__dot--pulse{animation:livePulse 2s ease-in-out infinite}@keyframes livePulse{0%,to{opacity:1}50%{opacity:.3}}.health-banner__stats{display:flex;gap:var(--s4);align-items:center}.stat-cell{display:flex;flex-direction:column;align-items:center}.stat-cell__value{font-family:var(--fm);font-size:var(--sm);font-weight:700;line-height:1}.stat-cell__label{font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.06em}.stat-cell__sparkline{display:flex;gap:1px;margin-top:2px}.spark{width:2px;height:6px;border-radius:1px}.spark--ok{background:var(--ok);opacity:.4}.spark--fail{background:var(--fail)}.dot{display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.dot--ok{background:var(--ok)}.dot--fail{background:var(--fail)}.dot--warn{background:var(--warn)}.top-section{flex-shrink:0;max-height:33vh;overflow-y:auto;background:var(--bg2);border-bottom:2px solid var(--bd)}.chip-row{display:flex;gap:var(--s1);flex-wrap:wrap;padding:var(--s1) var(--s4);border-bottom:1px solid var(--bd);background:#161b2299;align-items:center}.chip-row__label{font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.05em;font-weight:600;margin-right:var(--s2);white-space:nowrap}.schip{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-family:var(--fm);padding:2px 6px;border-radius:10px;border:1px solid var(--bd);color:var(--t2)}.schip--ok{border-color:#3fb95033}.schip--fail{border-color:#f851494d;color:var(--fail)}.schip--off{opacity:.5}.schip--infra{border-color:#58a6ff33}.schip--worker{padding:1px 6px;border-radius:var(--r)}.schip__name{font-weight:600;color:var(--t1)}.schip__detail{color:var(--t3)}.schip__state{color:var(--t3);font-style:italic}.chip{font-size:12px;font-family:var(--fm);padding:1px 8px;border-radius:10px;border:1px solid var(--bd);color:var(--t2)}.chip--ok{border-color:#3fb9504d}.chip--fail{border-color:#f851494d;color:var(--fail)}.chip--off{opacity:.5}.chip--infra{border-color:#58a6ff4d}.agroup{border-bottom:1px solid var(--bd)}.agroup__head{display:flex;width:100%;align-items:center;gap:var(--s2);padding:var(--s1) var(--s4);background:transparent;border:none;color:var(--t1);cursor:pointer;font-size:var(--xs);text-align:left}.agroup__head:hover{background:var(--bgh)}.agroup__expand{font-size:11px;color:var(--t3);width:12px}.agroup__name{font-weight:600}.agroup__stats{color:var(--t2);font-family:var(--fm)}.agroup__fail{color:var(--fail);font-weight:600}.agroup__svc{font-size:11px;color:var(--t3);font-family:var(--fm)}.agroup__svc--ok{color:var(--ok)}.agroup__count{margin-left:auto;color:var(--t3)}.agroup__body{padding:var(--s1) var(--s4);display:flex;flex-wrap:wrap;gap:var(--s3);align-items:flex-start}.asubgroup{flex:1;min-width:200px}.asubgroup__label{font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.06em;padding:var(--s1) 0;font-weight:600}.asubgroup__cards{display:flex;gap:var(--s1);flex-wrap:wrap}.acard__merged{font-size:12px;color:var(--info);font-style:italic}.agent-row{display:flex;gap:var(--s2);flex-wrap:wrap;padding:var(--s2) var(--s4)}.acard{background:var(--bg3);border:1px solid var(--bd);border-radius:var(--r);padding:var(--s1) var(--s2);cursor:pointer;text-align:left;min-width:120px;flex:1 1 140px;max-width:220px;transition:border-color .1s;font-size:var(--xs)}.acard:hover{border-color:var(--info)}.acard--sel{border-color:var(--info);background:#58a6ff0f}.acard--fail{border-left:3px solid var(--fail)}.acard__r1{display:flex;align-items:center;gap:3px;margin-bottom:1px}.acard__name{font-family:var(--fm);font-size:var(--xs);font-weight:600;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.acard__source{font-size:12px;padding:0 4px;border-radius:2px;background:#58a6ff26;color:var(--info);text-transform:uppercase;letter-spacing:.04em;flex-shrink:0}.acard__pct{font-family:var(--fm);font-size:var(--xs);color:var(--ok)}.acard__pct--warn{color:var(--warn)}.acard__r2{display:flex;gap:var(--s2);font-size:12px;color:var(--t3)}.acard__failn{color:var(--fail);font-weight:700}.acard__spark{display:flex;gap:1px;align-items:flex-end;height:8px;margin-top:2px;overflow:hidden}.sk{width:2px;min-height:2px;border-radius:1px;flex-shrink:0}.sk--ok{background:var(--ok);opacity:.4}.sk--fail{background:var(--fail)}.exec-sidebar{width:270px;overflow-y:auto;border-right:2px solid var(--bd);flex-shrink:0;background:var(--bg2)}.exec-sidebar__head{display:flex;justify-content:space-between;padding:var(--s2) var(--s3);font-size:var(--xs);font-weight:600;color:var(--t3);text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--bdm);position:sticky;top:0;background:var(--bg2);z-index:1}.exec-sidebar__agent,.exec-sidebar__count{font-family:var(--fm)}.exec-sidebar__fails{color:var(--fail);font-weight:700}.erow{display:flex;width:100%;align-items:center;gap:4px;padding:3px var(--s2);background:transparent;border:none;border-bottom:1px solid var(--bdm);color:var(--t2);cursor:pointer;font-size:12px;text-align:left}.erow:hover{background:var(--bgh)}.erow--sel{background:var(--bg3);border-left:2px solid var(--info)}.erow--fail{color:var(--fail)}.erow__icon{width:12px;text-align:center;font-size:12px}.erow__time{width:72px;font-size:11px;color:var(--t3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.erow__n{font-family:var(--fm);width:22px;text-align:right}.erow__dur{font-family:var(--fm);width:36px;text-align:right}.erow__bar{flex:1;height:3px;background:var(--bg);border-radius:2px;overflow:hidden}.erow__fill{display:block;height:100%;border-radius:2px}.erow__fill--ok{background:var(--ok);opacity:.4}.erow__fill--fail{background:var(--fail);opacity:.6}.agent-profile{padding:var(--s3);height:100%;display:flex;flex-direction:column}.ap-stats{display:flex;gap:var(--s5);padding:var(--s2) 0;border-bottom:1px solid var(--bdm);margin-bottom:var(--s3);flex-wrap:wrap}.ap-stat{text-align:center}.ap-stat__v{display:block;font-family:var(--fm);font-size:var(--lg);font-weight:700}.ap-stat__l{font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.05em}.ap-tabs{display:flex;gap:0;border-bottom:1px solid var(--bdm);margin-bottom:var(--s3)}.ap-tab{background:transparent;border:none;border-bottom:2px solid transparent;padding:var(--s2) var(--s3);font-size:var(--xs);color:var(--t3);cursor:pointer}.ap-tab:hover{color:var(--t1)}.ap-tab--active{color:var(--t1);border-bottom-color:var(--info)}.ap-content{flex:1;overflow-y:auto}.pmap__controls{display:flex;align-items:center;gap:var(--s4);margin-bottom:var(--s2);font-size:var(--xs);color:var(--t2)}.pmap__slider-label{display:flex;align-items:center;gap:var(--s2)}.pmap__slider{width:120px}.pmap__info{color:var(--t3)}.pmap__svg{display:block}.var-row{padding:var(--s2) 0;border-bottom:1px solid var(--bdm)}.var-row--happy{border-left:3px solid var(--ok);padding-left:var(--s2)}.var-row__header{display:flex;align-items:center;gap:var(--s2);font-size:var(--xs);margin-bottom:var(--s1)}.var-row__rank{font-family:var(--fm);color:var(--t3);width:24px}.var-row__badge{background:#3fb95026;color:var(--ok);font-size:11px;padding:1px 6px;border-radius:3px;font-weight:600}.var-row__count{font-family:var(--fm);color:var(--t2)}.var-row__pct-bar{flex:1;height:4px;background:var(--bg3);border-radius:2px;overflow:hidden}.var-row__pct-fill{height:100%;background:var(--info);border-radius:2px;opacity:.5}.var-row__steps{display:flex;align-items:center;gap:2px;flex-wrap:wrap}.var-row__arrow{color:var(--t3);font-size:12px;margin:0 2px}.var-row__step{font-family:var(--fm);font-size:11px;padding:1px 6px;border:1px solid;border-radius:3px;white-space:nowrap}.bn-layout{display:flex;flex-direction:column;height:100%}.bn-chart-area{flex:1;min-height:200px;max-height:50%;overflow:auto;border:1px solid var(--bdm);border-radius:var(--r);margin-bottom:var(--s2);position:relative}.bn-chart-controls{position:sticky;top:0;right:0;display:flex;justify-content:flex-end;gap:2px;padding:var(--s1);z-index:1;background:#0d1117cc}.bn-ranking{flex:1;overflow-y:auto;min-height:100px}.bn-row{display:flex;align-items:center;gap:var(--s2);padding:var(--s1) 0;font-size:var(--xs)}.bn-row__name{font-family:var(--fm);width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bn-row__type{color:var(--t3);width:60px}.bn-row__p95{font-family:var(--fm);width:60px;text-align:right;color:var(--fail)}.bn-row__bar{flex:1;height:6px;background:var(--bg3);border-radius:3px;overflow:hidden}.bn-row__fill{height:100%;background:var(--fail);opacity:.6;border-radius:3px}.exec-detail{display:flex;flex-direction:column;height:100%}.ed-header{display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3);border-bottom:1px solid var(--bdm);flex-shrink:0;flex-wrap:wrap}.ed-header__agent{font-family:var(--fm);font-weight:700;font-size:var(--sm)}.ed-header__meta{font-size:var(--xs);color:var(--t2)}.ed-header__ts{font-size:var(--xs);color:var(--t3);font-family:var(--fm);margin-left:auto}.ed-tag{font-size:11px;padding:1px 6px;background:var(--bg2);border:1px solid var(--bd);border-radius:3px;color:var(--t3);font-family:var(--fm)}.ed-tabs{display:flex;gap:0;border-bottom:1px solid var(--bdm);flex-shrink:0;overflow-x:auto;flex-wrap:wrap}.ed-tab{background:transparent;border:none;border-bottom:2px solid transparent;padding:var(--s1) var(--s2);font-size:var(--xs);color:var(--t3);cursor:pointer;white-space:nowrap}.ed-tab:hover{color:var(--t1)}.ed-tab--active{color:var(--t1);border-bottom-color:var(--info)}.ed-content{flex:1;overflow-y:auto;padding:var(--s3)}.flame__fail-callout{background:#f8514914;border:1px solid rgba(248,81,73,.25);border-radius:var(--r);padding:var(--s2) var(--s3);margin-bottom:var(--s3);font-size:var(--xs)}.flame__fail-title{color:var(--fail);font-weight:700;font-size:var(--sm);margin-bottom:var(--s1)}.flame__fail-item{display:flex;align-items:baseline;gap:var(--s2);padding:1px 0}.flame__fail-ts{font-family:var(--fm);color:var(--t3)}.flame__fail-err{color:var(--fail);font-family:var(--fm);font-size:11px}.flame__range{font-size:var(--xs);color:var(--t2);font-family:var(--fm);margin-bottom:var(--s2)}.flame__axis{display:flex;justify-content:space-between;padding:0 0 var(--s1) 28px;font-size:11px;color:var(--t3);font-family:var(--fm);border-bottom:1px solid var(--bdm)}.flame__row{display:flex;align-items:center;height:24px;position:relative}.flame__depth{width:28px;font-size:11px;color:var(--t3);text-align:right;padding-right:var(--s1);flex-shrink:0;font-family:var(--fm)}.flame__track{flex:1;height:18px;position:relative;background:var(--bg2);border-radius:2px;overflow:visible}.flame__bar{position:absolute;top:1px;height:16px;border-radius:2px;cursor:pointer;display:flex;align-items:center;overflow:hidden;transition:opacity .1s}.flame__bar--hov{z-index:1;box-shadow:0 0 4px #fff3}.flame__bar-label{font-size:12px;color:#fff;padding:0 3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 1px 2px rgba(0,0,0,.5)}.flame__side-label{position:absolute;left:calc(100% + 4px);top:1px;font-size:12px;color:var(--t1);white-space:nowrap;pointer-events:none;font-family:var(--fm)}.flame__tooltip{position:fixed;background:var(--bg3);border:1px solid var(--bd);border-radius:var(--r);padding:var(--s2);font-size:var(--xs);white-space:nowrap;z-index:9999;pointer-events:none;box-shadow:0 4px 16px #0009;min-width:220px;max-width:400px}.flame__tt-title{font-weight:700;font-size:var(--sm);margin-bottom:2px}.flame__tt-type{font-size:var(--xs);margin-bottom:2px}.flame__tt-dur{font-family:var(--fm);color:var(--t2);margin-bottom:4px}.flame__tt-meta{font-size:11px;color:var(--t3)}.flame__tt-err{font-size:var(--xs);color:var(--fail);margin-top:4px}.aflow{position:relative}.af-step{position:relative;padding:1px 0}.af-step--fail{background:#f851490a}.af-step__line{position:absolute;top:-3px;left:11px;width:1px;height:6px;background:var(--bdm)}.af-step__row{display:flex;align-items:center;gap:var(--s2);font-size:var(--xs);padding:2px var(--s2)}.af-step__icon{width:16px;text-align:center;font-size:var(--sm);flex-shrink:0}.af-step__cat{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;width:48px;flex-shrink:0}.af-step__name{font-family:var(--fm);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.af-step__dur{font-family:var(--fm);color:var(--t3);width:48px;text-align:right;flex-shrink:0}.af-step__ts{font-family:var(--fm);color:var(--t3);font-size:11px;width:64px;text-align:right;flex-shrink:0}.af-step__ops{display:flex;gap:var(--s1);align-items:center;padding:1px var(--s2) 1px 32px;flex-wrap:wrap}.af-step__op-tag{font-size:12px;padding:0 4px;border-radius:2px;background:var(--bg);border:1px solid var(--bdm);color:var(--t2);font-family:var(--fm)}.af-step__op-tag--model{border-color:#bc8cff4d;color:#bc8cff}.af-step__op-detail{font-size:12px;color:var(--t3);font-family:var(--fm)}.af-step__err{font-size:var(--xs);color:var(--fail);padding:1px var(--s2) 1px 32px;font-family:var(--fm)}.mv-row{display:flex;gap:var(--s2);flex-wrap:wrap;margin-bottom:var(--s3)}.mv-c{background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);padding:var(--s2);text-align:center;min-width:70px;flex:1}.mv-v{display:block;font-family:var(--fm);font-size:var(--lg);font-weight:700}.mv-l{font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.05em}.c-ok{color:var(--ok)}.c-fail{color:var(--fail)}.mview__section{font-size:var(--xs);font-weight:600;color:var(--t3);text-transform:uppercase;letter-spacing:.05em;margin:var(--s3) 0 var(--s2)}.mt-row{display:flex;align-items:center;gap:var(--s2);padding:2px 0;font-size:var(--xs)}.mt-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.mt-name{font-family:var(--fm);width:70px}.mt-cnt{font-family:var(--fm);width:20px;text-align:right}.mt-fail{color:var(--fail);font-weight:700}.mt-dur{color:var(--t3);font-family:var(--fm);width:48px;text-align:right}.mt-bar{flex:1;height:5px;background:var(--bg3);border-radius:3px;overflow:hidden}.mt-fill{height:100%;border-radius:3px;opacity:.5}.dtree{font-size:var(--xs)}.dt-node{display:flex;align-items:center;gap:4px;padding:2px 0}.dt-node--fail{background:#f851490a}.dt-node__name{font-family:var(--fm);font-weight:600}.dt-node__type{font-size:11px;color:var(--t3)}.dt-node__dur{font-family:var(--fm);color:var(--t3);margin-left:auto}.dt-node__err{color:var(--fail);font-family:var(--fm);font-size:11px;margin-left:var(--s2)}.summary-content{font-size:var(--sm)}.sc-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s2);margin-bottom:var(--s3)}.sc-label{font-size:11px;color:var(--t3);text-transform:uppercase;display:block}.sc-failures{margin:var(--s3) 0;padding:var(--s2) var(--s3);background:#f851490f;border:1px solid rgba(248,81,73,.2);border-radius:var(--r)}.sc-failures__title{color:var(--fail);font-weight:700;margin-bottom:var(--s1)}.sc-failure{display:flex;gap:var(--s2);font-size:var(--xs);padding:1px 0}.sc-failure__type{color:var(--t3)}.sc-failure__err{color:var(--fail);font-family:var(--fm)}.sc-types{margin-top:var(--s3)}.sc-types__title{font-size:var(--xs);color:var(--t3);text-transform:uppercase;margin-bottom:var(--s1)}.sc-types__list{display:flex;gap:var(--s2);flex-wrap:wrap}.sc-type-badge{font-size:11px;font-family:var(--fm);padding:1px 6px;border:1px solid var(--bd);border-radius:3px;color:var(--t2)}.alert-card{display:flex;align-items:flex-start;gap:var(--s2);padding:var(--s2) var(--s4);font-size:var(--xs);border-bottom:1px solid var(--bdm)}.alert-card--critical{background:#f851490f}.alert-card--warn{background:#d299220f}.alert-icon{flex-shrink:0}.alert-content{flex:1;min-width:0}.alert-title{font-weight:600}.alert-description{color:var(--t3)}.alert-actions{display:flex;gap:var(--s2);margin-top:3px}.alert-action{font-size:11px;font-family:var(--fm);padding:1px 6px;background:var(--bg3);border:1px solid var(--bd);border-radius:3px;color:var(--t2);cursor:pointer}.alert-action:hover{color:var(--t1)}.alert-dismiss{padding:2px;background:transparent;border:none;color:var(--t3);cursor:pointer;font-size:var(--base);flex-shrink:0}.summary-bar{display:flex;gap:var(--s5);padding:var(--s2) var(--s4);background:var(--bg2);border-top:1px solid var(--bd);font-size:var(--xs);flex-shrink:0;height:32px;align-items:center;color:var(--t2)}.tv-chat{display:flex;flex-direction:column;gap:var(--s2)}.tv-bubble{background:var(--bg2);border:1px solid var(--bd);border-radius:12px;padding:var(--s2) var(--s3);max-width:85%;font-size:var(--xs)}.tv-bubble--right{align-self:flex-end;background:#58a6ff14;border-color:#58a6ff33}.tv-bubble--user{border-color:#58a6ff40}.tv-bubble--assistant{border-color:#3fb95033}.tv-bubble--tool{border-color:#d2992233;background:#d299220a}.tv-bubble--thinking{border-color:var(--bdm);opacity:.7;border-style:dashed}.tv-bubble--system{border-color:var(--bdm);background:var(--bg3)}.tv-bubble--event{border-color:var(--bdm)}.tv-bubble--error{border-color:#f851494d;background:#f851490a}.tv-bubble__header{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s1);flex-wrap:wrap}.tv-bubble__icon{font-size:var(--sm)}.tv-bubble__role{font-weight:600;font-size:var(--xs)}.tv-bubble__model{color:var(--t3);font-family:var(--fm);font-size:11px}.tv-bubble__tokens{color:var(--t3);font-size:11px;font-family:var(--fm)}.tv-bubble__time{color:var(--t3);margin-left:auto;font-family:var(--fm);font-size:11px}.tv-bubble__tool{margin:var(--s1) 0}.tv-bubble__tool-name{font-family:var(--fm);font-weight:600;color:var(--warn);font-size:var(--xs)}.tv-bubble__content{white-space:pre-wrap;word-break:break-word;line-height:1.5;color:var(--t1)}.tv-bubble__error{color:var(--fail);font-weight:600;margin-top:var(--s1)}.tv-code{background:var(--bg);border:1px solid var(--bdm);border-radius:6px;padding:var(--s2);font-family:var(--fm);font-size:12px;overflow-x:auto;max-height:150px;overflow-y:auto;white-space:pre;margin:var(--s1) 0;color:var(--t2)}.tv-code--error{border-color:#f8514933;color:var(--fail)}.tv-thinking-btn{background:transparent;border:none;color:var(--t3);cursor:pointer;font-size:var(--xs);padding:var(--s1) 0}.tv-bubble--left{align-self:flex-start}.tv-origin{font-size:7px;padding:1px 4px;border-radius:3px;text-transform:uppercase;letter-spacing:.04em;font-weight:700}.tv-origin--user{background:#58a6ff26;color:var(--info)}.tv-origin--agent{background:#3fb9501f;color:var(--ok)}.tv-origin--system{background:#6e768126;color:var(--t3)}.settings-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;z-index:100;display:flex;justify-content:flex-end}.settings-panel{width:420px;max-width:90vw;background:var(--bg2);border-left:1px solid var(--bd);overflow-y:auto;padding:var(--s4)}.sp-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--s4)}.sp-head h3{font-size:var(--base);font-weight:600}.sp-close{background:transparent;border:none;color:var(--t3);cursor:pointer;font-size:var(--xl)}.sp-error{padding:var(--s2);background:#f851491a;border:1px solid rgba(248,81,73,.3);border-radius:var(--r);font-size:var(--xs);color:var(--fail);margin-bottom:var(--s3)}.sp-section{font-size:var(--xs);color:var(--t3);text-transform:uppercase;letter-spacing:.06em;margin:var(--s3) 0 var(--s2);font-weight:600}.sp-dir{display:flex;align-items:center;gap:var(--s2);padding:var(--s1) var(--s2);font-size:var(--sm);border-radius:var(--r)}.sp-dir:hover{background:var(--bgh)}.sp-dir--sug{background:#d299220a}.sp-dir__path{font-family:var(--fm);font-size:var(--xs);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sp-btn{font-size:var(--xs);padding:2px 8px;background:var(--bg3);border:1px solid var(--bd);border-radius:var(--r);cursor:pointer;color:var(--t2)}.sp-btn--add{color:var(--info)}.sp-btn--add:hover{background:var(--bgh);border-color:var(--info)}.sp-btn--rm{color:var(--fail);font-size:var(--sm);padding:0 6px}.sp-btn--rm:hover{background:#f851491a}.sp-btn--rescan{width:100%;padding:var(--s2);color:var(--info);font-size:var(--sm)}.sp-btn--rescan:hover{background:var(--bgh)}.sp-btn:disabled{opacity:.5;cursor:default}.sp-manual{display:flex;gap:var(--s2);margin-bottom:var(--s2)}.sp-input{flex:1;background:var(--bg3);border:1px solid var(--bd);border-radius:var(--r);padding:var(--s1) var(--s2);color:var(--t1);font-family:var(--fm);font-size:var(--xs)}.sp-input:focus{outline:none;border-color:var(--info)}.zb{width:24px;height:24px;background:var(--bg3);border:1px solid var(--bd);border-radius:var(--r);color:var(--t1);cursor:pointer;font-size:var(--sm);display:inline-flex;align-items:center;justify-content:center}.zb:hover{background:var(--bgh);border-color:var(--t3)}.pmap__zoom{display:flex;gap:2px;margin-left:auto}.status-dot{display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.status-dot--ok{background:var(--ok)}.status-dot--critical{background:var(--fail)}.status-dot--warn{background:var(--warn)}.status-dot--inactive{background:var(--t3)}.soma-intel{padding:16px;font-size:13px;line-height:1.5}.soma-intel__header{display:flex;align-items:center;gap:8px;margin-bottom:16px}.soma-intel__title{font-size:16px;font-weight:700}.soma-intel__badge{font-size:10px;background:var(--info);color:#000;padding:2px 6px;border-radius:4px;font-weight:600}.soma-intel__ts{margin-left:auto;font-size:11px;color:var(--t3)}.soma-intel__ts--stale{color:var(--warn)}.soma-intel__agent-card{background:var(--bg2);border:1px solid var(--bd);border-radius:6px;padding:12px;margin-bottom:16px}.soma-intel__agent-name{font-weight:700;font-size:14px;margin-bottom:4px}.soma-intel__agent-stats{display:flex;gap:16px;font-size:12px;color:var(--t2)}.soma-intel__guard-block{margin-top:8px;padding:8px;background:#f851491a;border:1px solid var(--fail);border-radius:4px;color:var(--fail);font-size:12px;font-weight:600}.soma-intel__empty{color:var(--t3);padding:12px}.soma-intel__section{margin-bottom:16px}.soma-intel__section-header{display:flex;align-items:center;gap:8px;margin-bottom:8px;border-bottom:1px solid var(--bd);padding-bottom:4px;flex-wrap:wrap}.soma-intel__section-title{font-size:13px;font-weight:700;color:var(--t1);margin:0}.soma-intel__filters{display:flex;gap:4px;margin-left:auto}.soma-intel__filter{font-size:11px;padding:2px 6px;background:var(--bg2);border:1px solid var(--bd);border-radius:4px;color:var(--t1);cursor:pointer}.soma-intel__filter:focus{outline:1px solid var(--info)}.soma-intel__show-more{display:block;width:100%;padding:6px;margin-top:4px;background:none;border:1px dashed var(--bd);border-radius:4px;color:var(--t2);font-size:11px;cursor:pointer;text-align:center}.soma-intel__show-more:hover{background:var(--bg2);color:var(--t1)}.soma-intel__table{font-size:12px;font-family:var(--mono)}.soma-intel__row{display:grid;grid-template-columns:1fr 60px 50px 55px 60px;padding:4px 0;border-bottom:1px solid var(--bd)}.soma-intel__row--header{font-weight:700;color:var(--t2);border-bottom:2px solid var(--bd)}.soma-intel__row--active{background:#58a6ff14}.soma-intel__col-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.soma-intel__col-num,.soma-intel__col-status{text-align:right}.soma-intel__insight{padding:8px;background:var(--bg2);border-radius:4px;margin-bottom:6px}.soma-intel__insight-type{font-size:10px;text-transform:uppercase;color:var(--info);margin-right:6px;font-weight:600}.soma-intel__insight-conf{font-size:10px;color:var(--t3);margin-left:6px}.soma-intel__insight-claim{font-size:12px;color:var(--t2);margin-top:4px}.soma-intel__policy{padding:8px;background:var(--bg2);border-radius:4px;margin-bottom:6px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}.soma-intel__enforcement{font-size:10px;padding:2px 6px;border-radius:3px;font-weight:600}.soma-intel__enforcement--warn{background:#d2992233;color:var(--warn)}.soma-intel__enforcement--error{background:#f8514933;color:var(--fail)}.soma-intel__enforcement--abort{background:#f851494d;color:var(--fail)}.soma-intel__policy-cond{width:100%;font-size:11px;color:var(--t3);margin-top:2px}.soma-intel--teaser{text-align:center;padding:40px 20px}.soma-intel__teaser-icon{font-size:48px;margin-bottom:12px}.soma-intel__teaser-title{font-size:20px;font-weight:700;margin-bottom:4px}.soma-intel__teaser-subtitle{font-size:14px;color:var(--t2);margin-bottom:24px}.soma-intel__teaser-features{text-align:left;max-width:360px;margin:0 auto 24px;font-size:13px}.soma-intel__teaser-feature{padding:6px 0;color:var(--t1)}.soma-intel__teaser-cta{display:inline-block;padding:10px 24px;background:var(--info);color:#000;border-radius:6px;text-decoration:none;font-weight:700;font-size:14px}.soma-intel__teaser-cta:hover{opacity:.9}.page-tabs{display:flex;gap:0;background:var(--bg2);border-bottom:1px solid var(--bd);padding:0 var(--s3);flex-shrink:0}.page-tabs__tab{padding:8px 16px;font-size:13px;font-weight:600;color:var(--t2);background:transparent;border:none;border-bottom:2px solid transparent;cursor:pointer;transition:all .15s}.page-tabs__tab:hover{color:var(--t1);background:var(--bg3)}.page-tabs__tab--active{color:var(--info);border-bottom-color:var(--info)}.soma-page{flex:1;overflow-y:auto;padding:var(--s4)}.soma-page__tabs{display:flex;gap:4px;padding:0 0 var(--s3) 0;border-bottom:1px solid var(--bd);margin-bottom:var(--s3)}.soma-page__tab{padding:6px 14px;font-size:12px;font-weight:600;color:var(--t2);background:transparent;border:1px solid transparent;border-radius:var(--r);cursor:pointer;transition:all .15s}.soma-page__tab:hover{color:var(--t1);background:var(--bg3)}.soma-page__tab--active{color:var(--t1);background:var(--bg3);border-color:var(--bd)}.soma-page__tab--locked{opacity:.5;cursor:not-allowed}.soma-page__content{min-height:300px}.soma-page__loading,.soma-page__locked{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;color:var(--t3);gap:var(--s3)}.soma-page__locked-icon{font-size:32px;opacity:.5}.soma-page__teaser{text-align:center;padding:48px var(--s4);max-width:600px;margin:0 auto}.soma-page__teaser-icon{font-size:48px;margin-bottom:var(--s3)}.soma-page__teaser h2{font-size:var(--xl);margin-bottom:var(--s2)}.soma-page__teaser p{color:var(--t2);margin-bottom:var(--s4)}.soma-page__teaser-features{display:grid;gap:var(--s3);text-align:left;margin-bottom:var(--s5)}.soma-page__teaser-card{padding:var(--s3);background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r)}.soma-page__teaser-card strong{display:block;color:var(--t1);margin-bottom:4px;font-size:13px}.soma-page__teaser-card p{font-size:12px;color:var(--t2);margin:0}.soma-page__teaser-cta{font-size:12px;color:var(--t3)}.soma-page__teaser-cta code{background:var(--bg3);padding:2px 6px;border-radius:3px;font-family:var(--fm);font-size:11px}.soma-policies{padding:var(--s3)}.soma-policies__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--s3)}.soma-policies__header h3{font-size:var(--base);font-weight:700}.soma-policies__add{font-size:12px;padding:4px 12px;background:var(--info);color:#000;border:none;border-radius:var(--r);cursor:pointer;font-weight:600}.soma-policies__form{display:flex;gap:var(--s2);margin-bottom:var(--s3);flex-wrap:wrap}.soma-policies__form input,.soma-policies__form select{padding:6px 10px;background:var(--bg);color:var(--t1);border:1px solid var(--bd);border-radius:var(--r);font-size:12px;flex:1;min-width:120px}.soma-policies__submit{padding:6px 16px;background:var(--ok);color:#000;border:none;border-radius:var(--r);cursor:pointer;font-weight:600;font-size:12px}.soma-policies__list{display:flex;flex-direction:column;gap:4px}.soma-policies__row{display:flex;align-items:center;gap:var(--s2);padding:8px var(--s3);background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);font-size:12px}.soma-policies__name{font-weight:600;color:var(--t1);min-width:120px}.soma-policies__badge{font-size:10px;font-weight:700;border:1px solid;border-radius:3px;padding:1px 6px}.soma-policies__scope{color:var(--t2);flex:1}.soma-policies__cond{color:var(--t3);flex:2}.soma-policies__del{background:none;border:none;color:var(--fail);cursor:pointer;font-size:12px;padding:2px 6px}.soma-policies__empty{color:var(--t3);font-size:12px;padding:var(--s4);text-align:center}.soma-knowledge{padding:var(--s3)}.soma-knowledge__filters{display:flex;gap:var(--s2);margin-bottom:var(--s3);align-items:center;flex-wrap:wrap}.soma-knowledge__search{padding:6px 10px;background:var(--bg);color:var(--t1);border:1px solid var(--bd);border-radius:var(--r);font-size:12px;flex:1;min-width:150px}.soma-knowledge__filters select{padding:6px 8px;background:var(--bg);color:var(--t1);border:1px solid var(--bd);border-radius:var(--r);font-size:12px}.soma-knowledge__count{font-size:11px;color:var(--t3)}.soma-knowledge__body{display:flex;gap:var(--s3);min-height:400px}.soma-knowledge__list{flex:1;display:flex;flex-direction:column;gap:2px;overflow-y:auto;max-height:600px}.soma-knowledge__row{display:flex;align-items:center;gap:var(--s2);padding:6px var(--s3);background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);font-size:12px;cursor:pointer;text-align:left;width:100%;color:var(--t1)}.soma-knowledge__row:hover{background:var(--bg3)}.soma-knowledge__row--sel{border-color:var(--info);background:var(--bg3)}.soma-knowledge__type{font-size:10px;font-weight:600;color:var(--t3);min-width:60px;text-transform:uppercase}.soma-knowledge__name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.soma-knowledge__layer{font-size:10px;font-weight:600;border:1px solid;border-radius:3px;padding:1px 4px}.soma-knowledge__empty{color:var(--t3);font-size:12px;padding:var(--s4);text-align:center}.soma-knowledge__more{padding:6px;background:var(--bg3);border:1px solid var(--bd);border-radius:var(--r);color:var(--info);cursor:pointer;font-size:12px;width:100%}.soma-knowledge__detail{flex:1;background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);padding:var(--s3);overflow-y:auto;max-height:600px}.soma-knowledge__detail-header{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s3);flex-wrap:wrap}.soma-knowledge__close{margin-left:auto;background:none;border:none;color:var(--t3);cursor:pointer;font-size:14px}.soma-knowledge__detail-body{margin-bottom:var(--s3)}.soma-knowledge__detail-body pre{font-family:var(--fm);font-size:12px;color:var(--t2);white-space:pre-wrap;word-break:break-word}.soma-knowledge__tags{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:var(--s2)}.soma-knowledge__tag{font-size:10px;padding:2px 6px;background:var(--bg3);border-radius:3px;color:var(--t2)}.soma-knowledge__related{font-size:12px;color:var(--t2)}.soma-knowledge__link{background:none;border:none;color:var(--info);cursor:pointer;font-size:12px;text-decoration:underline;margin-left:4px}.soma-activity{padding:var(--s3)}.soma-activity__header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--s3)}.soma-activity__header h3{font-size:var(--base);font-weight:700}.soma-activity__status{font-size:11px;color:var(--t3)}.soma-activity__list{display:flex;flex-direction:column;gap:2px;max-height:600px;overflow-y:auto}.soma-activity__empty{color:var(--t3);font-size:12px;padding:var(--s5);text-align:center}.soma-activity__event{display:flex;align-items:center;gap:var(--s2);padding:6px var(--s3);background:var(--bg2);border:1px solid var(--bd);border-radius:var(--r);font-size:12px}.soma-activity__icon{font-size:14px;width:20px;text-align:center}.soma-activity__time{font-family:var(--fm);font-size:11px;color:var(--t3);min-width:70px}.soma-activity__action{font-weight:600;color:var(--info);min-width:80px}.soma-activity__desc{color:var(--t2);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|