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.
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>AgentFlow Dashboard</title>
7
- <script type="module" crossorigin src="/assets/index-BgEw2MGK.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-DHcSpTgM.css">
7
+ <script type="module" crossorigin src="/assets/index-DA9m90ZC.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BQBa4cES.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/dist/index.cjs CHANGED
@@ -42,6 +42,11 @@ var fs3 = __toESM(require("fs"), 1);
42
42
  var import_node_http = require("http");
43
43
  var path3 = __toESM(require("path"), 1);
44
44
  var import_node_url2 = require("url");
45
+ var import_agentflow_core3 = require("agentflow-core");
46
+ var import_chokidar2 = __toESM(require("chokidar"), 1);
47
+ var import_express = __toESM(require("express"), 1);
48
+ var import_express_rate_limit = __toESM(require("express-rate-limit"), 1);
49
+ var import_ws = require("ws");
45
50
 
46
51
  // src/config.ts
47
52
  var import_node_fs = require("fs");
@@ -114,12 +119,6 @@ function getProcessPreference(config) {
114
119
  return config.processPreference ?? null;
115
120
  }
116
121
 
117
- // src/server.ts
118
- var import_agentflow_core3 = require("agentflow-core");
119
- var import_chokidar2 = __toESM(require("chokidar"), 1);
120
- var import_express = __toESM(require("express"), 1);
121
- var import_ws = require("ws");
122
-
123
122
  // src/adapters/agentflow.ts
124
123
  var SKIP_FILES = /* @__PURE__ */ new Set([
125
124
  "workers.json",
@@ -131,7 +130,14 @@ var SKIP_FILES = /* @__PURE__ */ new Set([
131
130
  "models.json",
132
131
  "config.json"
133
132
  ]);
134
- var SKIP_SUFFIXES = ["-state.json", "-config.json", "-watch-state.json", ".tmp", ".bak", ".backup"];
133
+ var SKIP_SUFFIXES = [
134
+ "-state.json",
135
+ "-config.json",
136
+ "-watch-state.json",
137
+ ".tmp",
138
+ ".bak",
139
+ ".backup"
140
+ ];
135
141
  var AgentFlowAdapter = class {
136
142
  name = "agentflow";
137
143
  detect(_dirPath) {
@@ -408,8 +414,14 @@ registerAdapter(new AgentFlowAdapter());
408
414
  var PURPOSE_KEYWORDS = [
409
415
  { keywords: ["email", "mail", "inbox", "smtp"], group: "Email Processors" },
410
416
  { keywords: ["monitor", "watch", "alert", "surveillance"], group: "Monitors" },
411
- { keywords: ["digest", "newsletter", "summary", "report", "briefing"], group: "Digests & Reports" },
412
- { keywords: ["curator", "janitor", "distiller", "surveyor", "worker", "indexer"], group: "Workers" },
417
+ {
418
+ keywords: ["digest", "newsletter", "summary", "report", "briefing"],
419
+ group: "Digests & Reports"
420
+ },
421
+ {
422
+ keywords: ["curator", "janitor", "distiller", "surveyor", "worker", "indexer"],
423
+ group: "Workers"
424
+ },
413
425
  { keywords: ["cron", "schedule", "timer", "periodic"], group: "Scheduled Jobs" },
414
426
  { keywords: ["search", "scrape", "crawl", "fetch"], group: "Data Collection" },
415
427
  { keywords: ["embed", "vector", "index"], group: "Embeddings" }
@@ -441,6 +453,7 @@ function capitalize(s) {
441
453
  return s.charAt(0).toUpperCase() + s.slice(1);
442
454
  }
443
455
  function deduplicateAgents(agents) {
456
+ var _a, _b, _c, _d;
444
457
  const tagged = agents.map((a) => ({
445
458
  ...a,
446
459
  ...extractSource(a.agentId)
@@ -457,14 +470,14 @@ function deduplicateAgents(agents) {
457
470
  const mergedIds = /* @__PURE__ */ new Set();
458
471
  const mergedAgents = [];
459
472
  for (const [_key, group] of suffixGroups) {
460
- const suffix = extractSuffix(group[0].localId);
473
+ const suffix = extractSuffix((_a = group[0]) == null ? void 0 : _a.localId);
461
474
  if (group.length < 2) continue;
462
475
  const prefixes = new Set(group.map((a) => a.localId.split("-")[0]));
463
476
  if (prefixes.size < 2) continue;
464
477
  const longPrefixes = [...prefixes].filter((p) => p !== suffix && p.length > 2);
465
478
  if (longPrefixes.length >= 2) continue;
466
479
  const merged = {
467
- agentId: group[0].source === "agentflow" ? suffix : `${group[0].source}:${suffix}`,
480
+ agentId: ((_b = group[0]) == null ? void 0 : _b.source) === "agentflow" ? suffix : `${(_c = group[0]) == null ? void 0 : _c.source}:${suffix}`,
468
481
  displayName: suffix,
469
482
  totalExecutions: group.reduce((s, a) => s + a.totalExecutions, 0),
470
483
  successfulExecutions: group.reduce((s, a) => s + a.successfulExecutions, 0),
@@ -475,7 +488,7 @@ function deduplicateAgents(agents) {
475
488
  triggers: {},
476
489
  recentActivity: group.flatMap((a) => a.recentActivity).sort((a, b) => b.timestamp - a.timestamp).slice(0, 50),
477
490
  sources: group.map((a) => a.agentId),
478
- adapterSource: group[0].source
491
+ adapterSource: (_d = group[0]) == null ? void 0 : _d.source
479
492
  };
480
493
  merged.successRate = merged.totalExecutions > 0 ? merged.successfulExecutions / merged.totalExecutions * 100 : 0;
481
494
  const totalExecTime = group.reduce((s, a) => s + a.avgExecutionTime * a.totalExecutions, 0);
@@ -891,7 +904,9 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
891
904
  ...getSkipFiles(this.userConfig)
892
905
  ]);
893
906
  this.userSkipDirs = new Set(getSkipDirectories(this.userConfig));
894
- this.allWatchDirs = [...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))];
907
+ this.allWatchDirs = [
908
+ ...new Set([this.tracesDir, ...this.dataDirs].map((d) => path.resolve(d)))
909
+ ];
895
910
  this.ensureTracesDir();
896
911
  this.loadExistingFiles();
897
912
  this.archiveOldTraces();
@@ -901,7 +916,7 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
901
916
  /** Move trace files older than maxAgeMs into archive/YYYY-MM/ subdirectories. */
902
917
  archiveOldTraces() {
903
918
  const cutoff = Date.now() - this.maxAgeMs;
904
- let archived = 0;
919
+ const _archived = 0;
905
920
  for (const dir of this.allWatchDirs) {
906
921
  if (!fs.existsSync(dir)) continue;
907
922
  try {
@@ -918,7 +933,8 @@ var TraceWatcher = class _TraceWatcher extends import_node_events.EventEmitter {
918
933
  try {
919
934
  const entries = fs.readdirSync(dir, { withFileTypes: true });
920
935
  for (const entry of entries) {
921
- if (entry.name.startsWith(".") || entry.name === "archive" || this.userSkipDirs.has(entry.name)) continue;
936
+ if (entry.name.startsWith(".") || entry.name === "archive" || this.userSkipDirs.has(entry.name))
937
+ continue;
922
938
  const fullPath = path.join(dir, entry.name);
923
939
  if (entry.isDirectory()) {
924
940
  archived += this.archiveDirectory(fullPath, cutoff, depth + 1);
@@ -2165,7 +2181,9 @@ var path2 = __toESM(require("path"), 1);
2165
2181
  var import_node_url = require("url");
2166
2182
  var import_meta = {};
2167
2183
  var __cliDirname = path2.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
2168
- var VERSION = JSON.parse(fs2.readFileSync(path2.resolve(__cliDirname, "../package.json"), "utf-8")).version;
2184
+ var VERSION = JSON.parse(
2185
+ fs2.readFileSync(path2.resolve(__cliDirname, "../package.json"), "utf-8")
2186
+ ).version;
2169
2187
  function getLanAddress() {
2170
2188
  const interfaces = os.networkInterfaces();
2171
2189
  for (const name of Object.keys(interfaces)) {
@@ -2423,6 +2441,17 @@ var DashboardServer = class {
2423
2441
  userConfig;
2424
2442
  configPath;
2425
2443
  setupExpress() {
2444
+ this.app.use(
2445
+ "/api/",
2446
+ (0, import_express_rate_limit.default)({
2447
+ windowMs: 60 * 1e3,
2448
+ // 1 minute
2449
+ max: 300,
2450
+ // 300 requests per minute per IP
2451
+ standardHeaders: true,
2452
+ legacyHeaders: false
2453
+ })
2454
+ );
2426
2455
  if (this.config.enableCors) {
2427
2456
  this.app.use((_req, res, next) => {
2428
2457
  res.header("Access-Control-Allow-Origin", "*");
@@ -2665,6 +2694,7 @@ var DashboardServer = class {
2665
2694
  }
2666
2695
  });
2667
2696
  this.app.get("/api/process-model/:agentId", (req, res) => {
2697
+ var _a, _b;
2668
2698
  try {
2669
2699
  const agentId = req.params.agentId;
2670
2700
  const allTraces = this.watcher.getTracesByAgent(agentId);
@@ -2682,8 +2712,8 @@ var DashboardServer = class {
2682
2712
  const nodeArr = Object.values(nodes);
2683
2713
  const sorted = nodeArr.filter((n) => n.name && typeof n.startTime === "number" && n.startTime > 0).sort((a, b) => (a.startTime ?? 0) - (b.startTime ?? 0));
2684
2714
  for (let i = 0; i < sorted.length - 1; i++) {
2685
- const from = sorted[i].name;
2686
- const to = sorted[i + 1].name;
2715
+ const from = (_a = sorted[i]) == null ? void 0 : _a.name;
2716
+ const to = (_b = sorted[i + 1]) == null ? void 0 : _b.name;
2687
2717
  const key = `${from}|||${to}`;
2688
2718
  transMap.set(key, (transMap.get(key) ?? 0) + 1);
2689
2719
  }
@@ -2782,7 +2812,11 @@ var DashboardServer = class {
2782
2812
  try {
2783
2813
  const reportPath = path3.join(somaVault, "..", "soma-report.json");
2784
2814
  if (!fs3.existsSync(reportPath)) {
2785
- return res.json({ available: false, teaser: false, message: "No report file yet. Run soma watch." });
2815
+ return res.json({
2816
+ available: false,
2817
+ teaser: false,
2818
+ message: "No report file yet. Run soma watch."
2819
+ });
2786
2820
  }
2787
2821
  const report = JSON.parse(fs3.readFileSync(reportPath, "utf-8"));
2788
2822
  res.json(report);
@@ -2806,7 +2840,9 @@ var DashboardServer = class {
2806
2840
  available: true,
2807
2841
  layers: report.layers ?? { archive: 0, working: 0, emerging: 0, canon: 0 },
2808
2842
  governance: report.governance ?? { pending: 0, promoted: 0, rejected: 0 },
2809
- insights: (report.insights ?? []).filter((i) => i.layer === "emerging" && i.proposal_status === "pending"),
2843
+ insights: (report.insights ?? []).filter(
2844
+ (i) => i.layer === "emerging" && i.proposal_status === "pending"
2845
+ ),
2810
2846
  canon: (report.insights ?? []).filter((i) => i.layer === "canon"),
2811
2847
  generatedAt: report.generatedAt
2812
2848
  });
@@ -2815,21 +2851,23 @@ var DashboardServer = class {
2815
2851
  res.status(500).json({ available: false, message: "Failed to read governance data" });
2816
2852
  }
2817
2853
  });
2818
- const sanitizeArg = (s) => s.replace(/[^a-zA-Z0-9_\-.:]/g, "");
2819
- const sanitizeReason = (s) => s.replace(/["`$\\]/g, "").slice(0, 500);
2854
+ const isValidId = (s) => /^[a-zA-Z0-9_\-.:]+$/.test(s);
2820
2855
  this.app.post("/api/soma/governance/promote", (req, res) => {
2821
2856
  var _a;
2822
2857
  const somaVault = this.config.somaVault;
2823
2858
  if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
2824
2859
  const { entryId } = req.body ?? {};
2825
- if (!entryId) return res.status(400).json({ error: "entryId required" });
2860
+ if (!entryId || !isValidId(String(entryId)))
2861
+ return res.status(400).json({ error: "Invalid entryId" });
2826
2862
  try {
2827
- const { execSync: execSync2 } = require("child_process");
2828
- const safeId = sanitizeArg(String(entryId));
2829
- const result = execSync2(`npx soma governance promote ${safeId} --vault "${somaVault}"`, {
2830
- encoding: "utf-8",
2831
- timeout: 1e4
2832
- });
2863
+ const result = (0, import_node_child_process.execFileSync)(
2864
+ "npx",
2865
+ ["soma", "governance", "promote", String(entryId), "--vault", somaVault],
2866
+ {
2867
+ encoding: "utf-8",
2868
+ timeout: 1e4
2869
+ }
2870
+ );
2833
2871
  res.json({ success: true, message: result.trim() });
2834
2872
  } catch (error) {
2835
2873
  res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
@@ -2840,15 +2878,27 @@ var DashboardServer = class {
2840
2878
  const somaVault = this.config.somaVault;
2841
2879
  if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
2842
2880
  const { entryId, reason } = req.body ?? {};
2843
- if (!entryId || !reason) return res.status(400).json({ error: "entryId and reason required" });
2881
+ if (!entryId || !isValidId(String(entryId)))
2882
+ return res.status(400).json({ error: "Invalid entryId" });
2883
+ if (!reason || typeof reason !== "string")
2884
+ return res.status(400).json({ error: "reason required" });
2844
2885
  try {
2845
- const { execSync: execSync2 } = require("child_process");
2846
- const safeId = sanitizeArg(String(entryId));
2847
- const safeReason = sanitizeReason(String(reason));
2848
- const result = execSync2(`npx soma governance reject ${safeId} "${safeReason}" --vault "${somaVault}"`, {
2849
- encoding: "utf-8",
2850
- timeout: 1e4
2851
- });
2886
+ const result = (0, import_node_child_process.execFileSync)(
2887
+ "npx",
2888
+ [
2889
+ "soma",
2890
+ "governance",
2891
+ "reject",
2892
+ String(entryId),
2893
+ String(reason).slice(0, 500),
2894
+ "--vault",
2895
+ somaVault
2896
+ ],
2897
+ {
2898
+ encoding: "utf-8",
2899
+ timeout: 1e4
2900
+ }
2901
+ );
2852
2902
  res.json({ success: true, message: result.trim() });
2853
2903
  } catch (error) {
2854
2904
  res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
@@ -2858,13 +2908,16 @@ var DashboardServer = class {
2858
2908
  var _a;
2859
2909
  const somaVault = this.config.somaVault;
2860
2910
  if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
2911
+ if (!isValidId(String(req.params.id))) return res.status(400).json({ error: "Invalid id" });
2861
2912
  try {
2862
- const { execSync: execSync2 } = require("child_process");
2863
- const safeId = sanitizeArg(String(req.params.id));
2864
- const result = execSync2(`npx soma governance show ${safeId} --vault "${somaVault}"`, {
2865
- encoding: "utf-8",
2866
- timeout: 1e4
2867
- });
2913
+ const result = (0, import_node_child_process.execFileSync)(
2914
+ "npx",
2915
+ ["soma", "governance", "show", String(req.params.id), "--vault", somaVault],
2916
+ {
2917
+ encoding: "utf-8",
2918
+ timeout: 1e4
2919
+ }
2920
+ );
2868
2921
  res.json({ available: true, output: result.trim() });
2869
2922
  } catch (error) {
2870
2923
  res.status(404).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
@@ -2887,16 +2940,24 @@ var DashboardServer = class {
2887
2940
  const somaVault = this.config.somaVault;
2888
2941
  if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
2889
2942
  const { name, enforcement, scope, conditions } = req.body ?? {};
2890
- if (!name) return res.status(400).json({ error: "name required" });
2943
+ if (!name || !isValidId(String(name)))
2944
+ return res.status(400).json({ error: "Invalid policy name" });
2945
+ const enf = String(enforcement || "warn");
2946
+ if (!isValidId(enf)) return res.status(400).json({ error: "Invalid enforcement value" });
2891
2947
  try {
2892
- const safeName = sanitizeArg(String(name));
2893
- const safeEnf = sanitizeArg(String(enforcement || "warn"));
2894
- const safeScope = sanitizeReason(String(scope || "all"));
2895
- const safeCond = sanitizeReason(String(conditions || ""));
2896
- const result = (0, import_node_child_process.execSync)(
2897
- `npx soma policy create "${safeName}" --enforcement ${safeEnf} --scope "${safeScope}" --conditions "${safeCond}" --vault "${somaVault}"`,
2898
- { encoding: "utf-8", timeout: 1e4 }
2899
- );
2948
+ const args = [
2949
+ "soma",
2950
+ "policy",
2951
+ "create",
2952
+ String(name),
2953
+ "--enforcement",
2954
+ enf,
2955
+ "--vault",
2956
+ somaVault
2957
+ ];
2958
+ if (scope) args.push("--scope", String(scope).slice(0, 500));
2959
+ if (conditions) args.push("--conditions", String(conditions).slice(0, 500));
2960
+ const result = (0, import_node_child_process.execFileSync)("npx", args, { encoding: "utf-8", timeout: 1e4 });
2900
2961
  res.json({ success: true, message: result.trim() });
2901
2962
  } catch (error) {
2902
2963
  res.status(400).json({ error: ((_a = error.stderr) == null ? void 0 : _a.trim()) || error.message });
@@ -2906,11 +2967,16 @@ var DashboardServer = class {
2906
2967
  var _a;
2907
2968
  const somaVault = this.config.somaVault;
2908
2969
  if (!somaVault) return res.status(400).json({ error: "Soma vault not configured" });
2970
+ if (!isValidId(String(req.params.name)))
2971
+ return res.status(400).json({ error: "Invalid policy name" });
2909
2972
  try {
2910
- const safeName = sanitizeArg(String(req.params.name));
2911
- const result = (0, import_node_child_process.execSync)(
2912
- `npx soma policy delete "${safeName}" --vault "${somaVault}"`,
2913
- { encoding: "utf-8", timeout: 1e4 }
2973
+ const result = (0, import_node_child_process.execFileSync)(
2974
+ "npx",
2975
+ ["soma", "policy", "delete", String(req.params.name), "--vault", somaVault],
2976
+ {
2977
+ encoding: "utf-8",
2978
+ timeout: 1e4
2979
+ }
2914
2980
  );
2915
2981
  res.json({ success: true, message: result.trim() });
2916
2982
  } catch (error) {
@@ -2928,16 +2994,28 @@ var DashboardServer = class {
2928
2994
  ...(report.agents ?? []).map((a) => ({ ...a, type: "agent", id: a.name })),
2929
2995
  ...(report.insights ?? []).map((i, idx) => {
2930
2996
  var _a;
2931
- return { ...i, type: i.type || "insight", id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}` };
2997
+ return {
2998
+ ...i,
2999
+ type: i.type || "insight",
3000
+ id: ((_a = i.title) == null ? void 0 : _a.replace(/\s+/g, "-").toLowerCase()) || `insight-${idx}`
3001
+ };
2932
3002
  }),
2933
3003
  ...(report.policies ?? []).map((p) => ({ ...p, type: "policy", id: p.name }))
2934
3004
  ];
2935
- const { type, layer, q, limit: limitStr, offset: offsetStr } = req.query;
3005
+ const {
3006
+ type,
3007
+ layer,
3008
+ q,
3009
+ limit: limitStr,
3010
+ offset: offsetStr
3011
+ } = req.query;
2936
3012
  if (type) entities = entities.filter((e) => e.type === type);
2937
3013
  if (layer) entities = entities.filter((e) => e.layer === layer);
2938
3014
  if (q) {
2939
3015
  const lq = q.toLowerCase();
2940
- entities = entities.filter((e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq));
3016
+ entities = entities.filter(
3017
+ (e) => (e.name || e.title || "").toLowerCase().includes(lq) || (e.claim || e.body || "").toLowerCase().includes(lq)
3018
+ );
2941
3019
  }
2942
3020
  const total = entities.length;
2943
3021
  const offset = parseInt(offsetStr || "0", 10);
@@ -3028,9 +3106,7 @@ var DashboardServer = class {
3028
3106
  const orphans = uniqueProcesses.filter(
3029
3107
  (p) => !allKnownPids.has(p.pid) && p.pid !== process.pid && p.pid !== process.ppid
3030
3108
  );
3031
- const problems = services.flatMap(
3032
- (s) => s.audit.problems.map((p) => `[${s.name}] ${p}`)
3033
- );
3109
+ const problems = services.flatMap((s) => s.audit.problems.map((p) => `[${s.name}] ${p}`));
3034
3110
  const result = {
3035
3111
  // Backward-compatible fields from primary service
3036
3112
  pidFile: (primary == null ? void 0 : primary.audit.pidFile) ?? null,
@@ -3085,19 +3161,24 @@ var DashboardServer = class {
3085
3161
  }
3086
3162
  } catch {
3087
3163
  }
3088
- const watched = [...new Set([
3089
- this.config.tracesDir,
3090
- ...this.config.dataDirs || [],
3091
- ...extraDirs
3092
- ].map((w) => path3.resolve(w)))];
3164
+ const watched = [
3165
+ ...new Set(
3166
+ [this.config.tracesDir, ...this.config.dataDirs || [], ...extraDirs].map(
3167
+ (w) => path3.resolve(w)
3168
+ )
3169
+ )
3170
+ ];
3093
3171
  const discovered = [];
3094
3172
  const svcNames = getSystemdServices(this.userConfig);
3095
3173
  if (svcNames.length > 0) {
3096
3174
  try {
3097
- const { execSync: execSync2 } = require("child_process");
3098
- const raw = execSync2(
3099
- `systemctl --user show --property=ExecStart --no-pager ${svcNames.join(" ")} 2>/dev/null`,
3100
- { encoding: "utf8", timeout: 5e3 }
3175
+ const raw = (0, import_node_child_process.execFileSync)(
3176
+ "systemctl",
3177
+ ["--user", "show", "--property=ExecStart", "--no-pager", ...svcNames],
3178
+ {
3179
+ encoding: "utf8",
3180
+ timeout: 5e3
3181
+ }
3101
3182
  );
3102
3183
  for (const line of raw.split("\n")) {
3103
3184
  const match = line.match(/path=([^\s;]+)/);
@@ -3129,10 +3210,19 @@ var DashboardServer = class {
3129
3210
  this.app.post("/api/directories", import_express.default.json(), (req, res) => {
3130
3211
  try {
3131
3212
  const { add, remove } = req.body;
3132
- if (add && !fs3.existsSync(add)) {
3133
- return res.status(400).json({ error: `Directory does not exist: ${add}` });
3213
+ if (add) {
3214
+ const resolved = path3.resolve(add);
3215
+ if (resolved !== add || add.includes("..")) {
3216
+ return res.status(400).json({ error: "Invalid directory path" });
3217
+ }
3218
+ if (!fs3.existsSync(resolved)) {
3219
+ return res.status(400).json({ error: `Directory does not exist: ${add}` });
3220
+ }
3134
3221
  }
3135
- const configPath = path3.join(process.env.HOME ?? "/home/trader", ".agentflow/dashboard-config.json");
3222
+ const configPath = path3.join(
3223
+ process.env.HOME ?? "/home/trader",
3224
+ ".agentflow/dashboard-config.json"
3225
+ );
3136
3226
  let config = {};
3137
3227
  try {
3138
3228
  if (fs3.existsSync(configPath)) {
@@ -3342,13 +3432,31 @@ var DashboardServer = class {
3342
3432
  isVirtual: false
3343
3433
  });
3344
3434
  }
3345
- const rootSteps = new Set(model.steps);
3346
- const childSteps = new Set(model.transitions.map((t) => t.to));
3347
- const leafSteps = new Set(model.steps);
3348
- for (const t of model.transitions) {
3349
- }
3350
- nodes.push({ id: "[START]", label: "[START]", count: model.totalGraphs, frequency: 1, avgDuration: 0, failRate: 0, p95Duration: 0, isVirtual: true });
3351
- nodes.push({ id: "[END]", label: "[END]", count: model.totalGraphs, frequency: 1, avgDuration: 0, failRate: 0, p95Duration: 0, isVirtual: true });
3435
+ const _rootSteps = new Set(model.steps);
3436
+ const _childSteps = new Set(model.transitions.map((t) => t.to));
3437
+ const _leafSteps = new Set(model.steps);
3438
+ for (const _t of model.transitions) {
3439
+ }
3440
+ nodes.push({
3441
+ id: "[START]",
3442
+ label: "[START]",
3443
+ count: model.totalGraphs,
3444
+ frequency: 1,
3445
+ avgDuration: 0,
3446
+ failRate: 0,
3447
+ p95Duration: 0,
3448
+ isVirtual: true
3449
+ });
3450
+ nodes.push({
3451
+ id: "[END]",
3452
+ label: "[END]",
3453
+ count: model.totalGraphs,
3454
+ frequency: 1,
3455
+ avgDuration: 0,
3456
+ failRate: 0,
3457
+ p95Duration: 0,
3458
+ isVirtual: true
3459
+ });
3352
3460
  const edges = model.transitions.map((t) => ({
3353
3461
  source: t.from,
3354
3462
  target: t.to,
@@ -3368,7 +3476,10 @@ var DashboardServer = class {
3368
3476
  }
3369
3477
  }
3370
3478
  const maxEdgeCount = Math.max(...edges.map((e) => e.count), 1);
3371
- const maxNodeCount = Math.max(...nodes.filter((n) => !n.isVirtual).map((n) => n.count), 1);
3479
+ const maxNodeCount = Math.max(
3480
+ ...nodes.filter((n) => !n.isVirtual).map((n) => n.count),
3481
+ 1
3482
+ );
3372
3483
  return { agentId, totalTraces: model.totalGraphs, nodes, edges, maxEdgeCount, maxNodeCount };
3373
3484
  }
3374
3485
  /**
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  AgentStats,
4
4
  DashboardServer,
5
5
  TraceWatcher
6
- } from "./chunk-E5RJCBK2.js";
6
+ } from "./chunk-YLQ5MVCW.js";
7
7
  export {
8
8
  AgentStats,
9
9
  DashboardServer,