@solongate/proxy 0.66.0 → 0.67.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 +246 -109
- package/dist/tui/index.js +201 -64
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7020,9 +7020,39 @@ 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 { closeSync, openSync, readSync, statSync } from "fs";
|
|
7024
|
+
import { homedir as homedir3 } from "os";
|
|
7025
|
+
import { join as join5 } from "path";
|
|
7023
7026
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
7024
7027
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7025
|
-
function
|
|
7028
|
+
function tailLines(file, maxBytes = 65536) {
|
|
7029
|
+
try {
|
|
7030
|
+
const size = statSync(file).size;
|
|
7031
|
+
const start = Math.max(0, size - maxBytes);
|
|
7032
|
+
const fd = openSync(file, "r");
|
|
7033
|
+
const buf = Buffer.alloc(size - start);
|
|
7034
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
7035
|
+
closeSync(fd);
|
|
7036
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
7037
|
+
if (start > 0) lines.shift();
|
|
7038
|
+
return lines;
|
|
7039
|
+
} catch {
|
|
7040
|
+
return [];
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
function cloudItem(e) {
|
|
7044
|
+
return {
|
|
7045
|
+
id: "c:" + e.id,
|
|
7046
|
+
at: Date.parse(e.created_at),
|
|
7047
|
+
tool: e.tool_name,
|
|
7048
|
+
decision: e.decision,
|
|
7049
|
+
permission: (e.permission ?? "").slice(0, 4),
|
|
7050
|
+
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
7051
|
+
dlp: !!e.dlp_matches?.length,
|
|
7052
|
+
source: "cloud"
|
|
7053
|
+
};
|
|
7054
|
+
}
|
|
7055
|
+
function ColumnChart({ series, hot, height, width: width2, color, hotColor = theme.bad }) {
|
|
7026
7056
|
const pad = Math.max(0, width2 - series.length);
|
|
7027
7057
|
const data = [...new Array(pad).fill(0), ...series.slice(-width2)];
|
|
7028
7058
|
const hotPad = hot ? [...new Array(pad).fill(false), ...hot.slice(-width2)] : void 0;
|
|
@@ -7034,13 +7064,11 @@ function ColumnChart({ series, hot, height, width: width2, color }) {
|
|
|
7034
7064
|
const v = data[i];
|
|
7035
7065
|
const frac = v / max;
|
|
7036
7066
|
let ch;
|
|
7037
|
-
let c2;
|
|
7038
7067
|
if (frac >= r / height) ch = "\u2588";
|
|
7039
7068
|
else if (frac >= (r - 0.5) / height) ch = "\u2584";
|
|
7040
7069
|
else if (r === 1) ch = "\u2581";
|
|
7041
7070
|
else ch = " ";
|
|
7042
|
-
|
|
7043
|
-
else c2 = hotPad?.[i] ? theme.bad : color;
|
|
7071
|
+
const c2 = r === 1 && v === 0 ? "#233457" : hotPad?.[i] ? hotColor : color;
|
|
7044
7072
|
const last = segs[segs.length - 1];
|
|
7045
7073
|
if (last && last.color === c2) last.text += ch;
|
|
7046
7074
|
else segs.push({ text: ch, color: c2 });
|
|
@@ -7076,60 +7104,147 @@ function LivePanel({ active: active2 }) {
|
|
|
7076
7104
|
const [s, setS] = useState2(null);
|
|
7077
7105
|
const [tsData, setTsData] = useState2(null);
|
|
7078
7106
|
const [lat, setLat] = useState2([]);
|
|
7079
|
-
const [
|
|
7107
|
+
const [cloudBuf, setCloudBuf] = useState2([]);
|
|
7108
|
+
const [localBuf, setLocalBuf] = useState2([]);
|
|
7109
|
+
const [localOn, setLocalOn] = useState2(null);
|
|
7110
|
+
const [ring, setRing] = useState2(null);
|
|
7080
7111
|
const [log6, setLog] = useState2([]);
|
|
7081
7112
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7113
|
+
const lastLocalTs = useRef2(0);
|
|
7114
|
+
const pausedUntil = useRef2(0);
|
|
7082
7115
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7083
7116
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7084
7117
|
}, []);
|
|
7085
|
-
const
|
|
7118
|
+
const onApiError = useCallback2(
|
|
7119
|
+
(e) => {
|
|
7120
|
+
const is429 = e instanceof ApiError && e.status === 429;
|
|
7121
|
+
if (is429 && Date.now() >= pausedUntil.current) {
|
|
7122
|
+
pausedUntil.current = Date.now() + 3e4;
|
|
7123
|
+
pushLog("rate limited by api \xB7 backing off 30s", "warn");
|
|
7124
|
+
} else if (!is429) {
|
|
7125
|
+
pushLog(`api error \xB7 ${truncate2(e instanceof Error ? e.message : String(e), 40)}`, "bad");
|
|
7126
|
+
}
|
|
7127
|
+
},
|
|
7128
|
+
[pushLog]
|
|
7129
|
+
);
|
|
7130
|
+
const paused = () => Date.now() < pausedUntil.current;
|
|
7131
|
+
const pollLocal = useCallback2(() => {
|
|
7132
|
+
const lines = tailLines(LOCAL_LOG);
|
|
7133
|
+
if (!lines.length) {
|
|
7134
|
+
setLocalOn((prev) => prev === null ? false : prev);
|
|
7135
|
+
return;
|
|
7136
|
+
}
|
|
7137
|
+
setLocalOn(true);
|
|
7138
|
+
const fresh = [];
|
|
7139
|
+
for (const line of lines) {
|
|
7140
|
+
try {
|
|
7141
|
+
const j = JSON.parse(line);
|
|
7142
|
+
const at = Date.parse(j.ts ?? "");
|
|
7143
|
+
if (!Number.isFinite(at) || at <= lastLocalTs.current) continue;
|
|
7144
|
+
fresh.push({
|
|
7145
|
+
id: "l:" + at + ":" + (j.tool ?? ""),
|
|
7146
|
+
at,
|
|
7147
|
+
tool: j.tool ?? "?",
|
|
7148
|
+
decision: j.decision ?? "ALLOW",
|
|
7149
|
+
permission: "",
|
|
7150
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
7151
|
+
dlp: !!j.dlp,
|
|
7152
|
+
source: "local"
|
|
7153
|
+
});
|
|
7154
|
+
} catch {
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
if (fresh.length) {
|
|
7158
|
+
lastLocalTs.current = fresh[fresh.length - 1].at;
|
|
7159
|
+
setLocalBuf((prev) => [...prev, ...fresh].slice(-400));
|
|
7160
|
+
const denies = fresh.filter((f) => f.decision !== "ALLOW").length;
|
|
7161
|
+
if (lastLocalTs.current !== fresh[0].at || fresh.length < 10) {
|
|
7162
|
+
pushLog(`local +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : "warn");
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
7165
|
+
const ringLines = tailLines(RING, 8192).slice(-30);
|
|
7166
|
+
if (ringLines.length) {
|
|
7167
|
+
let sum = 0;
|
|
7168
|
+
let n = 0;
|
|
7169
|
+
let session = "";
|
|
7170
|
+
for (const rl2 of ringLines) {
|
|
7171
|
+
try {
|
|
7172
|
+
const j = JSON.parse(rl2);
|
|
7173
|
+
if (typeof j.ms === "number") {
|
|
7174
|
+
sum += j.ms;
|
|
7175
|
+
n++;
|
|
7176
|
+
}
|
|
7177
|
+
if (j.session) session = j.session;
|
|
7178
|
+
} catch {
|
|
7179
|
+
}
|
|
7180
|
+
}
|
|
7181
|
+
if (n) setRing({ avgMs: Math.round(sum / n), session, count: n });
|
|
7182
|
+
}
|
|
7183
|
+
}, [pushLog]);
|
|
7184
|
+
useEffect2(() => {
|
|
7185
|
+
if (!active2) return;
|
|
7186
|
+
pollLocal();
|
|
7187
|
+
const t = setInterval(pollLocal, 2e3);
|
|
7188
|
+
return () => clearInterval(t);
|
|
7189
|
+
}, [active2, pollLocal]);
|
|
7190
|
+
const pollFeed = useCallback2(async () => {
|
|
7191
|
+
if (paused()) return;
|
|
7086
7192
|
const t0 = Date.now();
|
|
7087
7193
|
try {
|
|
7088
|
-
const
|
|
7194
|
+
const fd = await api.audit.list({ limit: 50 });
|
|
7089
7195
|
const ms = Date.now() - t0;
|
|
7090
|
-
|
|
7091
|
-
setTsData(td);
|
|
7092
|
-
setLat((l) => [...l.slice(-47), ms]);
|
|
7196
|
+
setLat((l) => [...l, ms].slice(-240));
|
|
7093
7197
|
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
7198
|
+
const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
|
|
7094
7199
|
for (const e of fresh) seenRef.current.add(e.id);
|
|
7095
|
-
const firstLoad = seenRef.current.size === fresh.length && fresh.length > 1;
|
|
7096
7200
|
if (fresh.length) {
|
|
7097
|
-
|
|
7201
|
+
setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
7098
7202
|
}
|
|
7099
7203
|
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
7100
|
-
|
|
7101
|
-
|
|
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
|
-
);
|
|
7204
|
+
if (firstLoad) pushLog(`cloud link up \xB7 api ${ms}ms \xB7 ${fresh.length} calls`);
|
|
7205
|
+
else pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : fresh.length ? "warn" : "ok");
|
|
7107
7206
|
} catch (e) {
|
|
7108
|
-
|
|
7207
|
+
onApiError(e);
|
|
7109
7208
|
}
|
|
7110
|
-
}, [pushLog]);
|
|
7209
|
+
}, [pushLog, onApiError]);
|
|
7210
|
+
const pollMedium = useCallback2(async () => {
|
|
7211
|
+
if (paused()) return;
|
|
7212
|
+
try {
|
|
7213
|
+
setS(await api.stats.get());
|
|
7214
|
+
} catch (e) {
|
|
7215
|
+
onApiError(e);
|
|
7216
|
+
}
|
|
7217
|
+
}, [onApiError]);
|
|
7218
|
+
const pollSlow = useCallback2(async () => {
|
|
7219
|
+
if (paused()) return;
|
|
7220
|
+
try {
|
|
7221
|
+
setTsData(await api.stats.timeseries({ period: "24h" }));
|
|
7222
|
+
} catch (e) {
|
|
7223
|
+
onApiError(e);
|
|
7224
|
+
}
|
|
7225
|
+
}, [onApiError]);
|
|
7111
7226
|
useEffect2(() => {
|
|
7112
7227
|
if (!active2) return;
|
|
7113
|
-
void
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7228
|
+
void pollFeed();
|
|
7229
|
+
void pollMedium();
|
|
7230
|
+
void pollSlow();
|
|
7231
|
+
const t1 = setInterval(() => void pollFeed(), 4e3);
|
|
7232
|
+
const t2 = setInterval(() => void pollMedium(), 1e4);
|
|
7233
|
+
const t3 = setInterval(() => void pollSlow(), 3e4);
|
|
7234
|
+
return () => {
|
|
7235
|
+
clearInterval(t1);
|
|
7236
|
+
clearInterval(t2);
|
|
7237
|
+
clearInterval(t3);
|
|
7238
|
+
};
|
|
7239
|
+
}, [active2, pollFeed, pollMedium, pollSlow]);
|
|
7117
7240
|
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
7118
7241
|
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
7119
|
-
usePoll(
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
if (
|
|
7124
|
-
},
|
|
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]);
|
|
7242
|
+
usePoll(() => {
|
|
7243
|
+
if (!paused()) sessions.reload();
|
|
7244
|
+
}, 1e4, active2);
|
|
7245
|
+
usePoll(() => {
|
|
7246
|
+
if (!paused()) insights.reload();
|
|
7247
|
+
}, 3e4, active2);
|
|
7133
7248
|
const [tick, setTick] = useState2(0);
|
|
7134
7249
|
useEffect2(() => {
|
|
7135
7250
|
if (!active2) return;
|
|
@@ -7149,29 +7264,33 @@ function LivePanel({ active: active2 }) {
|
|
|
7149
7264
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
7150
7265
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
7151
7266
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
7267
|
+
const merged = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7268
|
+
const denials = merged.filter((e) => e.decision !== "ALLOW");
|
|
7269
|
+
const lastDeny = denials[denials.length - 1];
|
|
7152
7270
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
7153
|
-
for (const e of
|
|
7271
|
+
for (const e of merged) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
7154
7272
|
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
7155
|
-
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
7156
|
-
const lastDeny = denials[denials.length - 1];
|
|
7157
7273
|
const latNow = lat[lat.length - 1] ?? 0;
|
|
7158
|
-
const
|
|
7274
|
+
const sortedLat = [...lat].sort((a, b) => a - b);
|
|
7275
|
+
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
7276
|
+
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
7277
|
+
const backingOff = Date.now() < pausedUntil.current;
|
|
7159
7278
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
7160
7279
|
const sessCounts = sessions.data?.counts;
|
|
7161
7280
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7162
7281
|
const colH = rows >= 32 ? 6 : 5;
|
|
7163
|
-
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
7164
|
-
const tail =
|
|
7282
|
+
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
7283
|
+
const tail = merged.slice(-streamRows);
|
|
7165
7284
|
const innerW = cols - 2;
|
|
7166
7285
|
const leftW = Math.floor(innerW * 0.55);
|
|
7167
7286
|
const rightW = innerW - leftW - 2;
|
|
7168
7287
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7169
|
-
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\
|
|
7288
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7170
7289
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7171
7290
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7172
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: "
|
|
7291
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7173
7292
|
/* @__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.
|
|
7293
|
+
backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
|
|
7175
7294
|
] }),
|
|
7176
7295
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7177
7296
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -7180,14 +7299,14 @@ function LivePanel({ active: active2 }) {
|
|
|
7180
7299
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
7181
7300
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7182
7301
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
7302
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 local " }),
|
|
7303
|
+
localOn ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "\u2713 streaming" }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "off" }),
|
|
7183
7304
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
7184
7305
|
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
7185
7306
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
7186
7307
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
7187
7308
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
|
|
7188
7309
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC ${sessCounts.deactivated}\u25CB` : "\xB7" }),
|
|
7189
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
|
|
7190
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
|
|
7191
7310
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7192
7311
|
] }),
|
|
7193
7312
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -7196,8 +7315,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7196
7315
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
7197
7316
|
] }),
|
|
7198
7317
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
7199
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `
|
|
7200
|
-
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v >
|
|
7318
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
|
|
7319
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0", hotColor: "#ffb454" })
|
|
7201
7320
|
] })
|
|
7202
7321
|
] }),
|
|
7203
7322
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -7217,12 +7336,22 @@ function LivePanel({ active: active2 }) {
|
|
|
7217
7336
|
" patterns armed"
|
|
7218
7337
|
] })
|
|
7219
7338
|
] }),
|
|
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" })
|
|
7339
|
+
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" }),
|
|
7340
|
+
ring ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7341
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
7342
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local \u2713" }),
|
|
7343
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` eval avg ${ring.avgMs}ms \xB7 ${ring.count} recent` })
|
|
7344
|
+
] }) : null
|
|
7221
7345
|
] }),
|
|
7222
7346
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7223
7347
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} live` : "", width: colW }),
|
|
7224
|
-
|
|
7225
|
-
|
|
7348
|
+
ring?.session ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7349
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "\u25CF " }),
|
|
7350
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: true, children: "this machine" }),
|
|
7351
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + truncate2(ring.session, 12) + " \xB7 guarded" })
|
|
7352
|
+
] }) : null,
|
|
7353
|
+
sess.length === 0 && !ring ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7354
|
+
sess.slice(0, colH - 1 - (ring ? 1 : 0)).map((a) => {
|
|
7226
7355
|
const dot = sessionDot(a.status);
|
|
7227
7356
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7228
7357
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
@@ -7254,7 +7383,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7254
7383
|
] }, l.ts + ":" + i))
|
|
7255
7384
|
] })
|
|
7256
7385
|
] }),
|
|
7257
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `tail
|
|
7386
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `local+cloud tail \xB7 ${merged.length} buffered`, width: innerW }),
|
|
7387
|
+
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7388
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
7389
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable in dashboard \u2192 Settings \u2192 Local logs; hooks then write ~/.solongate/local-logs (view them there with `solongate logs-server`)" })
|
|
7390
|
+
] }) : null,
|
|
7258
7391
|
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7259
7392
|
spin,
|
|
7260
7393
|
" awaiting traffic\u2026"
|
|
@@ -7262,23 +7395,25 @@ function LivePanel({ active: active2 }) {
|
|
|
7262
7395
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7263
7396
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7264
7397
|
"[",
|
|
7265
|
-
hhmmss(e.
|
|
7266
|
-
"
|
|
7398
|
+
hhmmss(e.at),
|
|
7399
|
+
" "
|
|
7267
7400
|
] }),
|
|
7401
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
7402
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
7268
7403
|
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7269
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.
|
|
7270
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
7271
|
-
e.
|
|
7272
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
7404
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 14).padEnd(15) }),
|
|
7405
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
7406
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7407
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
7273
7408
|
] }, e.id)),
|
|
7274
7409
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7275
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
7276
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${
|
|
7410
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7411
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${merged.length} ` }),
|
|
7277
7412
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
7278
7413
|
] })
|
|
7279
7414
|
] });
|
|
7280
7415
|
}
|
|
7281
|
-
var SPIN, BG, hhmmss, fmtUp;
|
|
7416
|
+
var SPIN, BG, LOCAL_LOG, RING, hhmmss, fmtUp;
|
|
7282
7417
|
var init_Live = __esm({
|
|
7283
7418
|
"src/tui/panels/Live.tsx"() {
|
|
7284
7419
|
"use strict";
|
|
@@ -7287,6 +7422,8 @@ var init_Live = __esm({
|
|
|
7287
7422
|
init_theme();
|
|
7288
7423
|
SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7289
7424
|
BG = "#12234f";
|
|
7425
|
+
LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
7426
|
+
RING = join5(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
7290
7427
|
hhmmss = (ts) => {
|
|
7291
7428
|
const d = new Date(ts);
|
|
7292
7429
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -8958,8 +9095,8 @@ __export(global_install_exports, {
|
|
|
8958
9095
|
unlockProtected: () => unlockProtected
|
|
8959
9096
|
});
|
|
8960
9097
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
8961
|
-
import { resolve as resolve4, join as
|
|
8962
|
-
import { homedir as
|
|
9098
|
+
import { resolve as resolve4, join as join6, dirname } from "path";
|
|
9099
|
+
import { homedir as homedir4 } from "os";
|
|
8963
9100
|
import { fileURLToPath } from "url";
|
|
8964
9101
|
import { createInterface } from "readline";
|
|
8965
9102
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -9022,10 +9159,10 @@ function unlockFile(file) {
|
|
|
9022
9159
|
function protectedTargets() {
|
|
9023
9160
|
const p = globalPaths();
|
|
9024
9161
|
return [
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9162
|
+
join6(p.hooksDir, "guard.mjs"),
|
|
9163
|
+
join6(p.hooksDir, "audit.mjs"),
|
|
9164
|
+
join6(p.hooksDir, "stop.mjs"),
|
|
9165
|
+
join6(p.hooksDir, "shield.mjs"),
|
|
9029
9166
|
p.configPath,
|
|
9030
9167
|
p.settingsPath
|
|
9031
9168
|
];
|
|
@@ -9037,25 +9174,25 @@ function unlockProtected() {
|
|
|
9037
9174
|
for (const f of protectedTargets()) unlockFile(f);
|
|
9038
9175
|
}
|
|
9039
9176
|
function globalPaths() {
|
|
9040
|
-
const home =
|
|
9041
|
-
const sgDir =
|
|
9042
|
-
const hooksDir =
|
|
9043
|
-
const claudeDir =
|
|
9177
|
+
const home = homedir4();
|
|
9178
|
+
const sgDir = join6(home, ".solongate");
|
|
9179
|
+
const hooksDir = join6(sgDir, "hooks");
|
|
9180
|
+
const claudeDir = join6(home, ".claude");
|
|
9044
9181
|
return {
|
|
9045
9182
|
home,
|
|
9046
9183
|
sgDir,
|
|
9047
9184
|
hooksDir,
|
|
9048
9185
|
claudeDir,
|
|
9049
|
-
settingsPath:
|
|
9050
|
-
backupPath:
|
|
9051
|
-
configPath:
|
|
9186
|
+
settingsPath: join6(claudeDir, "settings.json"),
|
|
9187
|
+
backupPath: join6(claudeDir, "settings.solongate.bak"),
|
|
9188
|
+
configPath: join6(sgDir, "cloud-guard.json")
|
|
9052
9189
|
};
|
|
9053
9190
|
}
|
|
9054
9191
|
function readHook(filename) {
|
|
9055
|
-
return readFileSync6(
|
|
9192
|
+
return readFileSync6(join6(HOOKS_DIR, filename), "utf-8");
|
|
9056
9193
|
}
|
|
9057
9194
|
function readGuard() {
|
|
9058
|
-
const bundled =
|
|
9195
|
+
const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
|
|
9059
9196
|
return existsSync4(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
|
|
9060
9197
|
}
|
|
9061
9198
|
function ask(question) {
|
|
@@ -9110,7 +9247,7 @@ function shimTargets() {
|
|
|
9110
9247
|
return [];
|
|
9111
9248
|
}
|
|
9112
9249
|
}
|
|
9113
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
9250
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join6(homedir4(), f)).filter((f) => existsSync4(f));
|
|
9114
9251
|
}
|
|
9115
9252
|
function writeShimBlock(file, block) {
|
|
9116
9253
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
@@ -9181,10 +9318,10 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9181
9318
|
mkdirSync3(p.hooksDir, { recursive: true });
|
|
9182
9319
|
mkdirSync3(p.claudeDir, { recursive: true });
|
|
9183
9320
|
unlockProtected();
|
|
9184
|
-
writeFileSync3(
|
|
9185
|
-
writeFileSync3(
|
|
9186
|
-
writeFileSync3(
|
|
9187
|
-
writeFileSync3(
|
|
9321
|
+
writeFileSync3(join6(p.hooksDir, "guard.mjs"), readGuard());
|
|
9322
|
+
writeFileSync3(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
9323
|
+
writeFileSync3(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
9324
|
+
writeFileSync3(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
9188
9325
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
9189
9326
|
removeClaudeShim();
|
|
9190
9327
|
writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
@@ -9202,9 +9339,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9202
9339
|
existing = {};
|
|
9203
9340
|
}
|
|
9204
9341
|
}
|
|
9205
|
-
const guardAbs =
|
|
9206
|
-
const auditAbs =
|
|
9207
|
-
const stopAbs =
|
|
9342
|
+
const guardAbs = join6(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
9343
|
+
const auditAbs = join6(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
9344
|
+
const stopAbs = join6(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
9208
9345
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
9209
9346
|
const merged = {
|
|
9210
9347
|
...existing,
|
|
@@ -9394,11 +9531,11 @@ import { createServer, request as httpRequest } from "http";
|
|
|
9394
9531
|
import { request as httpsRequest } from "https";
|
|
9395
9532
|
import { spawn as spawn2 } from "child_process";
|
|
9396
9533
|
import { URL as URL2 } from "url";
|
|
9397
|
-
import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync } from "fs";
|
|
9534
|
+
import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
|
|
9398
9535
|
import { resolve as resolve5 } from "path";
|
|
9399
|
-
import { homedir as
|
|
9536
|
+
import { homedir as homedir5 } from "os";
|
|
9400
9537
|
function findCacheFile() {
|
|
9401
|
-
const dir = resolve5(
|
|
9538
|
+
const dir = resolve5(homedir5(), ".solongate");
|
|
9402
9539
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
9403
9540
|
if (envSel) {
|
|
9404
9541
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -9409,7 +9546,7 @@ function findCacheFile() {
|
|
|
9409
9546
|
for (const name of readdirSync(dir)) {
|
|
9410
9547
|
if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
|
|
9411
9548
|
const full = resolve5(dir, name);
|
|
9412
|
-
const ts =
|
|
9549
|
+
const ts = statSync2(full).mtimeMs;
|
|
9413
9550
|
if (ts > bestTs) {
|
|
9414
9551
|
bestTs = ts;
|
|
9415
9552
|
best = full;
|
|
@@ -9700,9 +9837,9 @@ __export(logs_server_exports, {
|
|
|
9700
9837
|
runLogsServer: () => runLogsServer
|
|
9701
9838
|
});
|
|
9702
9839
|
import { createServer as createServer2 } from "http";
|
|
9703
|
-
import { readFileSync as readFileSync8, statSync as
|
|
9704
|
-
import { resolve as resolve6, join as
|
|
9705
|
-
import { homedir as
|
|
9840
|
+
import { readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
9841
|
+
import { resolve as resolve6, join as join7, isAbsolute } from "path";
|
|
9842
|
+
import { homedir as homedir6 } from "os";
|
|
9706
9843
|
import { readdirSync as readdirSync2 } from "fs";
|
|
9707
9844
|
function allowedOrigins() {
|
|
9708
9845
|
const base = [
|
|
@@ -9719,15 +9856,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
9719
9856
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
9720
9857
|
if (!dir) return null;
|
|
9721
9858
|
if (isAbsolute(dir)) return dir;
|
|
9722
|
-
return resolve6(
|
|
9859
|
+
return resolve6(homedir6(), ".solongate", "local-logs");
|
|
9723
9860
|
}
|
|
9724
9861
|
async function findLogDir() {
|
|
9725
|
-
const base = resolve6(
|
|
9862
|
+
const base = resolve6(homedir6(), ".solongate");
|
|
9726
9863
|
try {
|
|
9727
9864
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
9728
9865
|
for (const f of files) {
|
|
9729
9866
|
try {
|
|
9730
|
-
const c2 = JSON.parse(readFileSync8(
|
|
9867
|
+
const c2 = JSON.parse(readFileSync8(join7(base, f), "utf-8"));
|
|
9731
9868
|
const p = c2?.security?.localLogs?.path;
|
|
9732
9869
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
9733
9870
|
} catch {
|
|
@@ -9736,7 +9873,7 @@ async function findLogDir() {
|
|
|
9736
9873
|
} catch {
|
|
9737
9874
|
}
|
|
9738
9875
|
try {
|
|
9739
|
-
const cfgRaw = readFileSync8(
|
|
9876
|
+
const cfgRaw = readFileSync8(join7(base, "cloud-guard.json"), "utf-8");
|
|
9740
9877
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
9741
9878
|
if (apiKey) {
|
|
9742
9879
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -9765,9 +9902,9 @@ function setCors(req, res) {
|
|
|
9765
9902
|
}
|
|
9766
9903
|
function fileInfo(dir) {
|
|
9767
9904
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
9768
|
-
const file =
|
|
9905
|
+
const file = join7(dir, LOG_FILENAME);
|
|
9769
9906
|
try {
|
|
9770
|
-
const st =
|
|
9907
|
+
const st = statSync3(file);
|
|
9771
9908
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
9772
9909
|
} catch {
|
|
9773
9910
|
return { file, exists: false, size: 0, mtimeMs: 0 };
|
|
@@ -10216,7 +10353,7 @@ var init_inject = __esm({
|
|
|
10216
10353
|
// src/create.ts
|
|
10217
10354
|
var create_exports = {};
|
|
10218
10355
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
|
|
10219
|
-
import { resolve as resolve8, join as
|
|
10356
|
+
import { resolve as resolve8, join as join8 } from "path";
|
|
10220
10357
|
import { execSync as execSync2 } from "child_process";
|
|
10221
10358
|
function withSpinner(message, fn) {
|
|
10222
10359
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -10296,7 +10433,7 @@ EXAMPLES
|
|
|
10296
10433
|
}
|
|
10297
10434
|
function createProject(dir, name, _policy) {
|
|
10298
10435
|
writeFileSync5(
|
|
10299
|
-
|
|
10436
|
+
join8(dir, "package.json"),
|
|
10300
10437
|
JSON.stringify(
|
|
10301
10438
|
{
|
|
10302
10439
|
name,
|
|
@@ -10326,7 +10463,7 @@ function createProject(dir, name, _policy) {
|
|
|
10326
10463
|
) + "\n"
|
|
10327
10464
|
);
|
|
10328
10465
|
writeFileSync5(
|
|
10329
|
-
|
|
10466
|
+
join8(dir, "tsconfig.json"),
|
|
10330
10467
|
JSON.stringify(
|
|
10331
10468
|
{
|
|
10332
10469
|
compilerOptions: {
|
|
@@ -10346,9 +10483,9 @@ function createProject(dir, name, _policy) {
|
|
|
10346
10483
|
2
|
|
10347
10484
|
) + "\n"
|
|
10348
10485
|
);
|
|
10349
|
-
mkdirSync4(
|
|
10486
|
+
mkdirSync4(join8(dir, "src"), { recursive: true });
|
|
10350
10487
|
writeFileSync5(
|
|
10351
|
-
|
|
10488
|
+
join8(dir, "src", "index.ts"),
|
|
10352
10489
|
`#!/usr/bin/env node
|
|
10353
10490
|
|
|
10354
10491
|
console.log = (...args: unknown[]) => {
|
|
@@ -10390,7 +10527,7 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10390
10527
|
`
|
|
10391
10528
|
);
|
|
10392
10529
|
writeFileSync5(
|
|
10393
|
-
|
|
10530
|
+
join8(dir, ".mcp.json"),
|
|
10394
10531
|
JSON.stringify(
|
|
10395
10532
|
{
|
|
10396
10533
|
mcpServers: {
|
|
@@ -10408,12 +10545,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10408
10545
|
) + "\n"
|
|
10409
10546
|
);
|
|
10410
10547
|
writeFileSync5(
|
|
10411
|
-
|
|
10548
|
+
join8(dir, ".env"),
|
|
10412
10549
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
10413
10550
|
`
|
|
10414
10551
|
);
|
|
10415
10552
|
writeFileSync5(
|
|
10416
|
-
|
|
10553
|
+
join8(dir, ".gitignore"),
|
|
10417
10554
|
`node_modules/
|
|
10418
10555
|
dist/
|
|
10419
10556
|
*.solongate-backup
|
package/dist/tui/index.js
CHANGED
|
@@ -126,6 +126,9 @@ 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 { closeSync, openSync, readSync, statSync } from "fs";
|
|
130
|
+
import { homedir as homedir2 } from "os";
|
|
131
|
+
import { join as join2 } from "path";
|
|
129
132
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
130
133
|
|
|
131
134
|
// src/api-client/client.ts
|
|
@@ -433,6 +436,8 @@ function usePoll(reload, intervalMs, enabled = true) {
|
|
|
433
436
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
434
437
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
435
438
|
var BG = "#12234f";
|
|
439
|
+
var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
440
|
+
var RING = join2(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
436
441
|
var hhmmss = (ts) => {
|
|
437
442
|
const d = new Date(ts);
|
|
438
443
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -442,7 +447,34 @@ var fmtUp = (ms) => {
|
|
|
442
447
|
const p = (n) => String(n).padStart(2, "0");
|
|
443
448
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
444
449
|
};
|
|
445
|
-
function
|
|
450
|
+
function tailLines(file, maxBytes = 65536) {
|
|
451
|
+
try {
|
|
452
|
+
const size = statSync(file).size;
|
|
453
|
+
const start = Math.max(0, size - maxBytes);
|
|
454
|
+
const fd = openSync(file, "r");
|
|
455
|
+
const buf = Buffer.alloc(size - start);
|
|
456
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
457
|
+
closeSync(fd);
|
|
458
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
459
|
+
if (start > 0) lines.shift();
|
|
460
|
+
return lines;
|
|
461
|
+
} catch {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function cloudItem(e) {
|
|
466
|
+
return {
|
|
467
|
+
id: "c:" + e.id,
|
|
468
|
+
at: Date.parse(e.created_at),
|
|
469
|
+
tool: e.tool_name,
|
|
470
|
+
decision: e.decision,
|
|
471
|
+
permission: (e.permission ?? "").slice(0, 4),
|
|
472
|
+
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
473
|
+
dlp: !!e.dlp_matches?.length,
|
|
474
|
+
source: "cloud"
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function ColumnChart({ series, hot, height, width, color, hotColor = theme.bad }) {
|
|
446
478
|
const pad = Math.max(0, width - series.length);
|
|
447
479
|
const data = [...new Array(pad).fill(0), ...series.slice(-width)];
|
|
448
480
|
const hotPad = hot ? [...new Array(pad).fill(false), ...hot.slice(-width)] : void 0;
|
|
@@ -454,13 +486,11 @@ function ColumnChart({ series, hot, height, width, color }) {
|
|
|
454
486
|
const v = data[i];
|
|
455
487
|
const frac = v / max;
|
|
456
488
|
let ch;
|
|
457
|
-
let c2;
|
|
458
489
|
if (frac >= r / height) ch = "\u2588";
|
|
459
490
|
else if (frac >= (r - 0.5) / height) ch = "\u2584";
|
|
460
491
|
else if (r === 1) ch = "\u2581";
|
|
461
492
|
else ch = " ";
|
|
462
|
-
|
|
463
|
-
else c2 = hotPad?.[i] ? theme.bad : color;
|
|
493
|
+
const c2 = r === 1 && v === 0 ? "#233457" : hotPad?.[i] ? hotColor : color;
|
|
464
494
|
const last = segs[segs.length - 1];
|
|
465
495
|
if (last && last.color === c2) last.text += ch;
|
|
466
496
|
else segs.push({ text: ch, color: c2 });
|
|
@@ -496,60 +526,147 @@ function LivePanel({ active: active2 }) {
|
|
|
496
526
|
const [s, setS] = useState2(null);
|
|
497
527
|
const [tsData, setTsData] = useState2(null);
|
|
498
528
|
const [lat, setLat] = useState2([]);
|
|
499
|
-
const [
|
|
529
|
+
const [cloudBuf, setCloudBuf] = useState2([]);
|
|
530
|
+
const [localBuf, setLocalBuf] = useState2([]);
|
|
531
|
+
const [localOn, setLocalOn] = useState2(null);
|
|
532
|
+
const [ring, setRing] = useState2(null);
|
|
500
533
|
const [log, setLog] = useState2([]);
|
|
501
534
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
535
|
+
const lastLocalTs = useRef2(0);
|
|
536
|
+
const pausedUntil = useRef2(0);
|
|
502
537
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
503
538
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
504
539
|
}, []);
|
|
505
|
-
const
|
|
540
|
+
const onApiError = useCallback2(
|
|
541
|
+
(e) => {
|
|
542
|
+
const is429 = e instanceof ApiError && e.status === 429;
|
|
543
|
+
if (is429 && Date.now() >= pausedUntil.current) {
|
|
544
|
+
pausedUntil.current = Date.now() + 3e4;
|
|
545
|
+
pushLog("rate limited by api \xB7 backing off 30s", "warn");
|
|
546
|
+
} else if (!is429) {
|
|
547
|
+
pushLog(`api error \xB7 ${truncate(e instanceof Error ? e.message : String(e), 40)}`, "bad");
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
[pushLog]
|
|
551
|
+
);
|
|
552
|
+
const paused = () => Date.now() < pausedUntil.current;
|
|
553
|
+
const pollLocal = useCallback2(() => {
|
|
554
|
+
const lines = tailLines(LOCAL_LOG);
|
|
555
|
+
if (!lines.length) {
|
|
556
|
+
setLocalOn((prev) => prev === null ? false : prev);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
setLocalOn(true);
|
|
560
|
+
const fresh = [];
|
|
561
|
+
for (const line of lines) {
|
|
562
|
+
try {
|
|
563
|
+
const j = JSON.parse(line);
|
|
564
|
+
const at = Date.parse(j.ts ?? "");
|
|
565
|
+
if (!Number.isFinite(at) || at <= lastLocalTs.current) continue;
|
|
566
|
+
fresh.push({
|
|
567
|
+
id: "l:" + at + ":" + (j.tool ?? ""),
|
|
568
|
+
at,
|
|
569
|
+
tool: j.tool ?? "?",
|
|
570
|
+
decision: j.decision ?? "ALLOW",
|
|
571
|
+
permission: "",
|
|
572
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
573
|
+
dlp: !!j.dlp,
|
|
574
|
+
source: "local"
|
|
575
|
+
});
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (fresh.length) {
|
|
580
|
+
lastLocalTs.current = fresh[fresh.length - 1].at;
|
|
581
|
+
setLocalBuf((prev) => [...prev, ...fresh].slice(-400));
|
|
582
|
+
const denies = fresh.filter((f) => f.decision !== "ALLOW").length;
|
|
583
|
+
if (lastLocalTs.current !== fresh[0].at || fresh.length < 10) {
|
|
584
|
+
pushLog(`local +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : "warn");
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const ringLines = tailLines(RING, 8192).slice(-30);
|
|
588
|
+
if (ringLines.length) {
|
|
589
|
+
let sum = 0;
|
|
590
|
+
let n = 0;
|
|
591
|
+
let session = "";
|
|
592
|
+
for (const rl2 of ringLines) {
|
|
593
|
+
try {
|
|
594
|
+
const j = JSON.parse(rl2);
|
|
595
|
+
if (typeof j.ms === "number") {
|
|
596
|
+
sum += j.ms;
|
|
597
|
+
n++;
|
|
598
|
+
}
|
|
599
|
+
if (j.session) session = j.session;
|
|
600
|
+
} catch {
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (n) setRing({ avgMs: Math.round(sum / n), session, count: n });
|
|
604
|
+
}
|
|
605
|
+
}, [pushLog]);
|
|
606
|
+
useEffect2(() => {
|
|
607
|
+
if (!active2) return;
|
|
608
|
+
pollLocal();
|
|
609
|
+
const t = setInterval(pollLocal, 2e3);
|
|
610
|
+
return () => clearInterval(t);
|
|
611
|
+
}, [active2, pollLocal]);
|
|
612
|
+
const pollFeed = useCallback2(async () => {
|
|
613
|
+
if (paused()) return;
|
|
506
614
|
const t0 = Date.now();
|
|
507
615
|
try {
|
|
508
|
-
const
|
|
616
|
+
const fd = await api.audit.list({ limit: 50 });
|
|
509
617
|
const ms = Date.now() - t0;
|
|
510
|
-
|
|
511
|
-
setTsData(td);
|
|
512
|
-
setLat((l) => [...l.slice(-47), ms]);
|
|
618
|
+
setLat((l) => [...l, ms].slice(-240));
|
|
513
619
|
const fresh = fd.entries.filter((e) => !seenRef.current.has(e.id));
|
|
620
|
+
const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
|
|
514
621
|
for (const e of fresh) seenRef.current.add(e.id);
|
|
515
|
-
const firstLoad = seenRef.current.size === fresh.length && fresh.length > 1;
|
|
516
622
|
if (fresh.length) {
|
|
517
|
-
|
|
623
|
+
setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
518
624
|
}
|
|
519
625
|
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
520
|
-
|
|
521
|
-
|
|
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
|
-
);
|
|
626
|
+
if (firstLoad) pushLog(`cloud link up \xB7 api ${ms}ms \xB7 ${fresh.length} calls`);
|
|
627
|
+
else pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : fresh.length ? "warn" : "ok");
|
|
527
628
|
} catch (e) {
|
|
528
|
-
|
|
629
|
+
onApiError(e);
|
|
529
630
|
}
|
|
530
|
-
}, [pushLog]);
|
|
631
|
+
}, [pushLog, onApiError]);
|
|
632
|
+
const pollMedium = useCallback2(async () => {
|
|
633
|
+
if (paused()) return;
|
|
634
|
+
try {
|
|
635
|
+
setS(await api.stats.get());
|
|
636
|
+
} catch (e) {
|
|
637
|
+
onApiError(e);
|
|
638
|
+
}
|
|
639
|
+
}, [onApiError]);
|
|
640
|
+
const pollSlow = useCallback2(async () => {
|
|
641
|
+
if (paused()) return;
|
|
642
|
+
try {
|
|
643
|
+
setTsData(await api.stats.timeseries({ period: "24h" }));
|
|
644
|
+
} catch (e) {
|
|
645
|
+
onApiError(e);
|
|
646
|
+
}
|
|
647
|
+
}, [onApiError]);
|
|
531
648
|
useEffect2(() => {
|
|
532
649
|
if (!active2) return;
|
|
533
|
-
void
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
650
|
+
void pollFeed();
|
|
651
|
+
void pollMedium();
|
|
652
|
+
void pollSlow();
|
|
653
|
+
const t1 = setInterval(() => void pollFeed(), 4e3);
|
|
654
|
+
const t2 = setInterval(() => void pollMedium(), 1e4);
|
|
655
|
+
const t3 = setInterval(() => void pollSlow(), 3e4);
|
|
656
|
+
return () => {
|
|
657
|
+
clearInterval(t1);
|
|
658
|
+
clearInterval(t2);
|
|
659
|
+
clearInterval(t3);
|
|
660
|
+
};
|
|
661
|
+
}, [active2, pollFeed, pollMedium, pollSlow]);
|
|
537
662
|
const sessions = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
|
|
538
663
|
const insights = useLoader(() => api.stats.securityInsights(7));
|
|
539
|
-
usePoll(
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (
|
|
544
|
-
},
|
|
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]);
|
|
664
|
+
usePoll(() => {
|
|
665
|
+
if (!paused()) sessions.reload();
|
|
666
|
+
}, 1e4, active2);
|
|
667
|
+
usePoll(() => {
|
|
668
|
+
if (!paused()) insights.reload();
|
|
669
|
+
}, 3e4, active2);
|
|
553
670
|
const [tick, setTick] = useState2(0);
|
|
554
671
|
useEffect2(() => {
|
|
555
672
|
if (!active2) return;
|
|
@@ -569,29 +686,33 @@ function LivePanel({ active: active2 }) {
|
|
|
569
686
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
570
687
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
571
688
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
689
|
+
const merged = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
|
|
690
|
+
const denials = merged.filter((e) => e.decision !== "ALLOW");
|
|
691
|
+
const lastDeny = denials[denials.length - 1];
|
|
572
692
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
573
|
-
for (const e of
|
|
693
|
+
for (const e of merged) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
574
694
|
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
575
|
-
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
576
|
-
const lastDeny = denials[denials.length - 1];
|
|
577
695
|
const latNow = lat[lat.length - 1] ?? 0;
|
|
578
|
-
const
|
|
696
|
+
const sortedLat = [...lat].sort((a, b) => a - b);
|
|
697
|
+
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
698
|
+
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
699
|
+
const backingOff = Date.now() < pausedUntil.current;
|
|
579
700
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
580
701
|
const sessCounts = sessions.data?.counts;
|
|
581
702
|
const chartH = rows >= 36 ? 6 : 4;
|
|
582
703
|
const colH = rows >= 32 ? 6 : 5;
|
|
583
|
-
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
584
|
-
const tail =
|
|
704
|
+
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
705
|
+
const tail = merged.slice(-streamRows);
|
|
585
706
|
const innerW = cols - 2;
|
|
586
707
|
const leftW = Math.floor(innerW * 0.55);
|
|
587
708
|
const rightW = innerW - leftW - 2;
|
|
588
709
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
589
|
-
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\
|
|
710
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
590
711
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
591
712
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
592
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: "
|
|
713
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
593
714
|
/* @__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.
|
|
715
|
+
backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
|
|
595
716
|
] }),
|
|
596
717
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
597
718
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -600,14 +721,14 @@ function LivePanel({ active: active2 }) {
|
|
|
600
721
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
601
722
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
602
723
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
724
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 local " }),
|
|
725
|
+
localOn ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "\u2713 streaming" }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "off" }),
|
|
603
726
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
604
727
|
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
605
728
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
606
729
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
607
730
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
|
|
608
731
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC ${sessCounts.deactivated}\u25CB` : "\xB7" }),
|
|
609
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
|
|
610
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
|
|
611
732
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
612
733
|
] }),
|
|
613
734
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -616,8 +737,8 @@ function LivePanel({ active: active2 }) {
|
|
|
616
737
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
617
738
|
] }),
|
|
618
739
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
619
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `
|
|
620
|
-
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v >
|
|
740
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
|
|
741
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0", hotColor: "#ffb454" })
|
|
621
742
|
] })
|
|
622
743
|
] }),
|
|
623
744
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -637,12 +758,22 @@ function LivePanel({ active: active2 }) {
|
|
|
637
758
|
" patterns armed"
|
|
638
759
|
] })
|
|
639
760
|
] }),
|
|
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" })
|
|
761
|
+
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" }),
|
|
762
|
+
ring ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
763
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
764
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local \u2713" }),
|
|
765
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` eval avg ${ring.avgMs}ms \xB7 ${ring.count} recent` })
|
|
766
|
+
] }) : null
|
|
641
767
|
] }),
|
|
642
768
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
643
769
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} live` : "", width: colW }),
|
|
644
|
-
|
|
645
|
-
|
|
770
|
+
ring?.session ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
771
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "\u25CF " }),
|
|
772
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: true, children: "this machine" }),
|
|
773
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + truncate(ring.session, 12) + " \xB7 guarded" })
|
|
774
|
+
] }) : null,
|
|
775
|
+
sess.length === 0 && !ring ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
776
|
+
sess.slice(0, colH - 1 - (ring ? 1 : 0)).map((a) => {
|
|
646
777
|
const dot = sessionDot(a.status);
|
|
647
778
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
648
779
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
@@ -674,7 +805,11 @@ function LivePanel({ active: active2 }) {
|
|
|
674
805
|
] }, l.ts + ":" + i))
|
|
675
806
|
] })
|
|
676
807
|
] }),
|
|
677
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `tail
|
|
808
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `local+cloud tail \xB7 ${merged.length} buffered`, width: innerW }),
|
|
809
|
+
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
810
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
811
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable in dashboard \u2192 Settings \u2192 Local logs; hooks then write ~/.solongate/local-logs (view them there with `solongate logs-server`)" })
|
|
812
|
+
] }) : null,
|
|
678
813
|
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
679
814
|
spin,
|
|
680
815
|
" awaiting traffic\u2026"
|
|
@@ -682,18 +817,20 @@ function LivePanel({ active: active2 }) {
|
|
|
682
817
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
683
818
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
684
819
|
"[",
|
|
685
|
-
hhmmss(e.
|
|
686
|
-
"
|
|
820
|
+
hhmmss(e.at),
|
|
821
|
+
" "
|
|
687
822
|
] }),
|
|
823
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
824
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
688
825
|
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
689
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.
|
|
690
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
691
|
-
e.
|
|
692
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
826
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 14).padEnd(15) }),
|
|
827
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
828
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
829
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
693
830
|
] }, e.id)),
|
|
694
831
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
695
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
696
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${
|
|
832
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
833
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${merged.length} ` }),
|
|
697
834
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
698
835
|
] })
|
|
699
836
|
] });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.67.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": {
|