@solongate/proxy 0.65.0 → 0.66.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 +139 -84
- package/dist/tui/index.js +139 -84
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7020,19 +7020,27 @@ var init_hooks = __esm({
|
|
|
7020
7020
|
|
|
7021
7021
|
// src/tui/panels/Live.tsx
|
|
7022
7022
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
7023
|
-
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7023
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7024
7024
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7025
|
-
function ColumnChart({ series, height, width: width2, color
|
|
7026
|
-
const
|
|
7027
|
-
const data = [...new Array(
|
|
7028
|
-
const
|
|
7025
|
+
function ColumnChart({ series, hot, height, width: width2, color }) {
|
|
7026
|
+
const pad = Math.max(0, width2 - series.length);
|
|
7027
|
+
const data = [...new Array(pad).fill(0), ...series.slice(-width2)];
|
|
7028
|
+
const hotPad = hot ? [...new Array(pad).fill(false), ...hot.slice(-width2)] : void 0;
|
|
7029
|
+
const max = Math.max(1, ...data);
|
|
7029
7030
|
const lines = [];
|
|
7030
7031
|
for (let r = height; r >= 1; r--) {
|
|
7031
7032
|
const segs = [];
|
|
7032
7033
|
for (let i = 0; i < data.length; i++) {
|
|
7033
|
-
const
|
|
7034
|
-
const
|
|
7035
|
-
|
|
7034
|
+
const v = data[i];
|
|
7035
|
+
const frac = v / max;
|
|
7036
|
+
let ch;
|
|
7037
|
+
let c2;
|
|
7038
|
+
if (frac >= r / height) ch = "\u2588";
|
|
7039
|
+
else if (frac >= (r - 0.5) / height) ch = "\u2584";
|
|
7040
|
+
else if (r === 1) ch = "\u2581";
|
|
7041
|
+
else ch = " ";
|
|
7042
|
+
if (r === 1 && v === 0) c2 = "#233457";
|
|
7043
|
+
else c2 = hotPad?.[i] ? theme.bad : color;
|
|
7036
7044
|
const last = segs[segs.length - 1];
|
|
7037
7045
|
if (last && last.color === c2) last.text += ch;
|
|
7038
7046
|
else segs.push({ text: ch, color: c2 });
|
|
@@ -7046,17 +7054,17 @@ function ColumnChart({ series, height, width: width2, color, limit = 0 }) {
|
|
|
7046
7054
|
function HBar({ label, value, max, width: width2, color }) {
|
|
7047
7055
|
const filled = max > 0 ? Math.round(value / max * width2) : 0;
|
|
7048
7056
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7049
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(
|
|
7057
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(10) }),
|
|
7050
7058
|
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width2, filled)) }),
|
|
7051
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7059
|
+
/* @__PURE__ */ jsx2(Text2, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
|
|
7060
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + value })
|
|
7052
7061
|
] });
|
|
7053
7062
|
}
|
|
7054
7063
|
function PaneTitle({ label, extra, width: width2 }) {
|
|
7055
|
-
const head = `\u2500 `;
|
|
7056
7064
|
const tail = extra ? ` ${extra} ` : " ";
|
|
7057
|
-
const used =
|
|
7065
|
+
const used = 2 + 1 + label.length + tail.length;
|
|
7058
7066
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7059
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
7067
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2500 " }),
|
|
7060
7068
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.accentBright, bold: true, children: [
|
|
7061
7069
|
"\u258E",
|
|
7062
7070
|
label
|
|
@@ -7065,18 +7073,63 @@ function PaneTitle({ label, extra, width: width2 }) {
|
|
|
7065
7073
|
] });
|
|
7066
7074
|
}
|
|
7067
7075
|
function LivePanel({ active: active2 }) {
|
|
7068
|
-
const
|
|
7069
|
-
const
|
|
7070
|
-
const
|
|
7071
|
-
const
|
|
7076
|
+
const [s, setS] = useState2(null);
|
|
7077
|
+
const [tsData, setTsData] = useState2(null);
|
|
7078
|
+
const [lat, setLat] = useState2([]);
|
|
7079
|
+
const [buffer, setBuffer] = useState2([]);
|
|
7080
|
+
const [log6, setLog] = useState2([]);
|
|
7081
|
+
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7082
|
+
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7083
|
+
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7084
|
+
}, []);
|
|
7085
|
+
const doPoll = useCallback2(async () => {
|
|
7086
|
+
const t0 = Date.now();
|
|
7087
|
+
try {
|
|
7088
|
+
const [sd, fd, td] = await Promise.all([api.stats.get(), api.audit.list({ limit: 50 }), api.stats.timeseries({ period: "24h" })]);
|
|
7089
|
+
const ms = Date.now() - t0;
|
|
7090
|
+
setS(sd);
|
|
7091
|
+
setTsData(td);
|
|
7092
|
+
setLat((l) => [...l.slice(-47), ms]);
|
|
7093
|
+
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
7094
|
+
for (const e of fresh) seenRef.current.add(e.id);
|
|
7095
|
+
const firstLoad = seenRef.current.size === fresh.length && fresh.length > 1;
|
|
7096
|
+
if (fresh.length) {
|
|
7097
|
+
setBuffer((prev) => [...prev, ...fresh.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at))].slice(-400));
|
|
7098
|
+
}
|
|
7099
|
+
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
7100
|
+
const dlp = fresh.filter((e) => e.dlp_matches?.length).length;
|
|
7101
|
+
if (firstLoad) pushLog(`link up \xB7 api ${ms}ms \xB7 loaded ${fresh.length} calls`);
|
|
7102
|
+
else
|
|
7103
|
+
pushLog(
|
|
7104
|
+
`api ${ms}ms \xB7 +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}${dlp ? ` \xB7 ${dlp} DLP` : ""}`,
|
|
7105
|
+
denies || dlp ? "bad" : fresh.length ? "warn" : "ok"
|
|
7106
|
+
);
|
|
7107
|
+
} catch (e) {
|
|
7108
|
+
pushLog(`api error \xB7 ${truncate2(e instanceof Error ? e.message : String(e), 40)}`, "bad");
|
|
7109
|
+
}
|
|
7110
|
+
}, [pushLog]);
|
|
7111
|
+
useEffect2(() => {
|
|
7112
|
+
if (!active2) return;
|
|
7113
|
+
void doPoll();
|
|
7114
|
+
const t = setInterval(() => void doPoll(), 2e3);
|
|
7115
|
+
return () => clearInterval(t);
|
|
7116
|
+
}, [active2, doPoll]);
|
|
7072
7117
|
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
7073
|
-
|
|
7074
|
-
stats.reload();
|
|
7075
|
-
ts.reload();
|
|
7076
|
-
feed.reload();
|
|
7077
|
-
}, 2e3, active2);
|
|
7118
|
+
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
7078
7119
|
usePoll(sessions.reload, 5e3, active2);
|
|
7079
7120
|
usePoll(insights.reload, 1e4, active2);
|
|
7121
|
+
useEffect2(() => {
|
|
7122
|
+
const c2 = sessions.data?.counts;
|
|
7123
|
+
if (c2) pushLog(`sessions ${c2.active}\u25B2 ${c2.idle}\u25CC ${c2.deactivated}\u25CB`);
|
|
7124
|
+
}, [sessions.data, pushLog]);
|
|
7125
|
+
useEffect2(() => {
|
|
7126
|
+
const ib = insights.data;
|
|
7127
|
+
if (ib?.layers) {
|
|
7128
|
+
const rl2 = ib.layers.rateLimit;
|
|
7129
|
+
const dl2 = ib.layers.dlp;
|
|
7130
|
+
pushLog(`layers \xB7 ratelimit ${rl2?.mode ?? "?"} ${rl2?.perMinute || "\u2014"}/m \xB7 dlp ${dl2?.mode ?? "?"} ${(dl2?.patterns ?? []).length} armed`);
|
|
7131
|
+
}
|
|
7132
|
+
}, [insights.data, pushLog]);
|
|
7080
7133
|
const [tick, setTick] = useState2(0);
|
|
7081
7134
|
useEffect2(() => {
|
|
7082
7135
|
if (!active2) return;
|
|
@@ -7084,61 +7137,41 @@ function LivePanel({ active: active2 }) {
|
|
|
7084
7137
|
return () => clearInterval(t);
|
|
7085
7138
|
}, [active2]);
|
|
7086
7139
|
const startRef = useRef2(Date.now());
|
|
7087
|
-
const [buffer, setBuffer] = useState2([]);
|
|
7088
|
-
useEffect2(() => {
|
|
7089
|
-
const incoming = feed.data?.entries;
|
|
7090
|
-
if (!incoming?.length) return;
|
|
7091
|
-
setBuffer((prev) => {
|
|
7092
|
-
const seen = new Set(prev.map((e) => e.id));
|
|
7093
|
-
const fresh = incoming.filter((e) => !seen.has(e.id)).sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
|
|
7094
|
-
return fresh.length ? [...prev, ...fresh].slice(-400) : prev;
|
|
7095
|
-
});
|
|
7096
|
-
}, [feed.data]);
|
|
7097
7140
|
const cols = process.stdout.columns ?? 100;
|
|
7098
7141
|
const rows = process.stdout.rows ?? 30;
|
|
7099
|
-
const s = stats.data;
|
|
7100
7142
|
const ins = insights.data ?? {};
|
|
7101
|
-
const minuteRaw = (ins.activity?.minute ?? []).map((b) => b.count);
|
|
7102
|
-
const hourRaw = (ins.activity?.hour ?? []).map((b) => b.count);
|
|
7103
|
-
const minuteLive = minuteRaw.some((c2) => c2 > 0);
|
|
7104
|
-
const rateSeries = minuteLive ? minuteRaw : hourRaw;
|
|
7105
|
-
const rateLabel = minuteLive ? "calls/min \xB7 60m" : "calls/hour \xB7 24h";
|
|
7106
|
-
const limit = minuteLive ? ins.layers?.rateLimit?.perMinute ?? 0 : 0;
|
|
7107
|
-
const rlMode = ins.layers?.rateLimit?.mode ?? "?";
|
|
7108
|
-
const dlpMode = ins.layers?.dlp?.mode ?? "?";
|
|
7109
|
-
const denySeries = (ts.data?.timeseries ?? []).map((p) => p.denied);
|
|
7110
7143
|
const spin = SPIN[tick % SPIN.length];
|
|
7144
|
+
const points = tsData?.timeseries ?? [];
|
|
7145
|
+
const traffic = points.map((p) => p.total);
|
|
7146
|
+
const trafficHot = points.map((p) => p.denied > 0);
|
|
7147
|
+
const rl = ins.layers?.rateLimit;
|
|
7148
|
+
const dl = ins.layers?.dlp;
|
|
7149
|
+
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
7150
|
+
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
7151
|
+
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
7111
7152
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
7112
|
-
|
|
7113
|
-
for (const e of buffer) {
|
|
7114
|
-
toolCounts.set(e.tool_name, (toolCounts.get(e.tool_name) ?? 0) + 1);
|
|
7115
|
-
if (e.dlp_matches?.length) dlpBufHits++;
|
|
7116
|
-
}
|
|
7153
|
+
for (const e of buffer) toolCounts.set(e.tool_name, (toolCounts.get(e.tool_name) ?? 0) + 1);
|
|
7117
7154
|
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);
|
|
7121
7155
|
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
7122
7156
|
const lastDeny = denials[denials.length - 1];
|
|
7123
|
-
const
|
|
7157
|
+
const latNow = lat[lat.length - 1] ?? 0;
|
|
7158
|
+
const latAvg = lat.length ? Math.round(lat.reduce((a, b) => a + b, 0) / lat.length) : 0;
|
|
7124
7159
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
7125
7160
|
const sessCounts = sessions.data?.counts;
|
|
7126
7161
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7127
7162
|
const colH = rows >= 32 ? 6 : 5;
|
|
7128
7163
|
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
7129
7164
|
const tail = buffer.slice(-streamRows);
|
|
7130
|
-
const colW = Math.max(20, Math.floor((cols - 6) / 3));
|
|
7131
7165
|
const innerW = cols - 2;
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
] });
|
|
7166
|
+
const leftW = Math.floor(innerW * 0.55);
|
|
7167
|
+
const rightW = innerW - leftW - 2;
|
|
7168
|
+
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7136
7169
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25D0", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7137
7170
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7138
7171
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7139
7172
|
/* @__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
|
|
7173
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
7174
|
+
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 clean " })
|
|
7142
7175
|
] }),
|
|
7143
7176
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7144
7177
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -7147,40 +7180,48 @@ function LivePanel({ active: active2 }) {
|
|
|
7147
7180
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
7148
7181
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7149
7182
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
7150
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502
|
|
7151
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7152
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502
|
|
7153
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7183
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
7184
|
+
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
7185
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
7186
|
+
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
7154
7187
|
/* @__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" }),
|
|
7188
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC ${sessCounts.deactivated}\u25CB` : "\xB7" }),
|
|
7156
7189
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
|
|
7157
7190
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
|
|
7158
7191
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7159
7192
|
] }),
|
|
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" }) }),
|
|
7162
7193
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7163
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width:
|
|
7164
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "
|
|
7165
|
-
/* @__PURE__ */ jsx2(ColumnChart, { series:
|
|
7194
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
7195
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `24h \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = had denials`, width: leftW }),
|
|
7196
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
7166
7197
|
] }),
|
|
7198
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
7199
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `live \xB7 now ${latNow}ms \xB7 avg ${latAvg}ms`, width: rightW }),
|
|
7200
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > 800), height: chartH, width: rightW, color: "#82AAF0" })
|
|
7201
|
+
] })
|
|
7202
|
+
] }),
|
|
7203
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7167
7204
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7168
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7205
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", width: colW }),
|
|
7206
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7207
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
|
|
7208
|
+
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: (rl?.mode ?? "?").padEnd(7) }),
|
|
7209
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: rl?.perMinute ? `${minuteNow}/${rl.perMinute} per min` : "no limit set" })
|
|
7210
|
+
] }),
|
|
7211
|
+
rl?.perMinute ? /* @__PURE__ */ jsx2(HBar, { label: "load", value: minuteNow, max: rl.perMinute, width: Math.max(6, colW - 16), color: minuteNow > rl.perMinute ? theme.bad : theme.accent }) : null,
|
|
7212
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7213
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "DLP".padEnd(10) }),
|
|
7214
|
+
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
|
|
7172
7215
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
" ",
|
|
7176
|
-
ago(h.at),
|
|
7177
|
-
" ago"
|
|
7216
|
+
(dl?.patterns ?? []).length,
|
|
7217
|
+
" patterns armed"
|
|
7178
7218
|
] })
|
|
7179
|
-
] },
|
|
7219
|
+
] }),
|
|
7220
|
+
dlpBars.length ? dlpBars.map((d) => /* @__PURE__ */ jsx2(HBar, { label: truncate2(d.pattern, 10), value: d.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "hits 7d none \u2014 clean" })
|
|
7180
7221
|
] }),
|
|
7181
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
|
|
7182
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active}
|
|
7183
|
-
sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
7222
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7223
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} live` : "", width: colW }),
|
|
7224
|
+
sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7184
7225
|
sess.slice(0, colH - 1).map((a) => {
|
|
7185
7226
|
const dot = sessionDot(a.status);
|
|
7186
7227
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
@@ -7200,10 +7241,24 @@ function LivePanel({ active: active2 }) {
|
|
|
7200
7241
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(a.last_seen_at) })
|
|
7201
7242
|
] }, a.session_id);
|
|
7202
7243
|
})
|
|
7244
|
+
] }),
|
|
7245
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
|
|
7246
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "EVENT LOG", extra: "streaming", width: colW }),
|
|
7247
|
+
log6.slice(-(colH - 1)).map((l, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7248
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7249
|
+
hhmmss(l.ts),
|
|
7250
|
+
" "
|
|
7251
|
+
] }),
|
|
7252
|
+
/* @__PURE__ */ jsx2(Text2, { color: l.level === "bad" ? theme.bad : l.level === "warn" ? theme.warn : "#4f8f6b", children: "\u25B8 " }),
|
|
7253
|
+
/* @__PURE__ */ jsx2(Text2, { color: l.level === "bad" ? theme.bad : theme.dim, children: l.msg })
|
|
7254
|
+
] }, l.ts + ":" + i))
|
|
7203
7255
|
] })
|
|
7204
7256
|
] }),
|
|
7205
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `
|
|
7206
|
-
tail.length === 0 ? /* @__PURE__ */
|
|
7257
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `tail -f \xB7 ${buffer.length} buffered`, width: innerW }),
|
|
7258
|
+
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7259
|
+
spin,
|
|
7260
|
+
" awaiting traffic\u2026"
|
|
7261
|
+
] }) : null,
|
|
7207
7262
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7208
7263
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7209
7264
|
"[",
|
|
@@ -7218,7 +7273,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7218
7273
|
] }, e.id)),
|
|
7219
7274
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7220
7275
|
/* @__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"} ` }),
|
|
7276
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${buffer.length} ` }),
|
|
7222
7277
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
7223
7278
|
] })
|
|
7224
7279
|
] });
|
package/dist/tui/index.js
CHANGED
|
@@ -126,7 +126,7 @@ function KeyHints({ hints }) {
|
|
|
126
126
|
|
|
127
127
|
// src/tui/panels/Live.tsx
|
|
128
128
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
129
|
-
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
129
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
130
130
|
|
|
131
131
|
// src/api-client/client.ts
|
|
132
132
|
import { readFileSync, existsSync } from "fs";
|
|
@@ -442,17 +442,25 @@ var fmtUp = (ms) => {
|
|
|
442
442
|
const p = (n) => String(n).padStart(2, "0");
|
|
443
443
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
444
444
|
};
|
|
445
|
-
function ColumnChart({ series, height, width, color
|
|
446
|
-
const
|
|
447
|
-
const data = [...new Array(
|
|
448
|
-
const
|
|
445
|
+
function ColumnChart({ series, hot, height, width, color }) {
|
|
446
|
+
const pad = Math.max(0, width - series.length);
|
|
447
|
+
const data = [...new Array(pad).fill(0), ...series.slice(-width)];
|
|
448
|
+
const hotPad = hot ? [...new Array(pad).fill(false), ...hot.slice(-width)] : void 0;
|
|
449
|
+
const max = Math.max(1, ...data);
|
|
449
450
|
const lines = [];
|
|
450
451
|
for (let r = height; r >= 1; r--) {
|
|
451
452
|
const segs = [];
|
|
452
453
|
for (let i = 0; i < data.length; i++) {
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
|
|
454
|
+
const v = data[i];
|
|
455
|
+
const frac = v / max;
|
|
456
|
+
let ch;
|
|
457
|
+
let c2;
|
|
458
|
+
if (frac >= r / height) ch = "\u2588";
|
|
459
|
+
else if (frac >= (r - 0.5) / height) ch = "\u2584";
|
|
460
|
+
else if (r === 1) ch = "\u2581";
|
|
461
|
+
else ch = " ";
|
|
462
|
+
if (r === 1 && v === 0) c2 = "#233457";
|
|
463
|
+
else c2 = hotPad?.[i] ? theme.bad : color;
|
|
456
464
|
const last = segs[segs.length - 1];
|
|
457
465
|
if (last && last.color === c2) last.text += ch;
|
|
458
466
|
else segs.push({ text: ch, color: c2 });
|
|
@@ -466,17 +474,17 @@ function ColumnChart({ series, height, width, color, limit = 0 }) {
|
|
|
466
474
|
function HBar({ label, value, max, width, color }) {
|
|
467
475
|
const filled = max > 0 ? Math.round(value / max * width) : 0;
|
|
468
476
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
469
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(
|
|
477
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(10) }),
|
|
470
478
|
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width, filled)) }),
|
|
471
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
479
|
+
/* @__PURE__ */ jsx2(Text2, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
|
|
480
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + value })
|
|
472
481
|
] });
|
|
473
482
|
}
|
|
474
483
|
function PaneTitle({ label, extra, width }) {
|
|
475
|
-
const head = `\u2500 `;
|
|
476
484
|
const tail = extra ? ` ${extra} ` : " ";
|
|
477
|
-
const used =
|
|
485
|
+
const used = 2 + 1 + label.length + tail.length;
|
|
478
486
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
479
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
487
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2500 " }),
|
|
480
488
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.accentBright, bold: true, children: [
|
|
481
489
|
"\u258E",
|
|
482
490
|
label
|
|
@@ -485,18 +493,63 @@ function PaneTitle({ label, extra, width }) {
|
|
|
485
493
|
] });
|
|
486
494
|
}
|
|
487
495
|
function LivePanel({ active: active2 }) {
|
|
488
|
-
const
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
const
|
|
496
|
+
const [s, setS] = useState2(null);
|
|
497
|
+
const [tsData, setTsData] = useState2(null);
|
|
498
|
+
const [lat, setLat] = useState2([]);
|
|
499
|
+
const [buffer, setBuffer] = useState2([]);
|
|
500
|
+
const [log, setLog] = useState2([]);
|
|
501
|
+
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
502
|
+
const pushLog = useCallback2((msg, level = "ok") => {
|
|
503
|
+
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
504
|
+
}, []);
|
|
505
|
+
const doPoll = useCallback2(async () => {
|
|
506
|
+
const t0 = Date.now();
|
|
507
|
+
try {
|
|
508
|
+
const [sd, fd, td] = await Promise.all([api.stats.get(), api.audit.list({ limit: 50 }), api.stats.timeseries({ period: "24h" })]);
|
|
509
|
+
const ms = Date.now() - t0;
|
|
510
|
+
setS(sd);
|
|
511
|
+
setTsData(td);
|
|
512
|
+
setLat((l) => [...l.slice(-47), ms]);
|
|
513
|
+
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
514
|
+
for (const e of fresh) seenRef.current.add(e.id);
|
|
515
|
+
const firstLoad = seenRef.current.size === fresh.length && fresh.length > 1;
|
|
516
|
+
if (fresh.length) {
|
|
517
|
+
setBuffer((prev) => [...prev, ...fresh.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at))].slice(-400));
|
|
518
|
+
}
|
|
519
|
+
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
520
|
+
const dlp = fresh.filter((e) => e.dlp_matches?.length).length;
|
|
521
|
+
if (firstLoad) pushLog(`link up \xB7 api ${ms}ms \xB7 loaded ${fresh.length} calls`);
|
|
522
|
+
else
|
|
523
|
+
pushLog(
|
|
524
|
+
`api ${ms}ms \xB7 +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}${dlp ? ` \xB7 ${dlp} DLP` : ""}`,
|
|
525
|
+
denies || dlp ? "bad" : fresh.length ? "warn" : "ok"
|
|
526
|
+
);
|
|
527
|
+
} catch (e) {
|
|
528
|
+
pushLog(`api error \xB7 ${truncate(e instanceof Error ? e.message : String(e), 40)}`, "bad");
|
|
529
|
+
}
|
|
530
|
+
}, [pushLog]);
|
|
531
|
+
useEffect2(() => {
|
|
532
|
+
if (!active2) return;
|
|
533
|
+
void doPoll();
|
|
534
|
+
const t = setInterval(() => void doPoll(), 2e3);
|
|
535
|
+
return () => clearInterval(t);
|
|
536
|
+
}, [active2, doPoll]);
|
|
492
537
|
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
493
|
-
|
|
494
|
-
stats.reload();
|
|
495
|
-
ts.reload();
|
|
496
|
-
feed.reload();
|
|
497
|
-
}, 2e3, active2);
|
|
538
|
+
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
498
539
|
usePoll(sessions.reload, 5e3, active2);
|
|
499
540
|
usePoll(insights.reload, 1e4, active2);
|
|
541
|
+
useEffect2(() => {
|
|
542
|
+
const c2 = sessions.data?.counts;
|
|
543
|
+
if (c2) pushLog(`sessions ${c2.active}\u25B2 ${c2.idle}\u25CC ${c2.deactivated}\u25CB`);
|
|
544
|
+
}, [sessions.data, pushLog]);
|
|
545
|
+
useEffect2(() => {
|
|
546
|
+
const ib = insights.data;
|
|
547
|
+
if (ib?.layers) {
|
|
548
|
+
const rl2 = ib.layers.rateLimit;
|
|
549
|
+
const dl2 = ib.layers.dlp;
|
|
550
|
+
pushLog(`layers \xB7 ratelimit ${rl2?.mode ?? "?"} ${rl2?.perMinute || "\u2014"}/m \xB7 dlp ${dl2?.mode ?? "?"} ${(dl2?.patterns ?? []).length} armed`);
|
|
551
|
+
}
|
|
552
|
+
}, [insights.data, pushLog]);
|
|
500
553
|
const [tick, setTick] = useState2(0);
|
|
501
554
|
useEffect2(() => {
|
|
502
555
|
if (!active2) return;
|
|
@@ -504,61 +557,41 @@ function LivePanel({ active: active2 }) {
|
|
|
504
557
|
return () => clearInterval(t);
|
|
505
558
|
}, [active2]);
|
|
506
559
|
const startRef = useRef2(Date.now());
|
|
507
|
-
const [buffer, setBuffer] = useState2([]);
|
|
508
|
-
useEffect2(() => {
|
|
509
|
-
const incoming = feed.data?.entries;
|
|
510
|
-
if (!incoming?.length) return;
|
|
511
|
-
setBuffer((prev) => {
|
|
512
|
-
const seen = new Set(prev.map((e) => e.id));
|
|
513
|
-
const fresh = incoming.filter((e) => !seen.has(e.id)).sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
|
|
514
|
-
return fresh.length ? [...prev, ...fresh].slice(-400) : prev;
|
|
515
|
-
});
|
|
516
|
-
}, [feed.data]);
|
|
517
560
|
const cols = process.stdout.columns ?? 100;
|
|
518
561
|
const rows = process.stdout.rows ?? 30;
|
|
519
|
-
const s = stats.data;
|
|
520
562
|
const ins = insights.data ?? {};
|
|
521
|
-
const minuteRaw = (ins.activity?.minute ?? []).map((b) => b.count);
|
|
522
|
-
const hourRaw = (ins.activity?.hour ?? []).map((b) => b.count);
|
|
523
|
-
const minuteLive = minuteRaw.some((c2) => c2 > 0);
|
|
524
|
-
const rateSeries = minuteLive ? minuteRaw : hourRaw;
|
|
525
|
-
const rateLabel = minuteLive ? "calls/min \xB7 60m" : "calls/hour \xB7 24h";
|
|
526
|
-
const limit = minuteLive ? ins.layers?.rateLimit?.perMinute ?? 0 : 0;
|
|
527
|
-
const rlMode = ins.layers?.rateLimit?.mode ?? "?";
|
|
528
|
-
const dlpMode = ins.layers?.dlp?.mode ?? "?";
|
|
529
|
-
const denySeries = (ts.data?.timeseries ?? []).map((p) => p.denied);
|
|
530
563
|
const spin = SPIN[tick % SPIN.length];
|
|
564
|
+
const points = tsData?.timeseries ?? [];
|
|
565
|
+
const traffic = points.map((p) => p.total);
|
|
566
|
+
const trafficHot = points.map((p) => p.denied > 0);
|
|
567
|
+
const rl = ins.layers?.rateLimit;
|
|
568
|
+
const dl = ins.layers?.dlp;
|
|
569
|
+
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
570
|
+
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
571
|
+
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
531
572
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
532
|
-
|
|
533
|
-
for (const e of buffer) {
|
|
534
|
-
toolCounts.set(e.tool_name, (toolCounts.get(e.tool_name) ?? 0) + 1);
|
|
535
|
-
if (e.dlp_matches?.length) dlpBufHits++;
|
|
536
|
-
}
|
|
573
|
+
for (const e of buffer) toolCounts.set(e.tool_name, (toolCounts.get(e.tool_name) ?? 0) + 1);
|
|
537
574
|
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);
|
|
541
575
|
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
542
576
|
const lastDeny = denials[denials.length - 1];
|
|
543
|
-
const
|
|
577
|
+
const latNow = lat[lat.length - 1] ?? 0;
|
|
578
|
+
const latAvg = lat.length ? Math.round(lat.reduce((a, b) => a + b, 0) / lat.length) : 0;
|
|
544
579
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
545
580
|
const sessCounts = sessions.data?.counts;
|
|
546
581
|
const chartH = rows >= 36 ? 6 : 4;
|
|
547
582
|
const colH = rows >= 32 ? 6 : 5;
|
|
548
583
|
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
549
584
|
const tail = buffer.slice(-streamRows);
|
|
550
|
-
const colW = Math.max(20, Math.floor((cols - 6) / 3));
|
|
551
585
|
const innerW = cols - 2;
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
] });
|
|
586
|
+
const leftW = Math.floor(innerW * 0.55);
|
|
587
|
+
const rightW = innerW - leftW - 2;
|
|
588
|
+
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
556
589
|
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25D0", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
557
590
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
558
591
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
559
592
|
/* @__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
|
|
593
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
594
|
+
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 clean " })
|
|
562
595
|
] }),
|
|
563
596
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
564
597
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -567,40 +600,48 @@ function LivePanel({ active: active2 }) {
|
|
|
567
600
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
568
601
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
569
602
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
570
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502
|
|
571
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
572
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502
|
|
573
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
603
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
604
|
+
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
605
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
606
|
+
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
574
607
|
/* @__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" }),
|
|
608
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC ${sessCounts.deactivated}\u25CB` : "\xB7" }),
|
|
576
609
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
|
|
577
610
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
|
|
578
611
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
579
612
|
] }),
|
|
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" }) }),
|
|
582
613
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
583
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width:
|
|
584
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "
|
|
585
|
-
/* @__PURE__ */ jsx2(ColumnChart, { series:
|
|
614
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
615
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `24h \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = had denials`, width: leftW }),
|
|
616
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
586
617
|
] }),
|
|
618
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
619
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `live \xB7 now ${latNow}ms \xB7 avg ${latAvg}ms`, width: rightW }),
|
|
620
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > 800), height: chartH, width: rightW, color: "#82AAF0" })
|
|
621
|
+
] })
|
|
622
|
+
] }),
|
|
623
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
587
624
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
588
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
625
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", width: colW }),
|
|
626
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
627
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
|
|
628
|
+
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: (rl?.mode ?? "?").padEnd(7) }),
|
|
629
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: rl?.perMinute ? `${minuteNow}/${rl.perMinute} per min` : "no limit set" })
|
|
630
|
+
] }),
|
|
631
|
+
rl?.perMinute ? /* @__PURE__ */ jsx2(HBar, { label: "load", value: minuteNow, max: rl.perMinute, width: Math.max(6, colW - 16), color: minuteNow > rl.perMinute ? theme.bad : theme.accent }) : null,
|
|
632
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
633
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "DLP".padEnd(10) }),
|
|
634
|
+
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: (dl?.mode ?? "?").padEnd(7) }),
|
|
592
635
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
" ",
|
|
596
|
-
ago(h.at),
|
|
597
|
-
" ago"
|
|
636
|
+
(dl?.patterns ?? []).length,
|
|
637
|
+
" patterns armed"
|
|
598
638
|
] })
|
|
599
|
-
] },
|
|
639
|
+
] }),
|
|
640
|
+
dlpBars.length ? dlpBars.map((d) => /* @__PURE__ */ jsx2(HBar, { label: truncate(d.pattern, 10), value: d.count, max: maxDlpBar, width: Math.max(6, colW - 16), color: theme.bad }, d.pattern)) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "hits 7d none \u2014 clean" })
|
|
600
641
|
] }),
|
|
601
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
|
|
602
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active}
|
|
603
|
-
sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
642
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
643
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} live` : "", width: colW }),
|
|
644
|
+
sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
604
645
|
sess.slice(0, colH - 1).map((a) => {
|
|
605
646
|
const dot = sessionDot(a.status);
|
|
606
647
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
@@ -620,10 +661,24 @@ function LivePanel({ active: active2 }) {
|
|
|
620
661
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(a.last_seen_at) })
|
|
621
662
|
] }, a.session_id);
|
|
622
663
|
})
|
|
664
|
+
] }),
|
|
665
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
|
|
666
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "EVENT LOG", extra: "streaming", width: colW }),
|
|
667
|
+
log.slice(-(colH - 1)).map((l, i) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
668
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
669
|
+
hhmmss(l.ts),
|
|
670
|
+
" "
|
|
671
|
+
] }),
|
|
672
|
+
/* @__PURE__ */ jsx2(Text2, { color: l.level === "bad" ? theme.bad : l.level === "warn" ? theme.warn : "#4f8f6b", children: "\u25B8 " }),
|
|
673
|
+
/* @__PURE__ */ jsx2(Text2, { color: l.level === "bad" ? theme.bad : theme.dim, children: l.msg })
|
|
674
|
+
] }, l.ts + ":" + i))
|
|
623
675
|
] })
|
|
624
676
|
] }),
|
|
625
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `
|
|
626
|
-
tail.length === 0 ? /* @__PURE__ */
|
|
677
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `tail -f \xB7 ${buffer.length} buffered`, width: innerW }),
|
|
678
|
+
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
679
|
+
spin,
|
|
680
|
+
" awaiting traffic\u2026"
|
|
681
|
+
] }) : null,
|
|
627
682
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
628
683
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
629
684
|
"[",
|
|
@@ -638,7 +693,7 @@ function LivePanel({ active: active2 }) {
|
|
|
638
693
|
] }, e.id)),
|
|
639
694
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
640
695
|
/* @__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"} ` }),
|
|
696
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${buffer.length} ` }),
|
|
642
697
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
643
698
|
] })
|
|
644
699
|
] });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.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": {
|