@solongate/proxy 0.63.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 +190 -98
- package/dist/tui/index.js +189 -97
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7022,15 +7022,61 @@ var init_hooks = __esm({
|
|
|
7022
7022
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
7023
7023
|
import { 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, limit = 0 }) {
|
|
7026
|
+
const tailVals = series.slice(-width2);
|
|
7027
|
+
const data = [...new Array(Math.max(0, width2 - tailVals.length)).fill(0), ...tailVals];
|
|
7028
|
+
const max = Math.max(1, ...data, limit);
|
|
7029
|
+
const lines = [];
|
|
7030
|
+
for (let r = height; r >= 1; r--) {
|
|
7031
|
+
const segs = [];
|
|
7032
|
+
for (let i = 0; i < data.length; i++) {
|
|
7033
|
+
const frac = data[i] / max;
|
|
7034
|
+
const ch = frac >= r / height ? "\u2588" : frac >= (r - 0.5) / height ? "\u2584" : r === 1 && data[i] > 0 ? "\u2581" : " ";
|
|
7035
|
+
const c2 = limit > 0 && data[i] > limit ? theme.bad : color;
|
|
7036
|
+
const last = segs[segs.length - 1];
|
|
7037
|
+
if (last && last.color === c2) last.text += ch;
|
|
7038
|
+
else segs.push({ text: ch, color: c2 });
|
|
7039
|
+
}
|
|
7040
|
+
lines.push(
|
|
7041
|
+
/* @__PURE__ */ jsx2(Text2, { children: segs.map((s, i) => /* @__PURE__ */ jsx2(Text2, { color: s.color, children: s.text }, i)) }, r)
|
|
7042
|
+
);
|
|
7043
|
+
}
|
|
7044
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: lines });
|
|
7045
|
+
}
|
|
7046
|
+
function HBar({ label, value, max, width: width2, color }) {
|
|
7047
|
+
const filled = max > 0 ? Math.round(value / max * width2) : 0;
|
|
7048
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7049
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(9) }),
|
|
7050
|
+
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width2, filled)) }),
|
|
7051
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2591".repeat(Math.max(0, width2 - filled)) + " " + value })
|
|
7052
|
+
] });
|
|
7053
|
+
}
|
|
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
|
+
] });
|
|
7066
|
+
}
|
|
7025
7067
|
function LivePanel({ active: active2 }) {
|
|
7026
7068
|
const stats = useLoader(() => api.stats.get());
|
|
7027
7069
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
7028
|
-
const feed = useLoader(() => api.audit.list({ limit:
|
|
7070
|
+
const feed = useLoader(() => api.audit.list({ limit: 50 }));
|
|
7071
|
+
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
7072
|
+
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
7029
7073
|
usePoll(() => {
|
|
7030
7074
|
stats.reload();
|
|
7031
7075
|
ts.reload();
|
|
7032
7076
|
feed.reload();
|
|
7033
7077
|
}, 2e3, active2);
|
|
7078
|
+
usePoll(sessions.reload, 5e3, active2);
|
|
7079
|
+
usePoll(insights.reload, 1e4, active2);
|
|
7034
7080
|
const [tick, setTick] = useState2(0);
|
|
7035
7081
|
useEffect2(() => {
|
|
7036
7082
|
if (!active2) return;
|
|
@@ -7045,117 +7091,139 @@ function LivePanel({ active: active2 }) {
|
|
|
7045
7091
|
setBuffer((prev) => {
|
|
7046
7092
|
const seen = new Set(prev.map((e) => e.id));
|
|
7047
7093
|
const fresh = incoming.filter((e) => !seen.has(e.id)).sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
|
|
7048
|
-
return fresh.length ? [...prev, ...fresh].slice(-
|
|
7094
|
+
return fresh.length ? [...prev, ...fresh].slice(-400) : prev;
|
|
7049
7095
|
});
|
|
7050
7096
|
}, [feed.data]);
|
|
7051
|
-
const s = stats.data;
|
|
7052
|
-
const points = ts.data?.timeseries ?? [];
|
|
7053
7097
|
const cols = process.stdout.columns ?? 100;
|
|
7054
|
-
const rows = process.stdout.rows ??
|
|
7055
|
-
const
|
|
7056
|
-
const
|
|
7057
|
-
const
|
|
7058
|
-
const
|
|
7059
|
-
const
|
|
7060
|
-
const
|
|
7098
|
+
const rows = process.stdout.rows ?? 30;
|
|
7099
|
+
const s = stats.data;
|
|
7100
|
+
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);
|
|
7061
7110
|
const spin = SPIN[tick % SPIN.length];
|
|
7062
|
-
const
|
|
7111
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
7112
|
+
let dlpBufHits = 0;
|
|
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
|
+
}
|
|
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);
|
|
7121
|
+
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
7122
|
+
const lastDeny = denials[denials.length - 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);
|
|
7129
|
+
const tail = buffer.slice(-streamRows);
|
|
7130
|
+
const colW = Math.max(20, Math.floor((cols - 6) / 3));
|
|
7131
|
+
const innerW = cols - 2;
|
|
7063
7132
|
if (stats.error) return /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
|
|
7064
7133
|
"\u2717 ",
|
|
7065
7134
|
stats.error
|
|
7066
7135
|
] });
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
7071
|
-
/* @__PURE__ */
|
|
7072
|
-
|
|
7073
|
-
spin,
|
|
7074
|
-
" scanning"
|
|
7075
|
-
] }),
|
|
7076
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7077
|
-
" up ",
|
|
7078
|
-
fmtUp(Date.now() - startRef.current)
|
|
7079
|
-
] }),
|
|
7080
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7081
|
-
" ",
|
|
7082
|
-
hhmmss(Date.now())
|
|
7083
|
-
] }),
|
|
7084
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " poll 2s" })
|
|
7136
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25D0", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7137
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7138
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
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 " })
|
|
7085
7142
|
] }),
|
|
7086
|
-
/* @__PURE__ */ jsxs2(
|
|
7087
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "calls " }),
|
|
7143
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7144
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
7088
7145
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
7089
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
7146
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
|
|
7090
7147
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
7091
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
7148
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7092
7149
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
7093
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
7094
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7095
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
7096
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
7100
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children:
|
|
7101
|
-
|
|
7102
|
-
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7103
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "dny " }),
|
|
7104
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: sparkline(points.map((p) => p.denied), Math.min(64, cols - 30)) })
|
|
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 " }),
|
|
7153
|
+
/* @__PURE__ */ jsx2(Text2, { color: bursts ? theme.bad : theme.dim, children: bursts }),
|
|
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" })
|
|
7105
7159
|
] }),
|
|
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" }) }),
|
|
7106
7162
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7107
|
-
/* @__PURE__ */
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
"
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
] }),
|
|
7117
|
-
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
7118
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", flexGrow: 3, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
7119
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, bold: true, children: "\u2500\u2500 tool stream \u2500\u2500" }),
|
|
7120
|
-
tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
|
|
7121
|
-
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7163
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7164
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "DENIALS", extra: "24h", width: colW }),
|
|
7165
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: denySeries, height: colH - 1, width: colW, color: theme.bad })
|
|
7166
|
+
] }),
|
|
7167
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
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" }),
|
|
7122
7172
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7123
|
-
"
|
|
7124
|
-
|
|
7125
|
-
"
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7131
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
|
|
7132
|
-
] }, e.id))
|
|
7173
|
+
" ",
|
|
7174
|
+
truncate2(h.pattern, colW - 14),
|
|
7175
|
+
" ",
|
|
7176
|
+
ago(h.at),
|
|
7177
|
+
" ago"
|
|
7178
|
+
] })
|
|
7179
|
+
] }, i))
|
|
7133
7180
|
] }),
|
|
7134
|
-
|
|
7135
|
-
/* @__PURE__ */ jsx2(
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
] }
|
|
7144
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
7145
|
-
|
|
7181
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
|
|
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);
|
|
7146
7202
|
})
|
|
7147
|
-
] })
|
|
7203
|
+
] })
|
|
7148
7204
|
] }),
|
|
7149
|
-
/* @__PURE__ */
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
/* @__PURE__ */
|
|
7153
|
-
|
|
7154
|
-
|
|
7205
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `live tail \xB7 ${buffer.length} buffered`, width: innerW }),
|
|
7206
|
+
tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
|
|
7207
|
+
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7208
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7209
|
+
"[",
|
|
7210
|
+
hhmmss(e.created_at),
|
|
7211
|
+
"] "
|
|
7212
|
+
] }),
|
|
7213
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7214
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool_name, 14).padEnd(15) }),
|
|
7215
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.permission ?? "").slice(0, 4).padEnd(5) }),
|
|
7216
|
+
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7217
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
|
|
7218
|
+
] }, e.id)),
|
|
7219
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
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 " })
|
|
7155
7223
|
] })
|
|
7156
7224
|
] });
|
|
7157
7225
|
}
|
|
7158
|
-
var SPIN, hhmmss, fmtUp;
|
|
7226
|
+
var SPIN, BG, hhmmss, fmtUp;
|
|
7159
7227
|
var init_Live = __esm({
|
|
7160
7228
|
"src/tui/panels/Live.tsx"() {
|
|
7161
7229
|
"use strict";
|
|
@@ -7163,6 +7231,7 @@ var init_Live = __esm({
|
|
|
7163
7231
|
init_hooks();
|
|
7164
7232
|
init_theme();
|
|
7165
7233
|
SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7234
|
+
BG = "#12234f";
|
|
7166
7235
|
hhmmss = (ts) => {
|
|
7167
7236
|
const d = new Date(ts);
|
|
7168
7237
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -7969,6 +8038,18 @@ var init_Audit = __esm({
|
|
|
7969
8038
|
import { Box as Box8, Text as Text8, useApp, useInput as useInput5 } from "ink";
|
|
7970
8039
|
import { useState as useState7 } from "react";
|
|
7971
8040
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8041
|
+
function LiveHint() {
|
|
8042
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
8043
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
8044
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
8045
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
8046
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
8047
|
+
"press ",
|
|
8048
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
8049
|
+
" to go live"
|
|
8050
|
+
] }) })
|
|
8051
|
+
] });
|
|
8052
|
+
}
|
|
7972
8053
|
function Banner() {
|
|
7973
8054
|
const wide = (process.stdout.columns ?? 80) >= 82;
|
|
7974
8055
|
if (!wide) {
|
|
@@ -7994,17 +8075,23 @@ function App() {
|
|
|
7994
8075
|
else if (input === "q") exit();
|
|
7995
8076
|
} else {
|
|
7996
8077
|
if (key.escape || key.tab) setFocus("nav");
|
|
8078
|
+
else if (input === "q" && SECTIONS[section].label === "Live") exit();
|
|
7997
8079
|
}
|
|
7998
8080
|
});
|
|
8081
|
+
const cols = process.stdout.columns ?? 100;
|
|
8082
|
+
const rows = process.stdout.rows ?? 30;
|
|
7999
8083
|
const current = SECTIONS[section];
|
|
8084
|
+
if (current.label === "Live" && focus === "panel") {
|
|
8085
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
8086
|
+
}
|
|
8000
8087
|
const Panel = current.Panel;
|
|
8001
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
8088
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
8002
8089
|
/* @__PURE__ */ jsx8(Banner, {}),
|
|
8003
|
-
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
8090
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
|
|
8004
8091
|
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
8005
|
-
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1,
|
|
8092
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
8006
8093
|
] }),
|
|
8007
|
-
/* @__PURE__ */ jsx8(Box8, {
|
|
8094
|
+
/* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
8008
8095
|
] });
|
|
8009
8096
|
}
|
|
8010
8097
|
var SECTIONS, BANNER_HEX;
|
|
@@ -8021,7 +8108,7 @@ var init_App = __esm({
|
|
|
8021
8108
|
init_Stats();
|
|
8022
8109
|
init_Audit();
|
|
8023
8110
|
SECTIONS = [
|
|
8024
|
-
{ label: "Live", Panel:
|
|
8111
|
+
{ label: "Live", Panel: LiveHint },
|
|
8025
8112
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
8026
8113
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
8027
8114
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -8050,8 +8137,13 @@ async function launchTui() {
|
|
|
8050
8137
|
process.stderr.write("Not logged in. Run `solongate login` first.\n");
|
|
8051
8138
|
return;
|
|
8052
8139
|
}
|
|
8053
|
-
|
|
8054
|
-
|
|
8140
|
+
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
8141
|
+
try {
|
|
8142
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
|
|
8143
|
+
await waitUntilExit();
|
|
8144
|
+
} finally {
|
|
8145
|
+
process.stdout.write("\x1B[?1049l");
|
|
8146
|
+
}
|
|
8055
8147
|
}
|
|
8056
8148
|
var init_tui = __esm({
|
|
8057
8149
|
"src/tui/index.tsx"() {
|
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);
|
|
@@ -441,15 +442,61 @@ var fmtUp = (ms) => {
|
|
|
441
442
|
const p = (n) => String(n).padStart(2, "0");
|
|
442
443
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
443
444
|
};
|
|
445
|
+
function ColumnChart({ series, height, width, color, limit = 0 }) {
|
|
446
|
+
const tailVals = series.slice(-width);
|
|
447
|
+
const data = [...new Array(Math.max(0, width - tailVals.length)).fill(0), ...tailVals];
|
|
448
|
+
const max = Math.max(1, ...data, limit);
|
|
449
|
+
const lines = [];
|
|
450
|
+
for (let r = height; r >= 1; r--) {
|
|
451
|
+
const segs = [];
|
|
452
|
+
for (let i = 0; i < data.length; i++) {
|
|
453
|
+
const frac = data[i] / max;
|
|
454
|
+
const ch = frac >= r / height ? "\u2588" : frac >= (r - 0.5) / height ? "\u2584" : r === 1 && data[i] > 0 ? "\u2581" : " ";
|
|
455
|
+
const c2 = limit > 0 && data[i] > limit ? theme.bad : color;
|
|
456
|
+
const last = segs[segs.length - 1];
|
|
457
|
+
if (last && last.color === c2) last.text += ch;
|
|
458
|
+
else segs.push({ text: ch, color: c2 });
|
|
459
|
+
}
|
|
460
|
+
lines.push(
|
|
461
|
+
/* @__PURE__ */ jsx2(Text2, { children: segs.map((s, i) => /* @__PURE__ */ jsx2(Text2, { color: s.color, children: s.text }, i)) }, r)
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: lines });
|
|
465
|
+
}
|
|
466
|
+
function HBar({ label, value, max, width, color }) {
|
|
467
|
+
const filled = max > 0 ? Math.round(value / max * width) : 0;
|
|
468
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
469
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(9) }),
|
|
470
|
+
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width, filled)) }),
|
|
471
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2591".repeat(Math.max(0, width - filled)) + " " + value })
|
|
472
|
+
] });
|
|
473
|
+
}
|
|
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
|
+
] });
|
|
486
|
+
}
|
|
444
487
|
function LivePanel({ active: active2 }) {
|
|
445
488
|
const stats = useLoader(() => api.stats.get());
|
|
446
489
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
447
|
-
const feed = useLoader(() => api.audit.list({ limit:
|
|
490
|
+
const feed = useLoader(() => api.audit.list({ limit: 50 }));
|
|
491
|
+
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
492
|
+
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
448
493
|
usePoll(() => {
|
|
449
494
|
stats.reload();
|
|
450
495
|
ts.reload();
|
|
451
496
|
feed.reload();
|
|
452
497
|
}, 2e3, active2);
|
|
498
|
+
usePoll(sessions.reload, 5e3, active2);
|
|
499
|
+
usePoll(insights.reload, 1e4, active2);
|
|
453
500
|
const [tick, setTick] = useState2(0);
|
|
454
501
|
useEffect2(() => {
|
|
455
502
|
if (!active2) return;
|
|
@@ -464,113 +511,135 @@ function LivePanel({ active: active2 }) {
|
|
|
464
511
|
setBuffer((prev) => {
|
|
465
512
|
const seen = new Set(prev.map((e) => e.id));
|
|
466
513
|
const fresh = incoming.filter((e) => !seen.has(e.id)).sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
|
|
467
|
-
return fresh.length ? [...prev, ...fresh].slice(-
|
|
514
|
+
return fresh.length ? [...prev, ...fresh].slice(-400) : prev;
|
|
468
515
|
});
|
|
469
516
|
}, [feed.data]);
|
|
470
|
-
const s = stats.data;
|
|
471
|
-
const points = ts.data?.timeseries ?? [];
|
|
472
517
|
const cols = process.stdout.columns ?? 100;
|
|
473
|
-
const rows = process.stdout.rows ??
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
const
|
|
477
|
-
const
|
|
478
|
-
const
|
|
479
|
-
const
|
|
518
|
+
const rows = process.stdout.rows ?? 30;
|
|
519
|
+
const s = stats.data;
|
|
520
|
+
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);
|
|
480
530
|
const spin = SPIN[tick % SPIN.length];
|
|
481
|
-
const
|
|
531
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
532
|
+
let dlpBufHits = 0;
|
|
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
|
+
}
|
|
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);
|
|
541
|
+
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
542
|
+
const lastDeny = denials[denials.length - 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);
|
|
549
|
+
const tail = buffer.slice(-streamRows);
|
|
550
|
+
const colW = Math.max(20, Math.floor((cols - 6) / 3));
|
|
551
|
+
const innerW = cols - 2;
|
|
482
552
|
if (stats.error) return /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
|
|
483
553
|
"\u2717 ",
|
|
484
554
|
stats.error
|
|
485
555
|
] });
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
490
|
-
/* @__PURE__ */
|
|
491
|
-
|
|
492
|
-
spin,
|
|
493
|
-
" scanning"
|
|
494
|
-
] }),
|
|
495
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
496
|
-
" up ",
|
|
497
|
-
fmtUp(Date.now() - startRef.current)
|
|
498
|
-
] }),
|
|
499
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
500
|
-
" ",
|
|
501
|
-
hhmmss(Date.now())
|
|
502
|
-
] }),
|
|
503
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " poll 2s" })
|
|
556
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25D0", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
557
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
558
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
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 " })
|
|
504
562
|
] }),
|
|
505
|
-
/* @__PURE__ */ jsxs2(
|
|
506
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "calls " }),
|
|
563
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
564
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
507
565
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
508
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
566
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 allow " }),
|
|
509
567
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
510
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
568
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
511
569
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
512
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
513
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
514
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
515
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "
|
|
519
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children:
|
|
520
|
-
|
|
521
|
-
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
522
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "dny " }),
|
|
523
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: sparkline(points.map((p) => p.denied), Math.min(64, cols - 30)) })
|
|
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 " }),
|
|
573
|
+
/* @__PURE__ */ jsx2(Text2, { color: bursts ? theme.bad : theme.dim, children: bursts }),
|
|
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" })
|
|
524
579
|
] }),
|
|
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" }) }),
|
|
525
582
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
526
|
-
/* @__PURE__ */
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
"
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
] }),
|
|
536
|
-
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
537
|
-
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", flexGrow: 3, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
538
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, bold: true, children: "\u2500\u2500 tool stream \u2500\u2500" }),
|
|
539
|
-
tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
|
|
540
|
-
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
583
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
584
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "DENIALS", extra: "24h", width: colW }),
|
|
585
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: denySeries, height: colH - 1, width: colW, color: theme.bad })
|
|
586
|
+
] }),
|
|
587
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
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" }),
|
|
541
592
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
542
|
-
"
|
|
543
|
-
|
|
544
|
-
"
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
550
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
|
|
551
|
-
] }, e.id))
|
|
593
|
+
" ",
|
|
594
|
+
truncate(h.pattern, colW - 14),
|
|
595
|
+
" ",
|
|
596
|
+
ago(h.at),
|
|
597
|
+
" ago"
|
|
598
|
+
] })
|
|
599
|
+
] }, i))
|
|
552
600
|
] }),
|
|
553
|
-
|
|
554
|
-
/* @__PURE__ */ jsx2(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
] }
|
|
563
|
-
/* @__PURE__ */ jsx2(Text2, {
|
|
564
|
-
|
|
601
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, children: [
|
|
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);
|
|
565
622
|
})
|
|
566
|
-
] })
|
|
623
|
+
] })
|
|
567
624
|
] }),
|
|
568
|
-
/* @__PURE__ */
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
/* @__PURE__ */
|
|
572
|
-
|
|
573
|
-
|
|
625
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `live tail \xB7 ${buffer.length} buffered`, width: innerW }),
|
|
626
|
+
tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
|
|
627
|
+
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
628
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
629
|
+
"[",
|
|
630
|
+
hhmmss(e.created_at),
|
|
631
|
+
"] "
|
|
632
|
+
] }),
|
|
633
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
634
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool_name, 14).padEnd(15) }),
|
|
635
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.permission ?? "").slice(0, 4).padEnd(5) }),
|
|
636
|
+
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
637
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
|
|
638
|
+
] }, e.id)),
|
|
639
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
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 " })
|
|
574
643
|
] })
|
|
575
644
|
] });
|
|
576
645
|
}
|
|
@@ -1318,8 +1387,20 @@ function Detail({ label, value, color }) {
|
|
|
1318
1387
|
|
|
1319
1388
|
// src/tui/App.tsx
|
|
1320
1389
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1390
|
+
function LiveHint() {
|
|
1391
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1392
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
1393
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
1394
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
1395
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1396
|
+
"press ",
|
|
1397
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
1398
|
+
" to go live"
|
|
1399
|
+
] }) })
|
|
1400
|
+
] });
|
|
1401
|
+
}
|
|
1321
1402
|
var SECTIONS = [
|
|
1322
|
-
{ label: "Live", Panel:
|
|
1403
|
+
{ label: "Live", Panel: LiveHint },
|
|
1323
1404
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
1324
1405
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
1325
1406
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -1352,17 +1433,23 @@ function App() {
|
|
|
1352
1433
|
else if (input === "q") exit();
|
|
1353
1434
|
} else {
|
|
1354
1435
|
if (key.escape || key.tab) setFocus("nav");
|
|
1436
|
+
else if (input === "q" && SECTIONS[section].label === "Live") exit();
|
|
1355
1437
|
}
|
|
1356
1438
|
});
|
|
1439
|
+
const cols = process.stdout.columns ?? 100;
|
|
1440
|
+
const rows = process.stdout.rows ?? 30;
|
|
1357
1441
|
const current = SECTIONS[section];
|
|
1442
|
+
if (current.label === "Live" && focus === "panel") {
|
|
1443
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
1444
|
+
}
|
|
1358
1445
|
const Panel = current.Panel;
|
|
1359
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
1446
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
1360
1447
|
/* @__PURE__ */ jsx8(Banner, {}),
|
|
1361
|
-
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
1448
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
|
|
1362
1449
|
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
1363
|
-
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1,
|
|
1450
|
+
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
1364
1451
|
] }),
|
|
1365
|
-
/* @__PURE__ */ jsx8(Box8, {
|
|
1452
|
+
/* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
1366
1453
|
] });
|
|
1367
1454
|
}
|
|
1368
1455
|
|
|
@@ -1379,8 +1466,13 @@ async function launchTui() {
|
|
|
1379
1466
|
process.stderr.write("Not logged in. Run `solongate login` first.\n");
|
|
1380
1467
|
return;
|
|
1381
1468
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1469
|
+
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
1470
|
+
try {
|
|
1471
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
|
|
1472
|
+
await waitUntilExit();
|
|
1473
|
+
} finally {
|
|
1474
|
+
process.stdout.write("\x1B[?1049l");
|
|
1475
|
+
}
|
|
1384
1476
|
}
|
|
1385
1477
|
export {
|
|
1386
1478
|
launchTui
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "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": {
|