@solongate/proxy 0.63.0 → 0.64.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 +148 -85
- package/dist/tui/index.js +148 -85
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7022,15 +7022,50 @@ 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 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)) });
|
|
7057
|
+
}
|
|
7025
7058
|
function LivePanel({ active: active2 }) {
|
|
7026
7059
|
const stats = useLoader(() => api.stats.get());
|
|
7027
7060
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
7028
|
-
const feed = useLoader(() => api.audit.list({ limit:
|
|
7061
|
+
const feed = useLoader(() => api.audit.list({ limit: 50 }));
|
|
7062
|
+
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
7029
7063
|
usePoll(() => {
|
|
7030
7064
|
stats.reload();
|
|
7031
7065
|
ts.reload();
|
|
7032
7066
|
feed.reload();
|
|
7033
7067
|
}, 2e3, active2);
|
|
7068
|
+
usePoll(insights.reload, 1e4, active2);
|
|
7034
7069
|
const [tick, setTick] = useState2(0);
|
|
7035
7070
|
useEffect2(() => {
|
|
7036
7071
|
if (!active2) return;
|
|
@@ -7045,108 +7080,113 @@ function LivePanel({ active: active2 }) {
|
|
|
7045
7080
|
setBuffer((prev) => {
|
|
7046
7081
|
const seen = new Set(prev.map((e) => e.id));
|
|
7047
7082
|
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(-
|
|
7083
|
+
return fresh.length ? [...prev, ...fresh].slice(-400) : prev;
|
|
7049
7084
|
});
|
|
7050
7085
|
}, [feed.data]);
|
|
7051
|
-
const s = stats.data;
|
|
7052
|
-
const points = ts.data?.timeseries ?? [];
|
|
7053
7086
|
const cols = process.stdout.columns ?? 100;
|
|
7054
|
-
const rows = process.stdout.rows ??
|
|
7055
|
-
const
|
|
7056
|
-
const
|
|
7057
|
-
const
|
|
7058
|
-
const
|
|
7059
|
-
const
|
|
7060
|
-
const
|
|
7087
|
+
const rows = process.stdout.rows ?? 30;
|
|
7088
|
+
const s = stats.data;
|
|
7089
|
+
const ins = insights.data ?? {};
|
|
7090
|
+
const minuteRaw = (ins.activity?.minute ?? []).map((b) => b.count);
|
|
7091
|
+
const hourRaw = (ins.activity?.hour ?? []).map((b) => b.count);
|
|
7092
|
+
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";
|
|
7095
|
+
const limit = minuteLive ? ins.layers?.rateLimit?.perMinute ?? 0 : 0;
|
|
7096
|
+
const denySeries = (ts.data?.timeseries ?? []).map((p) => p.denied);
|
|
7061
7097
|
const spin = SPIN[tick % SPIN.length];
|
|
7062
7098
|
const cursor = tick % 4 < 2 ? "\u258C" : " ";
|
|
7099
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
7100
|
+
const permCounts = /* @__PURE__ */ new Map();
|
|
7101
|
+
for (const e of buffer) {
|
|
7102
|
+
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);
|
|
7111
|
+
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
7112
|
+
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));
|
|
7117
|
+
const tail = buffer.slice(-streamRows);
|
|
7118
|
+
const colW = Math.max(18, Math.floor((cols - 6) / 3));
|
|
7063
7119
|
if (stats.error) return /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
|
|
7064
7120
|
"\u2717 ",
|
|
7065
7121
|
stats.error
|
|
7066
7122
|
] });
|
|
7067
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
7068
|
-
/* @__PURE__ */ jsxs2(
|
|
7069
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
7070
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.accentBright, children: "LIVE" }),
|
|
7123
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7124
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7125
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE::LIVE " }),
|
|
7071
7126
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7072
|
-
"
|
|
7127
|
+
" ",
|
|
7073
7128
|
spin,
|
|
7074
|
-
"
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
] }),
|
|
7080
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7081
|
-
" ",
|
|
7082
|
-
hhmmss(Date.now())
|
|
7129
|
+
" up ",
|
|
7130
|
+
fmtUp(Date.now() - startRef.current),
|
|
7131
|
+
" \xB7 ",
|
|
7132
|
+
hhmmss(Date.now()),
|
|
7133
|
+
" \xB7 poll 2s"
|
|
7083
7134
|
] }),
|
|
7084
|
-
/* @__PURE__ */
|
|
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" })
|
|
7085
7143
|
] }),
|
|
7086
|
-
/* @__PURE__ */ jsxs2(
|
|
7144
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7087
7145
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "calls " }),
|
|
7088
7146
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
7089
7147
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " allow " }),
|
|
7090
7148
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
7091
7149
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " deny " }),
|
|
7092
7150
|
/* @__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:
|
|
7151
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " bursts(60m) " }),
|
|
7152
|
+
/* @__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" }),
|
|
7095
7155
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " policies " }),
|
|
7096
7156
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" })
|
|
7097
7157
|
] }),
|
|
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" }) }),
|
|
7098
7160
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7099
|
-
/* @__PURE__ */
|
|
7100
|
-
|
|
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)) })
|
|
7105
|
-
] }),
|
|
7106
|
-
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7107
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ">> " }),
|
|
7108
|
-
lastDeny ? /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
|
|
7109
|
-
"last deny ",
|
|
7110
|
-
hhmmss(lastDeny.created_at),
|
|
7111
|
-
" ",
|
|
7112
|
-
lastDeny.tool_name,
|
|
7113
|
-
" \xB7 ",
|
|
7114
|
-
truncate2(lastDeny.reason ?? "policy", Math.max(20, cols - 50))
|
|
7115
|
-
] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no denials in buffer" })
|
|
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: [
|
|
7122
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7123
|
-
"[",
|
|
7124
|
-
hhmmss(e.created_at),
|
|
7125
|
-
"] "
|
|
7126
|
-
] }),
|
|
7127
|
-
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7128
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool_name, 14).padEnd(15) }),
|
|
7129
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.permission ?? "").slice(0, 4).padEnd(5) }),
|
|
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))
|
|
7161
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7162
|
+
/* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "DENIALS 24h" }),
|
|
7163
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: denySeries, height: colH - 1, width: colW, color: theme.bad })
|
|
7133
7164
|
] }),
|
|
7134
|
-
|
|
7135
|
-
/* @__PURE__ */ jsx2(
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
] }, e.id + ":h"),
|
|
7144
|
-
/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + raw.replace(/\s+/g, " ") }, e.id + ":b")
|
|
7145
|
-
];
|
|
7146
|
-
})
|
|
7147
|
-
] }) : null
|
|
7165
|
+
/* @__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
|
+
] }),
|
|
7169
|
+
/* @__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))
|
|
7173
|
+
] })
|
|
7148
7174
|
] }),
|
|
7149
|
-
/* @__PURE__ */
|
|
7175
|
+
/* @__PURE__ */ jsx2(SectionTitle, { width: cols - 2, children: `TOOL STREAM \xB7 live tail \xB7 ${buffer.length} buffered` }),
|
|
7176
|
+
tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
|
|
7177
|
+
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7178
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7179
|
+
"[",
|
|
7180
|
+
hhmmss(e.created_at),
|
|
7181
|
+
"] "
|
|
7182
|
+
] }),
|
|
7183
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7184
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool_name, 14).padEnd(15) }),
|
|
7185
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.permission ?? "").slice(0, 4).padEnd(5) }),
|
|
7186
|
+
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7187
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
|
|
7188
|
+
] }, e.id)),
|
|
7189
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7150
7190
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "root@solongate" }),
|
|
7151
7191
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ":" }),
|
|
7152
7192
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: "~/live" }),
|
|
@@ -7969,6 +8009,18 @@ var init_Audit = __esm({
|
|
|
7969
8009
|
import { Box as Box8, Text as Text8, useApp, useInput as useInput5 } from "ink";
|
|
7970
8010
|
import { useState as useState7 } from "react";
|
|
7971
8011
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8012
|
+
function LiveHint() {
|
|
8013
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
8014
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
8015
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
8016
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
8017
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
8018
|
+
"press ",
|
|
8019
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
8020
|
+
" to go live"
|
|
8021
|
+
] }) })
|
|
8022
|
+
] });
|
|
8023
|
+
}
|
|
7972
8024
|
function Banner() {
|
|
7973
8025
|
const wide = (process.stdout.columns ?? 80) >= 82;
|
|
7974
8026
|
if (!wide) {
|
|
@@ -7994,17 +8046,23 @@ function App() {
|
|
|
7994
8046
|
else if (input === "q") exit();
|
|
7995
8047
|
} else {
|
|
7996
8048
|
if (key.escape || key.tab) setFocus("nav");
|
|
8049
|
+
else if (input === "q" && SECTIONS[section].label === "Live") exit();
|
|
7997
8050
|
}
|
|
7998
8051
|
});
|
|
8052
|
+
const cols = process.stdout.columns ?? 100;
|
|
8053
|
+
const rows = process.stdout.rows ?? 30;
|
|
7999
8054
|
const current = SECTIONS[section];
|
|
8055
|
+
if (current.label === "Live" && focus === "panel") {
|
|
8056
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
8057
|
+
}
|
|
8000
8058
|
const Panel = current.Panel;
|
|
8001
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
8059
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
8002
8060
|
/* @__PURE__ */ jsx8(Banner, {}),
|
|
8003
|
-
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
8061
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
|
|
8004
8062
|
/* @__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,
|
|
8063
|
+
/* @__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
8064
|
] }),
|
|
8007
|
-
/* @__PURE__ */ jsx8(Box8, {
|
|
8065
|
+
/* @__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
8066
|
] });
|
|
8009
8067
|
}
|
|
8010
8068
|
var SECTIONS, BANNER_HEX;
|
|
@@ -8021,7 +8079,7 @@ var init_App = __esm({
|
|
|
8021
8079
|
init_Stats();
|
|
8022
8080
|
init_Audit();
|
|
8023
8081
|
SECTIONS = [
|
|
8024
|
-
{ label: "Live", Panel:
|
|
8082
|
+
{ label: "Live", Panel: LiveHint },
|
|
8025
8083
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
8026
8084
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
8027
8085
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -8050,8 +8108,13 @@ async function launchTui() {
|
|
|
8050
8108
|
process.stderr.write("Not logged in. Run `solongate login` first.\n");
|
|
8051
8109
|
return;
|
|
8052
8110
|
}
|
|
8053
|
-
|
|
8054
|
-
|
|
8111
|
+
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
8112
|
+
try {
|
|
8113
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
|
|
8114
|
+
await waitUntilExit();
|
|
8115
|
+
} finally {
|
|
8116
|
+
process.stdout.write("\x1B[?1049l");
|
|
8117
|
+
}
|
|
8055
8118
|
}
|
|
8056
8119
|
var init_tui = __esm({
|
|
8057
8120
|
"src/tui/index.tsx"() {
|
package/dist/tui/index.js
CHANGED
|
@@ -441,15 +441,50 @@ var fmtUp = (ms) => {
|
|
|
441
441
|
const p = (n) => String(n).padStart(2, "0");
|
|
442
442
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
443
443
|
};
|
|
444
|
+
function ColumnChart({ series, height, width, color, limit = 0 }) {
|
|
445
|
+
const tailVals = series.slice(-width);
|
|
446
|
+
const data = [...new Array(Math.max(0, width - tailVals.length)).fill(0), ...tailVals];
|
|
447
|
+
const max = Math.max(1, ...data, limit);
|
|
448
|
+
const lines = [];
|
|
449
|
+
for (let r = height; r >= 1; r--) {
|
|
450
|
+
const segs = [];
|
|
451
|
+
for (let i = 0; i < data.length; i++) {
|
|
452
|
+
const frac = data[i] / max;
|
|
453
|
+
const ch = frac >= r / height ? "\u2588" : frac >= (r - 0.5) / height ? "\u2584" : r === 1 && data[i] > 0 ? "\u2581" : " ";
|
|
454
|
+
const c2 = limit > 0 && data[i] > limit ? theme.bad : color;
|
|
455
|
+
const last = segs[segs.length - 1];
|
|
456
|
+
if (last && last.color === c2) last.text += ch;
|
|
457
|
+
else segs.push({ text: ch, color: c2 });
|
|
458
|
+
}
|
|
459
|
+
lines.push(
|
|
460
|
+
/* @__PURE__ */ jsx2(Text2, { children: segs.map((s, i) => /* @__PURE__ */ jsx2(Text2, { color: s.color, children: s.text }, i)) }, r)
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: lines });
|
|
464
|
+
}
|
|
465
|
+
function HBar({ label, value, max, width, color }) {
|
|
466
|
+
const filled = max > 0 ? Math.round(value / max * width) : 0;
|
|
467
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
468
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label.padEnd(9) }),
|
|
469
|
+
/* @__PURE__ */ jsx2(Text2, { color, children: "\u2588".repeat(Math.min(width, filled)) }),
|
|
470
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2591".repeat(Math.max(0, width - filled)) + " " + value })
|
|
471
|
+
] });
|
|
472
|
+
}
|
|
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)) });
|
|
476
|
+
}
|
|
444
477
|
function LivePanel({ active: active2 }) {
|
|
445
478
|
const stats = useLoader(() => api.stats.get());
|
|
446
479
|
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
447
|
-
const feed = useLoader(() => api.audit.list({ limit:
|
|
480
|
+
const feed = useLoader(() => api.audit.list({ limit: 50 }));
|
|
481
|
+
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
448
482
|
usePoll(() => {
|
|
449
483
|
stats.reload();
|
|
450
484
|
ts.reload();
|
|
451
485
|
feed.reload();
|
|
452
486
|
}, 2e3, active2);
|
|
487
|
+
usePoll(insights.reload, 1e4, active2);
|
|
453
488
|
const [tick, setTick] = useState2(0);
|
|
454
489
|
useEffect2(() => {
|
|
455
490
|
if (!active2) return;
|
|
@@ -464,108 +499,113 @@ function LivePanel({ active: active2 }) {
|
|
|
464
499
|
setBuffer((prev) => {
|
|
465
500
|
const seen = new Set(prev.map((e) => e.id));
|
|
466
501
|
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(-
|
|
502
|
+
return fresh.length ? [...prev, ...fresh].slice(-400) : prev;
|
|
468
503
|
});
|
|
469
504
|
}, [feed.data]);
|
|
470
|
-
const s = stats.data;
|
|
471
|
-
const points = ts.data?.timeseries ?? [];
|
|
472
505
|
const cols = process.stdout.columns ?? 100;
|
|
473
|
-
const rows = process.stdout.rows ??
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
const
|
|
477
|
-
const
|
|
478
|
-
const
|
|
479
|
-
const
|
|
506
|
+
const rows = process.stdout.rows ?? 30;
|
|
507
|
+
const s = stats.data;
|
|
508
|
+
const ins = insights.data ?? {};
|
|
509
|
+
const minuteRaw = (ins.activity?.minute ?? []).map((b) => b.count);
|
|
510
|
+
const hourRaw = (ins.activity?.hour ?? []).map((b) => b.count);
|
|
511
|
+
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";
|
|
514
|
+
const limit = minuteLive ? ins.layers?.rateLimit?.perMinute ?? 0 : 0;
|
|
515
|
+
const denySeries = (ts.data?.timeseries ?? []).map((p) => p.denied);
|
|
480
516
|
const spin = SPIN[tick % SPIN.length];
|
|
481
517
|
const cursor = tick % 4 < 2 ? "\u258C" : " ";
|
|
518
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
519
|
+
const permCounts = /* @__PURE__ */ new Map();
|
|
520
|
+
for (const e of buffer) {
|
|
521
|
+
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);
|
|
524
|
+
}
|
|
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);
|
|
530
|
+
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
531
|
+
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));
|
|
536
|
+
const tail = buffer.slice(-streamRows);
|
|
537
|
+
const colW = Math.max(18, Math.floor((cols - 6) / 3));
|
|
482
538
|
if (stats.error) return /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
|
|
483
539
|
"\u2717 ",
|
|
484
540
|
stats.error
|
|
485
541
|
] });
|
|
486
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
487
|
-
/* @__PURE__ */ jsxs2(
|
|
488
|
-
/* @__PURE__ */ jsx2(Text2, { color:
|
|
489
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.accentBright, children: "LIVE" }),
|
|
542
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
543
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
544
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE::LIVE " }),
|
|
490
545
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
491
|
-
"
|
|
546
|
+
" ",
|
|
492
547
|
spin,
|
|
493
|
-
"
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
] }),
|
|
499
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
500
|
-
" ",
|
|
501
|
-
hhmmss(Date.now())
|
|
548
|
+
" up ",
|
|
549
|
+
fmtUp(Date.now() - startRef.current),
|
|
550
|
+
" \xB7 ",
|
|
551
|
+
hhmmss(Date.now()),
|
|
552
|
+
" \xB7 poll 2s"
|
|
502
553
|
] }),
|
|
503
|
-
/* @__PURE__ */
|
|
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" })
|
|
504
562
|
] }),
|
|
505
|
-
/* @__PURE__ */ jsxs2(
|
|
563
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
506
564
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "calls " }),
|
|
507
565
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: s ? s.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
508
566
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " allow " }),
|
|
509
567
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
510
568
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " 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:
|
|
570
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " bursts(60m) " }),
|
|
571
|
+
/* @__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" }),
|
|
514
574
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " policies " }),
|
|
515
575
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" })
|
|
516
576
|
] }),
|
|
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" }) }),
|
|
517
579
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
/* @__PURE__ */
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
526
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ">> " }),
|
|
527
|
-
lastDeny ? /* @__PURE__ */ jsxs2(Text2, { color: theme.bad, children: [
|
|
528
|
-
"last deny ",
|
|
529
|
-
hhmmss(lastDeny.created_at),
|
|
530
|
-
" ",
|
|
531
|
-
lastDeny.tool_name,
|
|
532
|
-
" \xB7 ",
|
|
533
|
-
truncate(lastDeny.reason ?? "policy", Math.max(20, cols - 50))
|
|
534
|
-
] }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no denials in buffer" })
|
|
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: [
|
|
541
|
-
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
542
|
-
"[",
|
|
543
|
-
hhmmss(e.created_at),
|
|
544
|
-
"] "
|
|
545
|
-
] }),
|
|
546
|
-
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
547
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool_name, 14).padEnd(15) }),
|
|
548
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.permission ?? "").slice(0, 4).padEnd(5) }),
|
|
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))
|
|
580
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
581
|
+
/* @__PURE__ */ jsx2(SectionTitle, { width: colW, children: "DENIALS 24h" }),
|
|
582
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: denySeries, height: colH - 1, width: colW, color: theme.bad })
|
|
583
|
+
] }),
|
|
584
|
+
/* @__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" })
|
|
552
587
|
] }),
|
|
553
|
-
|
|
554
|
-
/* @__PURE__ */ jsx2(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", color: e.decision === "ALLOW" ? theme.dim : theme.bad, children: [
|
|
559
|
-
e.id.slice(0, 8),
|
|
560
|
-
" ",
|
|
561
|
-
e.tool_name.slice(0, 12)
|
|
562
|
-
] }, e.id + ":h"),
|
|
563
|
-
/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + raw.replace(/\s+/g, " ") }, e.id + ":b")
|
|
564
|
-
];
|
|
565
|
-
})
|
|
566
|
-
] }) : null
|
|
588
|
+
/* @__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))
|
|
592
|
+
] })
|
|
567
593
|
] }),
|
|
568
|
-
/* @__PURE__ */
|
|
594
|
+
/* @__PURE__ */ jsx2(SectionTitle, { width: cols - 2, children: `TOOL STREAM \xB7 live tail \xB7 ${buffer.length} buffered` }),
|
|
595
|
+
tail.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "awaiting traffic\u2026" }) : null,
|
|
596
|
+
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
597
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
598
|
+
"[",
|
|
599
|
+
hhmmss(e.created_at),
|
|
600
|
+
"] "
|
|
601
|
+
] }),
|
|
602
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
603
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool_name, 14).padEnd(15) }),
|
|
604
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.permission ?? "").slice(0, 4).padEnd(5) }),
|
|
605
|
+
e.dlp_matches?.length ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
606
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " ") })
|
|
607
|
+
] }, e.id)),
|
|
608
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
569
609
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "root@solongate" }),
|
|
570
610
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ":" }),
|
|
571
611
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: "~/live" }),
|
|
@@ -1318,8 +1358,20 @@ function Detail({ label, value, color }) {
|
|
|
1318
1358
|
|
|
1319
1359
|
// src/tui/App.tsx
|
|
1320
1360
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1361
|
+
function LiveHint() {
|
|
1362
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1363
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
1364
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
1365
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
1366
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1367
|
+
"press ",
|
|
1368
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
1369
|
+
" to go live"
|
|
1370
|
+
] }) })
|
|
1371
|
+
] });
|
|
1372
|
+
}
|
|
1321
1373
|
var SECTIONS = [
|
|
1322
|
-
{ label: "Live", Panel:
|
|
1374
|
+
{ label: "Live", Panel: LiveHint },
|
|
1323
1375
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
1324
1376
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
1325
1377
|
{ label: "DLP", Panel: DlpPanel },
|
|
@@ -1352,17 +1404,23 @@ function App() {
|
|
|
1352
1404
|
else if (input === "q") exit();
|
|
1353
1405
|
} else {
|
|
1354
1406
|
if (key.escape || key.tab) setFocus("nav");
|
|
1407
|
+
else if (input === "q" && SECTIONS[section].label === "Live") exit();
|
|
1355
1408
|
}
|
|
1356
1409
|
});
|
|
1410
|
+
const cols = process.stdout.columns ?? 100;
|
|
1411
|
+
const rows = process.stdout.rows ?? 30;
|
|
1357
1412
|
const current = SECTIONS[section];
|
|
1413
|
+
if (current.label === "Live" && focus === "panel") {
|
|
1414
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
|
|
1415
|
+
}
|
|
1358
1416
|
const Panel = current.Panel;
|
|
1359
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
1417
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
1360
1418
|
/* @__PURE__ */ jsx8(Banner, {}),
|
|
1361
|
-
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
1419
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
|
|
1362
1420
|
/* @__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,
|
|
1421
|
+
/* @__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
1422
|
] }),
|
|
1365
|
-
/* @__PURE__ */ jsx8(Box8, {
|
|
1423
|
+
/* @__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
1424
|
] });
|
|
1367
1425
|
}
|
|
1368
1426
|
|
|
@@ -1379,8 +1437,13 @@ async function launchTui() {
|
|
|
1379
1437
|
process.stderr.write("Not logged in. Run `solongate login` first.\n");
|
|
1380
1438
|
return;
|
|
1381
1439
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1440
|
+
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
1441
|
+
try {
|
|
1442
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
|
|
1443
|
+
await waitUntilExit();
|
|
1444
|
+
} finally {
|
|
1445
|
+
process.stdout.write("\x1B[?1049l");
|
|
1446
|
+
}
|
|
1384
1447
|
}
|
|
1385
1448
|
export {
|
|
1386
1449
|
launchTui
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.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": {
|