@solongate/proxy 0.64.0 → 0.65.0

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/index.js CHANGED
@@ -7051,20 +7051,31 @@ function HBar({ label, value, max, width: width2, color }) {
7051
7051
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2591".repeat(Math.max(0, width2 - filled)) + " " + value })
7052
7052
  ] });
7053
7053
  }
7054
- function SectionTitle({ children, width: width2 }) {
7055
- const label = `\u2500\u2500 ${children} `;
7056
- return /* @__PURE__ */ jsx2(Text2, { color: theme.dim, wrap: "truncate", children: label + "\u2500".repeat(Math.max(0, width2 - label.length)) });
7054
+ function PaneTitle({ label, extra, width: width2 }) {
7055
+ const head = `\u2500 `;
7056
+ const tail = extra ? ` ${extra} ` : " ";
7057
+ const used = head.length + 1 + label.length + tail.length;
7058
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7059
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: head }),
7060
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.accentBright, bold: true, children: [
7061
+ "\u258E",
7062
+ label
7063
+ ] }),
7064
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
7065
+ ] });
7057
7066
  }
7058
7067
  function LivePanel({ active: active2 }) {
7059
7068
  const stats = useLoader(() => api.stats.get());
7060
7069
  const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
7061
7070
  const feed = useLoader(() => api.audit.list({ limit: 50 }));
7062
7071
  const insights = useLoader(() => api.stats.securityInsights(7));
7072
+ const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
7063
7073
  usePoll(() => {
7064
7074
  stats.reload();
7065
7075
  ts.reload();
7066
7076
  feed.reload();
7067
7077
  }, 2e3, active2);
7078
+ usePoll(sessions.reload, 5e3, active2);
7068
7079
  usePoll(insights.reload, 1e4, active2);
7069
7080
  const [tick, setTick] = useState2(0);
7070
7081
  useEffect2(() => {
@@ -7090,89 +7101,108 @@ function LivePanel({ active: active2 }) {
7090
7101
  const minuteRaw = (ins.activity?.minute ?? []).map((b) => b.count);
7091
7102
  const hourRaw = (ins.activity?.hour ?? []).map((b) => b.count);
7092
7103
  const minuteLive = minuteRaw.some((c2) => c2 > 0);
7093
- const minuteSeries = minuteLive ? minuteRaw : hourRaw;
7094
- const activityLabel = minuteLive ? "calls/min \xB7 last 60m" : "calls/hour \xB7 last 24h";
7104
+ const rateSeries = minuteLive ? minuteRaw : hourRaw;
7105
+ const rateLabel = minuteLive ? "calls/min \xB7 60m" : "calls/hour \xB7 24h";
7095
7106
  const limit = minuteLive ? ins.layers?.rateLimit?.perMinute ?? 0 : 0;
7107
+ const rlMode = ins.layers?.rateLimit?.mode ?? "?";
7108
+ const dlpMode = ins.layers?.dlp?.mode ?? "?";
7096
7109
  const denySeries = (ts.data?.timeseries ?? []).map((p) => p.denied);
7097
7110
  const spin = SPIN[tick % SPIN.length];
7098
- const cursor = tick % 4 < 2 ? "\u258C" : " ";
7099
7111
  const toolCounts = /* @__PURE__ */ new Map();
7100
- const permCounts = /* @__PURE__ */ new Map();
7112
+ let dlpBufHits = 0;
7101
7113
  for (const e of buffer) {
7102
7114
  toolCounts.set(e.tool_name, (toolCounts.get(e.tool_name) ?? 0) + 1);
7103
- const perm = (e.permission || "?").toUpperCase();
7104
- permCounts.set(perm, (permCounts.get(perm) ?? 0) + 1);
7105
- }
7106
- const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
7107
- const maxTool = topTools[0]?.[1] ?? 1;
7108
- const perms = ["READ", "WRITE", "EXECUTE", "NETWORK"].map((p) => [p, permCounts.get(p) ?? 0]);
7109
- const maxPerm = Math.max(1, ...perms.map(([, c2]) => c2));
7110
- const dlpTop = (ins.dlpByPattern ?? []).slice(0, 2);
7115
+ if (e.dlp_matches?.length) dlpBufHits++;
7116
+ }
7117
+ const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
7118
+ const dlpBars = (ins.dlpByPattern ?? []).slice(0, 3);
7119
+ const maxDlp = dlpBars[0]?.count ?? 1;
7120
+ const dlpRecent = (ins.dlpHits ?? []).slice(0, 2);
7111
7121
  const denials = buffer.filter((e) => e.decision !== "ALLOW");
7112
7122
  const lastDeny = denials[denials.length - 1];
7113
- const bursts = limit > 0 ? minuteSeries.filter((c2) => c2 > limit).length : 0;
7114
- const bigH = rows >= 34 ? 6 : 4;
7115
- const colH = rows >= 34 ? 6 : 5;
7116
- const streamRows = Math.max(4, rows - (2 + 1 + bigH + 1 + colH + 1 + 1));
7123
+ const bursts = limit > 0 ? rateSeries.filter((c2) => c2 > limit).length : 0;
7124
+ const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
7125
+ const sessCounts = sessions.data?.counts;
7126
+ const chartH = rows >= 36 ? 6 : 4;
7127
+ const colH = rows >= 32 ? 6 : 5;
7128
+ const streamRows = Math.max(4, rows - 6 - chartH - colH);
7117
7129
  const tail = buffer.slice(-streamRows);
7118
- const colW = Math.max(18, Math.floor((cols - 6) / 3));
7130
+ const colW = Math.max(20, Math.floor((cols - 6) / 3));
7131
+ const innerW = cols - 2;
7119
7132
  if (stats.error) return /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
7120
7133
  "\u2717 ",
7121
7134
  stats.error
7122
7135
  ] });
7136
+ const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25D0", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
7123
7137
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
7124
7138
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7125
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE::LIVE " }),
7126
- /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7127
- " ",
7128
- spin,
7129
- " up ",
7130
- fmtUp(Date.now() - startRef.current),
7131
- " \xB7 ",
7132
- hhmmss(Date.now()),
7133
- " \xB7 poll 2s"
7134
- ] }),
7135
- lastDeny ? /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
7136
- " \u26A0 ",
7137
- hhmmss(lastDeny.created_at),
7138
- " ",
7139
- lastDeny.tool_name,
7140
- " denied"
7141
- ] }) : null,
7142
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " esc menu \xB7 q quit" })
7139
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " \u26E8 SOLONGATE LIVE " }),
7140
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} ` }),
7141
+ lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.created_at)} ${lastDeny.tool_name} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 no recent denials " })
7143
7142
  ] }),
7144
7143
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7145
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "calls " }),
7144
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
7146
7145
  /* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
7147
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " allow " }),
7146
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
7148
7147
  /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
7149
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " deny " }),
7148
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
7150
7149
  /* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
7151
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " bursts(60m) " }),
7150
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp(buf) " }),
7151
+ /* @__PURE__ */ jsx2(Text2, { color: dlpBufHits ? theme.bad : theme.dim, children: dlpBufHits }),
7152
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 bursts " }),
7152
7153
  /* @__PURE__ */ jsx2(Text2, { color: bursts ? theme.bad : theme.dim, children: bursts }),
7153
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " limit/min " }),
7154
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: limit || "off" }),
7155
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " policies " }),
7156
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" })
7154
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
7155
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC` : "\xB7" }),
7156
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
7157
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
7158
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7157
7159
  ] }),
7158
- /* @__PURE__ */ jsx2(SectionTitle, { width: cols - 2, children: `ACTIVITY ${activityLabel} \xB7 peak ${Math.max(0, ...minuteSeries)}${limit ? ` \xB7 limit ${limit} (red = over)` : ""}` }),
7159
- minuteSeries.length ? /* @__PURE__ */ jsx2(ColumnChart, { series: minuteSeries, height: bigH, width: cols - 2, color: theme.accent, limit }) : /* @__PURE__ */ jsx2(Box2, { height: bigH, children: /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: insights.loading ? "loading activity\u2026" : "no activity data" }) }),
7160
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "RATE LIMIT", extra: `${rateLabel} \xB7 peak ${Math.max(0, ...rateSeries)} \xB7 limit ${limit || "off"} \xB7 mode ${rlMode}${limit ? " \xB7 red = over" : ""}`, width: innerW }),
7161
+ rateSeries.length ? /* @__PURE__ */ jsx2(ColumnChart, { series: rateSeries, height: chartH, width: innerW, color: theme.accent, limit }) : /* @__PURE__ */ jsx2(Box2, { height: chartH, children: /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: insights.loading ? `${spin} loading activity\u2026` : "no activity data" }) }),
7160
7162
  /* @__PURE__ */ jsxs2(Box2, { children: [
7161
7163
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
7162
- /* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "DENIALS 24h" }),
7164
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "DENIALS", extra: "24h", width: colW }),
7163
7165
  /* @__PURE__ */ jsx2(ColumnChart, { series: denySeries, height: colH - 1, width: colW, color: theme.bad })
7164
7166
  ] }),
7165
7167
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
7166
- /* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "TOP TOOLS (buffer)" }),
7167
- topTools.length ? topTools.slice(0, colH - 1).map(([tool, count]) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(tool, 9), value: count, max: maxTool, width: Math.max(6, colW - 16), color: theme.accent }, tool)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no traffic yet" })
7168
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "DLP", extra: `7d \xB7 mode ${dlpMode}`, width: colW }),
7169
+ dlpBars.length ? dlpBars.map((d) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d.pattern, 9), value: d.count, max: maxDlp, width: Math.max(6, colW - 16), color: theme.bad }, d.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no dlp hits" }),
7170
+ dlpRecent.map((h, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7171
+ /* @__PURE__ */ jsx2(Text2, { color: h.blocked ? theme.accent : theme.bad, children: h.blocked ? "\u2298" : "\u26A0" }),
7172
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7173
+ " ",
7174
+ truncate2(h.pattern, colW - 14),
7175
+ " ",
7176
+ ago(h.at),
7177
+ " ago"
7178
+ ] })
7179
+ ] }, i))
7168
7180
  ] }),
7169
7181
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
7170
- /* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "PERMISSIONS \xB7 DLP" }),
7171
- perms.map(([p, c2]) => /* @__PURE__ */ jsx2(HBar, { label: p, value: c2, max: maxPerm, width: Math.max(6, colW - 16), color: p === "EXECUTE" ? theme.warn : theme.accent }, p)),
7172
- dlpTop.map((d) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.bad, children: "DLP " + truncate2(d.pattern, colW - 10) + " \xD7" + d.count }, d.pattern))
7182
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} active` : "", width: colW }),
7183
+ sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no sessions" }) : null,
7184
+ sess.slice(0, colH - 1).map((a) => {
7185
+ const dot = sessionDot(a.status);
7186
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7187
+ /* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
7188
+ dot.ch,
7189
+ " "
7190
+ ] }),
7191
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(a.agent_name ?? a.session_id, 12).padEnd(13) }),
7192
+ /* @__PURE__ */ jsxs2(Text2, { children: [
7193
+ String(a.total_calls).padStart(4),
7194
+ "c "
7195
+ ] }),
7196
+ /* @__PURE__ */ jsxs2(Text2, { color: a.denied_calls ? theme.bad : theme.dim, children: [
7197
+ String(a.denied_calls).padStart(3),
7198
+ "d "
7199
+ ] }),
7200
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(a.last_seen_at) })
7201
+ ] }, a.session_id);
7202
+ })
7173
7203
  ] })
7174
7204
  ] }),
7175
- /* @__PURE__ */ jsx2(SectionTitle, { width: cols - 2, children: `TOOL STREAM \xB7 live tail \xB7 ${buffer.length} buffered` }),
7205
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `live tail \xB7 ${buffer.length} buffered`, width: innerW }),
7176
7206
  tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
7177
7207
  tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7178
7208
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
@@ -7187,15 +7217,13 @@ function LivePanel({ active: active2 }) {
7187
7217
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
7188
7218
  ] }, e.id)),
7189
7219
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7190
- /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "root@solongate" }),
7191
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ":" }),
7192
- /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: "~/live" }),
7193
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "$ " }),
7194
- /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: cursor })
7220
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " \u25B2 LIVE " }),
7221
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
7222
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
7195
7223
  ] })
7196
7224
  ] });
7197
7225
  }
7198
- var SPIN, hhmmss, fmtUp;
7226
+ var SPIN, BG, hhmmss, fmtUp;
7199
7227
  var init_Live = __esm({
7200
7228
  "src/tui/panels/Live.tsx"() {
7201
7229
  "use strict";
@@ -7203,6 +7231,7 @@ var init_Live = __esm({
7203
7231
  init_hooks();
7204
7232
  init_theme();
7205
7233
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7234
+ BG = "#12234f";
7206
7235
  hhmmss = (ts) => {
7207
7236
  const d = new Date(ts);
7208
7237
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
package/dist/tui/index.js CHANGED
@@ -432,6 +432,7 @@ function usePoll(reload, intervalMs, enabled = true) {
432
432
  // src/tui/panels/Live.tsx
433
433
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
434
434
  var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
435
+ var BG = "#12234f";
435
436
  var hhmmss = (ts) => {
436
437
  const d = new Date(ts);
437
438
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -470,20 +471,31 @@ function HBar({ label, value, max, width, color }) {
470
471
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2591".repeat(Math.max(0, width - filled)) + " " + value })
471
472
  ] });
472
473
  }
473
- function SectionTitle({ children, width }) {
474
- const label = `\u2500\u2500 ${children} `;
475
- return /* @__PURE__ */ jsx2(Text2, { color: theme.dim, wrap: "truncate", children: label + "\u2500".repeat(Math.max(0, width - label.length)) });
474
+ function PaneTitle({ label, extra, width }) {
475
+ const head = `\u2500 `;
476
+ const tail = extra ? ` ${extra} ` : " ";
477
+ const used = head.length + 1 + label.length + tail.length;
478
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
479
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: head }),
480
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.accentBright, bold: true, children: [
481
+ "\u258E",
482
+ label
483
+ ] }),
484
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
485
+ ] });
476
486
  }
477
487
  function LivePanel({ active: active2 }) {
478
488
  const stats = useLoader(() => api.stats.get());
479
489
  const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
480
490
  const feed = useLoader(() => api.audit.list({ limit: 50 }));
481
491
  const insights = useLoader(() => api.stats.securityInsights(7));
492
+ const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
482
493
  usePoll(() => {
483
494
  stats.reload();
484
495
  ts.reload();
485
496
  feed.reload();
486
497
  }, 2e3, active2);
498
+ usePoll(sessions.reload, 5e3, active2);
487
499
  usePoll(insights.reload, 1e4, active2);
488
500
  const [tick, setTick] = useState2(0);
489
501
  useEffect2(() => {
@@ -509,89 +521,108 @@ function LivePanel({ active: active2 }) {
509
521
  const minuteRaw = (ins.activity?.minute ?? []).map((b) => b.count);
510
522
  const hourRaw = (ins.activity?.hour ?? []).map((b) => b.count);
511
523
  const minuteLive = minuteRaw.some((c2) => c2 > 0);
512
- const minuteSeries = minuteLive ? minuteRaw : hourRaw;
513
- const activityLabel = minuteLive ? "calls/min \xB7 last 60m" : "calls/hour \xB7 last 24h";
524
+ const rateSeries = minuteLive ? minuteRaw : hourRaw;
525
+ const rateLabel = minuteLive ? "calls/min \xB7 60m" : "calls/hour \xB7 24h";
514
526
  const limit = minuteLive ? ins.layers?.rateLimit?.perMinute ?? 0 : 0;
527
+ const rlMode = ins.layers?.rateLimit?.mode ?? "?";
528
+ const dlpMode = ins.layers?.dlp?.mode ?? "?";
515
529
  const denySeries = (ts.data?.timeseries ?? []).map((p) => p.denied);
516
530
  const spin = SPIN[tick % SPIN.length];
517
- const cursor = tick % 4 < 2 ? "\u258C" : " ";
518
531
  const toolCounts = /* @__PURE__ */ new Map();
519
- const permCounts = /* @__PURE__ */ new Map();
532
+ let dlpBufHits = 0;
520
533
  for (const e of buffer) {
521
534
  toolCounts.set(e.tool_name, (toolCounts.get(e.tool_name) ?? 0) + 1);
522
- const perm = (e.permission || "?").toUpperCase();
523
- permCounts.set(perm, (permCounts.get(perm) ?? 0) + 1);
535
+ if (e.dlp_matches?.length) dlpBufHits++;
524
536
  }
525
- const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
526
- const maxTool = topTools[0]?.[1] ?? 1;
527
- const perms = ["READ", "WRITE", "EXECUTE", "NETWORK"].map((p) => [p, permCounts.get(p) ?? 0]);
528
- const maxPerm = Math.max(1, ...perms.map(([, c2]) => c2));
529
- const dlpTop = (ins.dlpByPattern ?? []).slice(0, 2);
537
+ const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
538
+ const dlpBars = (ins.dlpByPattern ?? []).slice(0, 3);
539
+ const maxDlp = dlpBars[0]?.count ?? 1;
540
+ const dlpRecent = (ins.dlpHits ?? []).slice(0, 2);
530
541
  const denials = buffer.filter((e) => e.decision !== "ALLOW");
531
542
  const lastDeny = denials[denials.length - 1];
532
- const bursts = limit > 0 ? minuteSeries.filter((c2) => c2 > limit).length : 0;
533
- const bigH = rows >= 34 ? 6 : 4;
534
- const colH = rows >= 34 ? 6 : 5;
535
- const streamRows = Math.max(4, rows - (2 + 1 + bigH + 1 + colH + 1 + 1));
543
+ const bursts = limit > 0 ? rateSeries.filter((c2) => c2 > limit).length : 0;
544
+ const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
545
+ const sessCounts = sessions.data?.counts;
546
+ const chartH = rows >= 36 ? 6 : 4;
547
+ const colH = rows >= 32 ? 6 : 5;
548
+ const streamRows = Math.max(4, rows - 6 - chartH - colH);
536
549
  const tail = buffer.slice(-streamRows);
537
- const colW = Math.max(18, Math.floor((cols - 6) / 3));
550
+ const colW = Math.max(20, Math.floor((cols - 6) / 3));
551
+ const innerW = cols - 2;
538
552
  if (stats.error) return /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
539
553
  "\u2717 ",
540
554
  stats.error
541
555
  ] });
556
+ const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25D0", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
542
557
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
543
558
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
544
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE::LIVE " }),
545
- /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
546
- " ",
547
- spin,
548
- " up ",
549
- fmtUp(Date.now() - startRef.current),
550
- " \xB7 ",
551
- hhmmss(Date.now()),
552
- " \xB7 poll 2s"
553
- ] }),
554
- lastDeny ? /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
555
- " \u26A0 ",
556
- hhmmss(lastDeny.created_at),
557
- " ",
558
- lastDeny.tool_name,
559
- " denied"
560
- ] }) : null,
561
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " esc menu \xB7 q quit" })
559
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " \u26E8 SOLONGATE LIVE " }),
560
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} ` }),
561
+ lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.created_at)} ${lastDeny.tool_name} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 no recent denials " })
562
562
  ] }),
563
563
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
564
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "calls " }),
564
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
565
565
  /* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
566
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " allow " }),
566
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
567
567
  /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
568
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " deny " }),
568
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
569
569
  /* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
570
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " bursts(60m) " }),
570
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp(buf) " }),
571
+ /* @__PURE__ */ jsx2(Text2, { color: dlpBufHits ? theme.bad : theme.dim, children: dlpBufHits }),
572
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 bursts " }),
571
573
  /* @__PURE__ */ jsx2(Text2, { color: bursts ? theme.bad : theme.dim, children: bursts }),
572
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " limit/min " }),
573
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: limit || "off" }),
574
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " policies " }),
575
- /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" })
574
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
575
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC` : "\xB7" }),
576
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
577
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
578
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
576
579
  ] }),
577
- /* @__PURE__ */ jsx2(SectionTitle, { width: cols - 2, children: `ACTIVITY ${activityLabel} \xB7 peak ${Math.max(0, ...minuteSeries)}${limit ? ` \xB7 limit ${limit} (red = over)` : ""}` }),
578
- minuteSeries.length ? /* @__PURE__ */ jsx2(ColumnChart, { series: minuteSeries, height: bigH, width: cols - 2, color: theme.accent, limit }) : /* @__PURE__ */ jsx2(Box2, { height: bigH, children: /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: insights.loading ? "loading activity\u2026" : "no activity data" }) }),
580
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "RATE LIMIT", extra: `${rateLabel} \xB7 peak ${Math.max(0, ...rateSeries)} \xB7 limit ${limit || "off"} \xB7 mode ${rlMode}${limit ? " \xB7 red = over" : ""}`, width: innerW }),
581
+ rateSeries.length ? /* @__PURE__ */ jsx2(ColumnChart, { series: rateSeries, height: chartH, width: innerW, color: theme.accent, limit }) : /* @__PURE__ */ jsx2(Box2, { height: chartH, children: /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: insights.loading ? `${spin} loading activity\u2026` : "no activity data" }) }),
579
582
  /* @__PURE__ */ jsxs2(Box2, { children: [
580
583
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
581
- /* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "DENIALS 24h" }),
584
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "DENIALS", extra: "24h", width: colW }),
582
585
  /* @__PURE__ */ jsx2(ColumnChart, { series: denySeries, height: colH - 1, width: colW, color: theme.bad })
583
586
  ] }),
584
587
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
585
- /* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "TOP TOOLS (buffer)" }),
586
- topTools.length ? topTools.slice(0, colH - 1).map(([tool, count]) => /* @__PURE__ */ jsx2(HBar, { label: truncate(tool, 9), value: count, max: maxTool, width: Math.max(6, colW - 16), color: theme.accent }, tool)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no traffic yet" })
588
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "DLP", extra: `7d \xB7 mode ${dlpMode}`, width: colW }),
589
+ dlpBars.length ? dlpBars.map((d) => /* @__PURE__ */ jsx2(HBar, { label: truncate(d.pattern, 9), value: d.count, max: maxDlp, width: Math.max(6, colW - 16), color: theme.bad }, d.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no dlp hits" }),
590
+ dlpRecent.map((h, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
591
+ /* @__PURE__ */ jsx2(Text2, { color: h.blocked ? theme.accent : theme.bad, children: h.blocked ? "\u2298" : "\u26A0" }),
592
+ /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
593
+ " ",
594
+ truncate(h.pattern, colW - 14),
595
+ " ",
596
+ ago(h.at),
597
+ " ago"
598
+ ] })
599
+ ] }, i))
587
600
  ] }),
588
601
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
589
- /* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "PERMISSIONS \xB7 DLP" }),
590
- perms.map(([p, c2]) => /* @__PURE__ */ jsx2(HBar, { label: p, value: c2, max: maxPerm, width: Math.max(6, colW - 16), color: p === "EXECUTE" ? theme.warn : theme.accent }, p)),
591
- dlpTop.map((d) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.bad, children: "DLP " + truncate(d.pattern, colW - 10) + " \xD7" + d.count }, d.pattern))
602
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} active` : "", width: colW }),
603
+ sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no sessions" }) : null,
604
+ sess.slice(0, colH - 1).map((a) => {
605
+ const dot = sessionDot(a.status);
606
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
607
+ /* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
608
+ dot.ch,
609
+ " "
610
+ ] }),
611
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(a.agent_name ?? a.session_id, 12).padEnd(13) }),
612
+ /* @__PURE__ */ jsxs2(Text2, { children: [
613
+ String(a.total_calls).padStart(4),
614
+ "c "
615
+ ] }),
616
+ /* @__PURE__ */ jsxs2(Text2, { color: a.denied_calls ? theme.bad : theme.dim, children: [
617
+ String(a.denied_calls).padStart(3),
618
+ "d "
619
+ ] }),
620
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(a.last_seen_at) })
621
+ ] }, a.session_id);
622
+ })
592
623
  ] })
593
624
  ] }),
594
- /* @__PURE__ */ jsx2(SectionTitle, { width: cols - 2, children: `TOOL STREAM \xB7 live tail \xB7 ${buffer.length} buffered` }),
625
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `live tail \xB7 ${buffer.length} buffered`, width: innerW }),
595
626
  tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
596
627
  tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
597
628
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
@@ -606,11 +637,9 @@ function LivePanel({ active: active2 }) {
606
637
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
607
638
  ] }, e.id)),
608
639
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
609
- /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "root@solongate" }),
610
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ":" }),
611
- /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: "~/live" }),
612
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "$ " }),
613
- /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: cursor })
640
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " \u25B2 LIVE " }),
641
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
642
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
614
643
  ] })
615
644
  ] });
616
645
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.64.0",
3
+ "version": "0.65.0",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {