@solongate/proxy 0.66.1 → 0.68.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 +262 -115
- package/dist/tui/index.js +217 -70
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7019,10 +7019,40 @@ var init_hooks = __esm({
|
|
|
7019
7019
|
});
|
|
7020
7020
|
|
|
7021
7021
|
// src/tui/panels/Live.tsx
|
|
7022
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
7022
|
+
import { Box as Box2, Text as Text2, useInput } 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,10 +7104,23 @@ 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);
|
|
7082
7114
|
const pausedUntil = useRef2(0);
|
|
7115
|
+
const [frozen, setFrozen] = useState2(false);
|
|
7116
|
+
const frozenRef = useRef2(false);
|
|
7117
|
+
frozenRef.current = frozen;
|
|
7118
|
+
useInput(
|
|
7119
|
+
(input) => {
|
|
7120
|
+
if (input === "p") setFrozen((f) => !f);
|
|
7121
|
+
},
|
|
7122
|
+
{ isActive: active2 }
|
|
7123
|
+
);
|
|
7083
7124
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7084
7125
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7085
7126
|
}, []);
|
|
@@ -7095,7 +7136,69 @@ function LivePanel({ active: active2 }) {
|
|
|
7095
7136
|
},
|
|
7096
7137
|
[pushLog]
|
|
7097
7138
|
);
|
|
7098
|
-
const paused = () => Date.now() < pausedUntil.current;
|
|
7139
|
+
const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
|
|
7140
|
+
const pollLocal = useCallback2(() => {
|
|
7141
|
+
if (frozenRef.current) return;
|
|
7142
|
+
const lines = tailLines(LOCAL_LOG);
|
|
7143
|
+
if (!lines.length) {
|
|
7144
|
+
setLocalOn((prev) => prev === null ? false : prev);
|
|
7145
|
+
return;
|
|
7146
|
+
}
|
|
7147
|
+
setLocalOn(true);
|
|
7148
|
+
const fresh = [];
|
|
7149
|
+
for (const line of lines) {
|
|
7150
|
+
try {
|
|
7151
|
+
const j = JSON.parse(line);
|
|
7152
|
+
const at = Date.parse(j.ts ?? "");
|
|
7153
|
+
if (!Number.isFinite(at) || at <= lastLocalTs.current) continue;
|
|
7154
|
+
fresh.push({
|
|
7155
|
+
id: "l:" + at + ":" + (j.tool ?? ""),
|
|
7156
|
+
at,
|
|
7157
|
+
tool: j.tool ?? "?",
|
|
7158
|
+
decision: j.decision ?? "ALLOW",
|
|
7159
|
+
permission: (j.permission ?? "").slice(0, 4),
|
|
7160
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
7161
|
+
dlp: !!j.dlp,
|
|
7162
|
+
source: "local",
|
|
7163
|
+
session: j.session_id,
|
|
7164
|
+
agent: j.agent_name
|
|
7165
|
+
});
|
|
7166
|
+
} catch {
|
|
7167
|
+
}
|
|
7168
|
+
}
|
|
7169
|
+
if (fresh.length) {
|
|
7170
|
+
lastLocalTs.current = fresh[fresh.length - 1].at;
|
|
7171
|
+
setLocalBuf((prev) => [...prev, ...fresh].slice(-400));
|
|
7172
|
+
const denies = fresh.filter((f) => f.decision !== "ALLOW").length;
|
|
7173
|
+
if (lastLocalTs.current !== fresh[0].at || fresh.length < 10) {
|
|
7174
|
+
pushLog(`local +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : "warn");
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
const ringLines = tailLines(RING, 8192).slice(-30);
|
|
7178
|
+
if (ringLines.length) {
|
|
7179
|
+
let sum = 0;
|
|
7180
|
+
let n = 0;
|
|
7181
|
+
let session = "";
|
|
7182
|
+
for (const rl2 of ringLines) {
|
|
7183
|
+
try {
|
|
7184
|
+
const j = JSON.parse(rl2);
|
|
7185
|
+
if (typeof j.ms === "number") {
|
|
7186
|
+
sum += j.ms;
|
|
7187
|
+
n++;
|
|
7188
|
+
}
|
|
7189
|
+
if (j.session) session = j.session;
|
|
7190
|
+
} catch {
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
if (n) setRing({ avgMs: Math.round(sum / n), session, count: n });
|
|
7194
|
+
}
|
|
7195
|
+
}, [pushLog]);
|
|
7196
|
+
useEffect2(() => {
|
|
7197
|
+
if (!active2) return;
|
|
7198
|
+
pollLocal();
|
|
7199
|
+
const t = setInterval(pollLocal, 2e3);
|
|
7200
|
+
return () => clearInterval(t);
|
|
7201
|
+
}, [active2, pollLocal]);
|
|
7099
7202
|
const pollFeed = useCallback2(async () => {
|
|
7100
7203
|
if (paused()) return;
|
|
7101
7204
|
const t0 = Date.now();
|
|
@@ -7107,16 +7210,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7107
7210
|
const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
|
|
7108
7211
|
for (const e of fresh) seenRef.current.add(e.id);
|
|
7109
7212
|
if (fresh.length) {
|
|
7110
|
-
|
|
7213
|
+
setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
7111
7214
|
}
|
|
7112
7215
|
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
else
|
|
7116
|
-
pushLog(
|
|
7117
|
-
`api ${ms}ms \xB7 +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}${dlp ? ` \xB7 ${dlp} DLP` : ""}`,
|
|
7118
|
-
denies || dlp ? "bad" : fresh.length ? "warn" : "ok"
|
|
7119
|
-
);
|
|
7216
|
+
if (firstLoad) pushLog(`cloud link up \xB7 api ${ms}ms \xB7 ${fresh.length} calls`);
|
|
7217
|
+
else pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : fresh.length ? "warn" : "ok");
|
|
7120
7218
|
} catch (e) {
|
|
7121
7219
|
onApiError(e);
|
|
7122
7220
|
}
|
|
@@ -7159,62 +7257,76 @@ function LivePanel({ active: active2 }) {
|
|
|
7159
7257
|
usePoll(() => {
|
|
7160
7258
|
if (!paused()) insights.reload();
|
|
7161
7259
|
}, 3e4, active2);
|
|
7162
|
-
useEffect2(() => {
|
|
7163
|
-
const c2 = sessions.data?.counts;
|
|
7164
|
-
if (c2) pushLog(`sessions ${c2.active}\u25B2 ${c2.idle}\u25CC ${c2.deactivated}\u25CB`);
|
|
7165
|
-
}, [sessions.data, pushLog]);
|
|
7166
|
-
useEffect2(() => {
|
|
7167
|
-
const ib = insights.data;
|
|
7168
|
-
if (ib?.layers) {
|
|
7169
|
-
const rl2 = ib.layers.rateLimit;
|
|
7170
|
-
const dl2 = ib.layers.dlp;
|
|
7171
|
-
pushLog(`layers \xB7 ratelimit ${rl2?.mode ?? "?"} ${rl2?.perMinute || "\u2014"}/m \xB7 dlp ${dl2?.mode ?? "?"} ${(dl2?.patterns ?? []).length} armed`);
|
|
7172
|
-
}
|
|
7173
|
-
}, [insights.data, pushLog]);
|
|
7174
7260
|
const [tick, setTick] = useState2(0);
|
|
7175
7261
|
useEffect2(() => {
|
|
7176
|
-
if (!active2) return;
|
|
7262
|
+
if (!active2 || frozen) return;
|
|
7177
7263
|
const t = setInterval(() => setTick((n) => n + 1), 200);
|
|
7178
7264
|
return () => clearInterval(t);
|
|
7179
|
-
}, [active2]);
|
|
7265
|
+
}, [active2, frozen]);
|
|
7180
7266
|
const startRef = useRef2(Date.now());
|
|
7181
7267
|
const cols = process.stdout.columns ?? 100;
|
|
7182
7268
|
const rows = process.stdout.rows ?? 30;
|
|
7183
7269
|
const ins = insights.data ?? {};
|
|
7184
7270
|
const spin = SPIN[tick % SPIN.length];
|
|
7185
7271
|
const points = tsData?.timeseries ?? [];
|
|
7186
|
-
const
|
|
7187
|
-
const
|
|
7272
|
+
const mergedAll = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
|
|
7273
|
+
const nowMs = Date.now();
|
|
7274
|
+
const minuteCounts = new Array(60).fill(0);
|
|
7275
|
+
const minuteHot = new Array(60).fill(false);
|
|
7276
|
+
for (const e of mergedAll) {
|
|
7277
|
+
const idx = 59 - Math.floor((nowMs - e.at) / 6e4);
|
|
7278
|
+
if (idx >= 0 && idx < 60) {
|
|
7279
|
+
minuteCounts[idx]++;
|
|
7280
|
+
if (e.decision !== "ALLOW") minuteHot[idx] = true;
|
|
7281
|
+
}
|
|
7282
|
+
}
|
|
7283
|
+
const localTraffic = localOn === true && minuteCounts.some((c2) => c2 > 0);
|
|
7284
|
+
const traffic = localTraffic ? minuteCounts : points.map((p) => p.total);
|
|
7285
|
+
const trafficHot = localTraffic ? minuteHot : points.map((p) => p.denied > 0);
|
|
7286
|
+
const trafficLabel = localTraffic ? "calls/min \xB7 60m \xB7 local+cloud" : "24h \xB7 cloud";
|
|
7188
7287
|
const rl = ins.layers?.rateLimit;
|
|
7189
7288
|
const dl = ins.layers?.dlp;
|
|
7190
7289
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
7191
7290
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
7192
7291
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
7292
|
+
const merged = mergedAll;
|
|
7293
|
+
const denials = merged.filter((e) => e.decision !== "ALLOW");
|
|
7294
|
+
const lastDeny = denials[denials.length - 1];
|
|
7193
7295
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
7194
|
-
for (const e of
|
|
7296
|
+
for (const e of merged) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
7195
7297
|
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
7196
|
-
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
7197
|
-
const lastDeny = denials[denials.length - 1];
|
|
7198
7298
|
const latNow = lat[lat.length - 1] ?? 0;
|
|
7199
|
-
const
|
|
7200
|
-
const
|
|
7299
|
+
const sortedLat = [...lat].sort((a, b) => a - b);
|
|
7300
|
+
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
7301
|
+
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
7201
7302
|
const backingOff = Date.now() < pausedUntil.current;
|
|
7202
7303
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
7203
7304
|
const sessCounts = sessions.data?.counts;
|
|
7305
|
+
const localSess = /* @__PURE__ */ new Map();
|
|
7306
|
+
for (const e of localBuf) {
|
|
7307
|
+
if (!e.session) continue;
|
|
7308
|
+
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, lastAt: 0 };
|
|
7309
|
+
cur.calls++;
|
|
7310
|
+
if (e.decision !== "ALLOW") cur.denies++;
|
|
7311
|
+
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
7312
|
+
if (e.agent) cur.agent = e.agent;
|
|
7313
|
+
localSess.set(e.session, cur);
|
|
7314
|
+
}
|
|
7315
|
+
const localSessList = [...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt);
|
|
7204
7316
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7205
7317
|
const colH = rows >= 32 ? 6 : 5;
|
|
7206
|
-
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
7207
|
-
const tail =
|
|
7318
|
+
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
7319
|
+
const tail = merged.slice(-streamRows);
|
|
7208
7320
|
const innerW = cols - 2;
|
|
7209
7321
|
const leftW = Math.floor(innerW * 0.55);
|
|
7210
7322
|
const rightW = innerW - leftW - 2;
|
|
7211
7323
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7212
|
-
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\
|
|
7324
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
7213
7325
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7214
7326
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7215
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: "
|
|
7327
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7216
7328
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
7217
|
-
|
|
7329
|
+
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " PAUSED \xB7 copy freely \xB7 p resume " }) : 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 " })
|
|
7218
7330
|
] }),
|
|
7219
7331
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7220
7332
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -7223,24 +7335,24 @@ function LivePanel({ active: active2 }) {
|
|
|
7223
7335
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
7224
7336
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7225
7337
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
7338
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 local " }),
|
|
7339
|
+
localOn ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "\u2713 streaming" }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "off" }),
|
|
7226
7340
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
7227
7341
|
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
7228
7342
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
7229
7343
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
7230
7344
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
|
|
7231
7345
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC ${sessCounts.deactivated}\u25CB` : "\xB7" }),
|
|
7232
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
|
|
7233
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
|
|
7234
7346
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7235
7347
|
] }),
|
|
7236
7348
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7237
7349
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
7238
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra:
|
|
7350
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
|
|
7239
7351
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
7240
7352
|
] }),
|
|
7241
7353
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
7242
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `
|
|
7243
|
-
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0" })
|
|
7354
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
|
|
7355
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0", hotColor: "#ffb454" })
|
|
7244
7356
|
] })
|
|
7245
7357
|
] }),
|
|
7246
7358
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -7260,18 +7372,45 @@ function LivePanel({ active: active2 }) {
|
|
|
7260
7372
|
" patterns armed"
|
|
7261
7373
|
] })
|
|
7262
7374
|
] }),
|
|
7263
|
-
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
|
|
7375
|
+
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: " no dlp hits in last 7 days" }),
|
|
7376
|
+
ring ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7377
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
7378
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local \u2713" }),
|
|
7379
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` eval avg ${ring.avgMs}ms \xB7 ${ring.count} recent` })
|
|
7380
|
+
] }) : null
|
|
7264
7381
|
] }),
|
|
7265
7382
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7266
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra:
|
|
7267
|
-
sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7268
|
-
|
|
7383
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: `${localSessList.length} local \xB7 ${sessCounts?.active ?? 0} cloud live`, width: colW }),
|
|
7384
|
+
localSessList.length === 0 && sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
7385
|
+
localSessList.slice(0, Math.max(1, colH - 2)).map(([id, v]) => {
|
|
7386
|
+
const fresh = nowMs - v.lastAt < 9e4;
|
|
7387
|
+
const isMe = ring?.session === id;
|
|
7388
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7389
|
+
/* @__PURE__ */ jsxs2(Text2, { color: fresh ? theme.ok : theme.dim, children: [
|
|
7390
|
+
fresh ? "\u25CF" : "\u25CB",
|
|
7391
|
+
" "
|
|
7392
|
+
] }),
|
|
7393
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "LOC " }),
|
|
7394
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: isMe, children: truncate2(isMe ? "this machine" : v.agent, 12).padEnd(13) }),
|
|
7395
|
+
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
7396
|
+
String(v.calls).padStart(4),
|
|
7397
|
+
"c "
|
|
7398
|
+
] }),
|
|
7399
|
+
/* @__PURE__ */ jsxs2(Text2, { color: v.denies ? theme.bad : theme.dim, children: [
|
|
7400
|
+
String(v.denies).padStart(3),
|
|
7401
|
+
"d "
|
|
7402
|
+
] }),
|
|
7403
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(v.lastAt) })
|
|
7404
|
+
] }, id);
|
|
7405
|
+
}),
|
|
7406
|
+
sess.slice(0, Math.max(0, colH - 1 - Math.min(localSessList.length, Math.max(1, colH - 2)))).map((a) => {
|
|
7269
7407
|
const dot = sessionDot(a.status);
|
|
7270
7408
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7271
7409
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
7272
7410
|
dot.ch,
|
|
7273
7411
|
" "
|
|
7274
7412
|
] }),
|
|
7413
|
+
/* @__PURE__ */ jsx2(Text2, { color: "#4f6db8", children: "CLD " }),
|
|
7275
7414
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(a.agent_name ?? a.session_id, 12).padEnd(13) }),
|
|
7276
7415
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
7277
7416
|
String(a.total_calls).padStart(4),
|
|
@@ -7297,7 +7436,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7297
7436
|
] }, l.ts + ":" + i))
|
|
7298
7437
|
] })
|
|
7299
7438
|
] }),
|
|
7300
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `tail
|
|
7439
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `local+cloud tail \xB7 ${merged.length} buffered`, width: innerW }),
|
|
7440
|
+
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7441
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
7442
|
+
/* @__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`)" })
|
|
7443
|
+
] }) : null,
|
|
7301
7444
|
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7302
7445
|
spin,
|
|
7303
7446
|
" awaiting traffic\u2026"
|
|
@@ -7305,23 +7448,25 @@ function LivePanel({ active: active2 }) {
|
|
|
7305
7448
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7306
7449
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7307
7450
|
"[",
|
|
7308
|
-
hhmmss(e.
|
|
7309
|
-
"
|
|
7451
|
+
hhmmss(e.at),
|
|
7452
|
+
" "
|
|
7310
7453
|
] }),
|
|
7454
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
7455
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
7311
7456
|
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
7312
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.
|
|
7313
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
7314
|
-
e.
|
|
7315
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
7457
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(e.tool, 14).padEnd(15) }),
|
|
7458
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
7459
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
7460
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
7316
7461
|
] }, e.id)),
|
|
7317
7462
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7318
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
7319
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${
|
|
7320
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
7463
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7464
|
+
/* @__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} ` }),
|
|
7465
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " p pause/copy \xB7 esc menu \xB7 q quit " })
|
|
7321
7466
|
] })
|
|
7322
7467
|
] });
|
|
7323
7468
|
}
|
|
7324
|
-
var SPIN, BG, hhmmss, fmtUp;
|
|
7469
|
+
var SPIN, BG, LOCAL_LOG, RING, hhmmss, fmtUp;
|
|
7325
7470
|
var init_Live = __esm({
|
|
7326
7471
|
"src/tui/panels/Live.tsx"() {
|
|
7327
7472
|
"use strict";
|
|
@@ -7330,6 +7475,8 @@ var init_Live = __esm({
|
|
|
7330
7475
|
init_theme();
|
|
7331
7476
|
SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7332
7477
|
BG = "#12234f";
|
|
7478
|
+
LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
7479
|
+
RING = join5(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
7333
7480
|
hhmmss = (ts) => {
|
|
7334
7481
|
const d = new Date(ts);
|
|
7335
7482
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -7343,7 +7490,7 @@ var init_Live = __esm({
|
|
|
7343
7490
|
});
|
|
7344
7491
|
|
|
7345
7492
|
// src/tui/panels/Policies.tsx
|
|
7346
|
-
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
7493
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
7347
7494
|
import TextInput from "ink-text-input";
|
|
7348
7495
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
7349
7496
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -7413,7 +7560,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7413
7560
|
setDirty(false);
|
|
7414
7561
|
setStatus("discarded");
|
|
7415
7562
|
};
|
|
7416
|
-
|
|
7563
|
+
useInput2(
|
|
7417
7564
|
(input, key) => {
|
|
7418
7565
|
if (view === "list") {
|
|
7419
7566
|
if (key.upArrow) setPi((n) => Math.max(0, n - 1));
|
|
@@ -7588,7 +7735,7 @@ var init_Policies = __esm({
|
|
|
7588
7735
|
});
|
|
7589
7736
|
|
|
7590
7737
|
// src/tui/panels/RateLimit.tsx
|
|
7591
|
-
import { Box as Box4, Text as Text4, useInput as
|
|
7738
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
7592
7739
|
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
7593
7740
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
7594
7741
|
function ColoredSpark({ values, limit, width: width2 = 60 }) {
|
|
@@ -7636,7 +7783,7 @@ function RateLimitPanel({ focused }) {
|
|
|
7636
7783
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7637
7784
|
}
|
|
7638
7785
|
};
|
|
7639
|
-
|
|
7786
|
+
useInput3(
|
|
7640
7787
|
(input, key) => {
|
|
7641
7788
|
const step = key.shift ? 10 : 1;
|
|
7642
7789
|
if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
|
|
@@ -7728,7 +7875,7 @@ var init_RateLimit = __esm({
|
|
|
7728
7875
|
});
|
|
7729
7876
|
|
|
7730
7877
|
// src/tui/panels/Dlp.tsx
|
|
7731
|
-
import { Box as Box5, Text as Text5, useInput as
|
|
7878
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
7732
7879
|
import TextInput2 from "ink-text-input";
|
|
7733
7880
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
7734
7881
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
@@ -7772,7 +7919,7 @@ function DlpPanel({ focused }) {
|
|
|
7772
7919
|
};
|
|
7773
7920
|
const customCount = dlp?.custom.length ?? 0;
|
|
7774
7921
|
const total = available.length + customCount;
|
|
7775
|
-
|
|
7922
|
+
useInput4(
|
|
7776
7923
|
(input, key) => {
|
|
7777
7924
|
if (!dlp) return;
|
|
7778
7925
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -7954,7 +8101,7 @@ var init_Stats = __esm({
|
|
|
7954
8101
|
});
|
|
7955
8102
|
|
|
7956
8103
|
// src/tui/panels/Audit.tsx
|
|
7957
|
-
import { Box as Box7, Text as Text7, useInput as
|
|
8104
|
+
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
7958
8105
|
import TextInput3 from "ink-text-input";
|
|
7959
8106
|
import { useState as useState6 } from "react";
|
|
7960
8107
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -7983,7 +8130,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
7983
8130
|
() => view === "detail" && current?.session_id ? api.audit.list({ session_id: current.session_id, limit: 40 }) : Promise.resolve(null),
|
|
7984
8131
|
[view, current?.session_id]
|
|
7985
8132
|
);
|
|
7986
|
-
|
|
8133
|
+
useInput5(
|
|
7987
8134
|
(input, key) => {
|
|
7988
8135
|
if (view === "detail") {
|
|
7989
8136
|
if (key.leftArrow || key.escape) setView("list");
|
|
@@ -8133,7 +8280,7 @@ var init_Audit = __esm({
|
|
|
8133
8280
|
});
|
|
8134
8281
|
|
|
8135
8282
|
// src/tui/App.tsx
|
|
8136
|
-
import { Box as Box8, Text as Text8, useApp, useInput as
|
|
8283
|
+
import { Box as Box8, Text as Text8, useApp, useInput as useInput6 } from "ink";
|
|
8137
8284
|
import { useState as useState7 } from "react";
|
|
8138
8285
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8139
8286
|
function LiveHint() {
|
|
@@ -8165,7 +8312,7 @@ function App() {
|
|
|
8165
8312
|
const { exit } = useApp();
|
|
8166
8313
|
const [section, setSection] = useState7(0);
|
|
8167
8314
|
const [focus, setFocus] = useState7("nav");
|
|
8168
|
-
|
|
8315
|
+
useInput6((input, key) => {
|
|
8169
8316
|
if (focus === "nav") {
|
|
8170
8317
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
8171
8318
|
else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
|
|
@@ -9001,8 +9148,8 @@ __export(global_install_exports, {
|
|
|
9001
9148
|
unlockProtected: () => unlockProtected
|
|
9002
9149
|
});
|
|
9003
9150
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
9004
|
-
import { resolve as resolve4, join as
|
|
9005
|
-
import { homedir as
|
|
9151
|
+
import { resolve as resolve4, join as join6, dirname } from "path";
|
|
9152
|
+
import { homedir as homedir4 } from "os";
|
|
9006
9153
|
import { fileURLToPath } from "url";
|
|
9007
9154
|
import { createInterface } from "readline";
|
|
9008
9155
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -9065,10 +9212,10 @@ function unlockFile(file) {
|
|
|
9065
9212
|
function protectedTargets() {
|
|
9066
9213
|
const p = globalPaths();
|
|
9067
9214
|
return [
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9215
|
+
join6(p.hooksDir, "guard.mjs"),
|
|
9216
|
+
join6(p.hooksDir, "audit.mjs"),
|
|
9217
|
+
join6(p.hooksDir, "stop.mjs"),
|
|
9218
|
+
join6(p.hooksDir, "shield.mjs"),
|
|
9072
9219
|
p.configPath,
|
|
9073
9220
|
p.settingsPath
|
|
9074
9221
|
];
|
|
@@ -9080,25 +9227,25 @@ function unlockProtected() {
|
|
|
9080
9227
|
for (const f of protectedTargets()) unlockFile(f);
|
|
9081
9228
|
}
|
|
9082
9229
|
function globalPaths() {
|
|
9083
|
-
const home =
|
|
9084
|
-
const sgDir =
|
|
9085
|
-
const hooksDir =
|
|
9086
|
-
const claudeDir =
|
|
9230
|
+
const home = homedir4();
|
|
9231
|
+
const sgDir = join6(home, ".solongate");
|
|
9232
|
+
const hooksDir = join6(sgDir, "hooks");
|
|
9233
|
+
const claudeDir = join6(home, ".claude");
|
|
9087
9234
|
return {
|
|
9088
9235
|
home,
|
|
9089
9236
|
sgDir,
|
|
9090
9237
|
hooksDir,
|
|
9091
9238
|
claudeDir,
|
|
9092
|
-
settingsPath:
|
|
9093
|
-
backupPath:
|
|
9094
|
-
configPath:
|
|
9239
|
+
settingsPath: join6(claudeDir, "settings.json"),
|
|
9240
|
+
backupPath: join6(claudeDir, "settings.solongate.bak"),
|
|
9241
|
+
configPath: join6(sgDir, "cloud-guard.json")
|
|
9095
9242
|
};
|
|
9096
9243
|
}
|
|
9097
9244
|
function readHook(filename) {
|
|
9098
|
-
return readFileSync6(
|
|
9245
|
+
return readFileSync6(join6(HOOKS_DIR, filename), "utf-8");
|
|
9099
9246
|
}
|
|
9100
9247
|
function readGuard() {
|
|
9101
|
-
const bundled =
|
|
9248
|
+
const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
|
|
9102
9249
|
return existsSync4(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
|
|
9103
9250
|
}
|
|
9104
9251
|
function ask(question) {
|
|
@@ -9153,7 +9300,7 @@ function shimTargets() {
|
|
|
9153
9300
|
return [];
|
|
9154
9301
|
}
|
|
9155
9302
|
}
|
|
9156
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
9303
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join6(homedir4(), f)).filter((f) => existsSync4(f));
|
|
9157
9304
|
}
|
|
9158
9305
|
function writeShimBlock(file, block) {
|
|
9159
9306
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
@@ -9224,10 +9371,10 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9224
9371
|
mkdirSync3(p.hooksDir, { recursive: true });
|
|
9225
9372
|
mkdirSync3(p.claudeDir, { recursive: true });
|
|
9226
9373
|
unlockProtected();
|
|
9227
|
-
writeFileSync3(
|
|
9228
|
-
writeFileSync3(
|
|
9229
|
-
writeFileSync3(
|
|
9230
|
-
writeFileSync3(
|
|
9374
|
+
writeFileSync3(join6(p.hooksDir, "guard.mjs"), readGuard());
|
|
9375
|
+
writeFileSync3(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
|
|
9376
|
+
writeFileSync3(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
|
|
9377
|
+
writeFileSync3(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
|
|
9231
9378
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
9232
9379
|
removeClaudeShim();
|
|
9233
9380
|
writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
@@ -9245,9 +9392,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9245
9392
|
existing = {};
|
|
9246
9393
|
}
|
|
9247
9394
|
}
|
|
9248
|
-
const guardAbs =
|
|
9249
|
-
const auditAbs =
|
|
9250
|
-
const stopAbs =
|
|
9395
|
+
const guardAbs = join6(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
|
|
9396
|
+
const auditAbs = join6(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
|
|
9397
|
+
const stopAbs = join6(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
|
|
9251
9398
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
9252
9399
|
const merged = {
|
|
9253
9400
|
...existing,
|
|
@@ -9437,11 +9584,11 @@ import { createServer, request as httpRequest } from "http";
|
|
|
9437
9584
|
import { request as httpsRequest } from "https";
|
|
9438
9585
|
import { spawn as spawn2 } from "child_process";
|
|
9439
9586
|
import { URL as URL2 } from "url";
|
|
9440
|
-
import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync } from "fs";
|
|
9587
|
+
import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
|
|
9441
9588
|
import { resolve as resolve5 } from "path";
|
|
9442
|
-
import { homedir as
|
|
9589
|
+
import { homedir as homedir5 } from "os";
|
|
9443
9590
|
function findCacheFile() {
|
|
9444
|
-
const dir = resolve5(
|
|
9591
|
+
const dir = resolve5(homedir5(), ".solongate");
|
|
9445
9592
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
9446
9593
|
if (envSel) {
|
|
9447
9594
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -9452,7 +9599,7 @@ function findCacheFile() {
|
|
|
9452
9599
|
for (const name of readdirSync(dir)) {
|
|
9453
9600
|
if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
|
|
9454
9601
|
const full = resolve5(dir, name);
|
|
9455
|
-
const ts =
|
|
9602
|
+
const ts = statSync2(full).mtimeMs;
|
|
9456
9603
|
if (ts > bestTs) {
|
|
9457
9604
|
bestTs = ts;
|
|
9458
9605
|
best = full;
|
|
@@ -9743,9 +9890,9 @@ __export(logs_server_exports, {
|
|
|
9743
9890
|
runLogsServer: () => runLogsServer
|
|
9744
9891
|
});
|
|
9745
9892
|
import { createServer as createServer2 } from "http";
|
|
9746
|
-
import { readFileSync as readFileSync8, statSync as
|
|
9747
|
-
import { resolve as resolve6, join as
|
|
9748
|
-
import { homedir as
|
|
9893
|
+
import { readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
9894
|
+
import { resolve as resolve6, join as join7, isAbsolute } from "path";
|
|
9895
|
+
import { homedir as homedir6 } from "os";
|
|
9749
9896
|
import { readdirSync as readdirSync2 } from "fs";
|
|
9750
9897
|
function allowedOrigins() {
|
|
9751
9898
|
const base = [
|
|
@@ -9762,15 +9909,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
9762
9909
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
9763
9910
|
if (!dir) return null;
|
|
9764
9911
|
if (isAbsolute(dir)) return dir;
|
|
9765
|
-
return resolve6(
|
|
9912
|
+
return resolve6(homedir6(), ".solongate", "local-logs");
|
|
9766
9913
|
}
|
|
9767
9914
|
async function findLogDir() {
|
|
9768
|
-
const base = resolve6(
|
|
9915
|
+
const base = resolve6(homedir6(), ".solongate");
|
|
9769
9916
|
try {
|
|
9770
9917
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
9771
9918
|
for (const f of files) {
|
|
9772
9919
|
try {
|
|
9773
|
-
const c2 = JSON.parse(readFileSync8(
|
|
9920
|
+
const c2 = JSON.parse(readFileSync8(join7(base, f), "utf-8"));
|
|
9774
9921
|
const p = c2?.security?.localLogs?.path;
|
|
9775
9922
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
9776
9923
|
} catch {
|
|
@@ -9779,7 +9926,7 @@ async function findLogDir() {
|
|
|
9779
9926
|
} catch {
|
|
9780
9927
|
}
|
|
9781
9928
|
try {
|
|
9782
|
-
const cfgRaw = readFileSync8(
|
|
9929
|
+
const cfgRaw = readFileSync8(join7(base, "cloud-guard.json"), "utf-8");
|
|
9783
9930
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
9784
9931
|
if (apiKey) {
|
|
9785
9932
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -9808,9 +9955,9 @@ function setCors(req, res) {
|
|
|
9808
9955
|
}
|
|
9809
9956
|
function fileInfo(dir) {
|
|
9810
9957
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
9811
|
-
const file =
|
|
9958
|
+
const file = join7(dir, LOG_FILENAME);
|
|
9812
9959
|
try {
|
|
9813
|
-
const st =
|
|
9960
|
+
const st = statSync3(file);
|
|
9814
9961
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
9815
9962
|
} catch {
|
|
9816
9963
|
return { file, exists: false, size: 0, mtimeMs: 0 };
|
|
@@ -10259,7 +10406,7 @@ var init_inject = __esm({
|
|
|
10259
10406
|
// src/create.ts
|
|
10260
10407
|
var create_exports = {};
|
|
10261
10408
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
|
|
10262
|
-
import { resolve as resolve8, join as
|
|
10409
|
+
import { resolve as resolve8, join as join8 } from "path";
|
|
10263
10410
|
import { execSync as execSync2 } from "child_process";
|
|
10264
10411
|
function withSpinner(message, fn) {
|
|
10265
10412
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -10339,7 +10486,7 @@ EXAMPLES
|
|
|
10339
10486
|
}
|
|
10340
10487
|
function createProject(dir, name, _policy) {
|
|
10341
10488
|
writeFileSync5(
|
|
10342
|
-
|
|
10489
|
+
join8(dir, "package.json"),
|
|
10343
10490
|
JSON.stringify(
|
|
10344
10491
|
{
|
|
10345
10492
|
name,
|
|
@@ -10369,7 +10516,7 @@ function createProject(dir, name, _policy) {
|
|
|
10369
10516
|
) + "\n"
|
|
10370
10517
|
);
|
|
10371
10518
|
writeFileSync5(
|
|
10372
|
-
|
|
10519
|
+
join8(dir, "tsconfig.json"),
|
|
10373
10520
|
JSON.stringify(
|
|
10374
10521
|
{
|
|
10375
10522
|
compilerOptions: {
|
|
@@ -10389,9 +10536,9 @@ function createProject(dir, name, _policy) {
|
|
|
10389
10536
|
2
|
|
10390
10537
|
) + "\n"
|
|
10391
10538
|
);
|
|
10392
|
-
mkdirSync4(
|
|
10539
|
+
mkdirSync4(join8(dir, "src"), { recursive: true });
|
|
10393
10540
|
writeFileSync5(
|
|
10394
|
-
|
|
10541
|
+
join8(dir, "src", "index.ts"),
|
|
10395
10542
|
`#!/usr/bin/env node
|
|
10396
10543
|
|
|
10397
10544
|
console.log = (...args: unknown[]) => {
|
|
@@ -10433,7 +10580,7 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10433
10580
|
`
|
|
10434
10581
|
);
|
|
10435
10582
|
writeFileSync5(
|
|
10436
|
-
|
|
10583
|
+
join8(dir, ".mcp.json"),
|
|
10437
10584
|
JSON.stringify(
|
|
10438
10585
|
{
|
|
10439
10586
|
mcpServers: {
|
|
@@ -10451,12 +10598,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10451
10598
|
) + "\n"
|
|
10452
10599
|
);
|
|
10453
10600
|
writeFileSync5(
|
|
10454
|
-
|
|
10601
|
+
join8(dir, ".env"),
|
|
10455
10602
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
10456
10603
|
`
|
|
10457
10604
|
);
|
|
10458
10605
|
writeFileSync5(
|
|
10459
|
-
|
|
10606
|
+
join8(dir, ".gitignore"),
|
|
10460
10607
|
`node_modules/
|
|
10461
10608
|
dist/
|
|
10462
10609
|
*.solongate-backup
|
package/dist/tui/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/tui/App.tsx
|
|
11
|
-
import { Box as Box8, Text as Text8, useApp, useInput as
|
|
11
|
+
import { Box as Box8, Text as Text8, useApp, useInput as useInput6 } from "ink";
|
|
12
12
|
import { useState as useState7 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/cli-utils.ts
|
|
@@ -125,7 +125,10 @@ function KeyHints({ hints }) {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// src/tui/panels/Live.tsx
|
|
128
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
128
|
+
import { Box as Box2, Text as Text2, useInput } 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,10 +526,23 @@ 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);
|
|
502
536
|
const pausedUntil = useRef2(0);
|
|
537
|
+
const [frozen, setFrozen] = useState2(false);
|
|
538
|
+
const frozenRef = useRef2(false);
|
|
539
|
+
frozenRef.current = frozen;
|
|
540
|
+
useInput(
|
|
541
|
+
(input) => {
|
|
542
|
+
if (input === "p") setFrozen((f) => !f);
|
|
543
|
+
},
|
|
544
|
+
{ isActive: active2 }
|
|
545
|
+
);
|
|
503
546
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
504
547
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
505
548
|
}, []);
|
|
@@ -515,7 +558,69 @@ function LivePanel({ active: active2 }) {
|
|
|
515
558
|
},
|
|
516
559
|
[pushLog]
|
|
517
560
|
);
|
|
518
|
-
const paused = () => Date.now() < pausedUntil.current;
|
|
561
|
+
const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
|
|
562
|
+
const pollLocal = useCallback2(() => {
|
|
563
|
+
if (frozenRef.current) return;
|
|
564
|
+
const lines = tailLines(LOCAL_LOG);
|
|
565
|
+
if (!lines.length) {
|
|
566
|
+
setLocalOn((prev) => prev === null ? false : prev);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
setLocalOn(true);
|
|
570
|
+
const fresh = [];
|
|
571
|
+
for (const line of lines) {
|
|
572
|
+
try {
|
|
573
|
+
const j = JSON.parse(line);
|
|
574
|
+
const at = Date.parse(j.ts ?? "");
|
|
575
|
+
if (!Number.isFinite(at) || at <= lastLocalTs.current) continue;
|
|
576
|
+
fresh.push({
|
|
577
|
+
id: "l:" + at + ":" + (j.tool ?? ""),
|
|
578
|
+
at,
|
|
579
|
+
tool: j.tool ?? "?",
|
|
580
|
+
decision: j.decision ?? "ALLOW",
|
|
581
|
+
permission: (j.permission ?? "").slice(0, 4),
|
|
582
|
+
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
583
|
+
dlp: !!j.dlp,
|
|
584
|
+
source: "local",
|
|
585
|
+
session: j.session_id,
|
|
586
|
+
agent: j.agent_name
|
|
587
|
+
});
|
|
588
|
+
} catch {
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (fresh.length) {
|
|
592
|
+
lastLocalTs.current = fresh[fresh.length - 1].at;
|
|
593
|
+
setLocalBuf((prev) => [...prev, ...fresh].slice(-400));
|
|
594
|
+
const denies = fresh.filter((f) => f.decision !== "ALLOW").length;
|
|
595
|
+
if (lastLocalTs.current !== fresh[0].at || fresh.length < 10) {
|
|
596
|
+
pushLog(`local +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : "warn");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const ringLines = tailLines(RING, 8192).slice(-30);
|
|
600
|
+
if (ringLines.length) {
|
|
601
|
+
let sum = 0;
|
|
602
|
+
let n = 0;
|
|
603
|
+
let session = "";
|
|
604
|
+
for (const rl2 of ringLines) {
|
|
605
|
+
try {
|
|
606
|
+
const j = JSON.parse(rl2);
|
|
607
|
+
if (typeof j.ms === "number") {
|
|
608
|
+
sum += j.ms;
|
|
609
|
+
n++;
|
|
610
|
+
}
|
|
611
|
+
if (j.session) session = j.session;
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (n) setRing({ avgMs: Math.round(sum / n), session, count: n });
|
|
616
|
+
}
|
|
617
|
+
}, [pushLog]);
|
|
618
|
+
useEffect2(() => {
|
|
619
|
+
if (!active2) return;
|
|
620
|
+
pollLocal();
|
|
621
|
+
const t = setInterval(pollLocal, 2e3);
|
|
622
|
+
return () => clearInterval(t);
|
|
623
|
+
}, [active2, pollLocal]);
|
|
519
624
|
const pollFeed = useCallback2(async () => {
|
|
520
625
|
if (paused()) return;
|
|
521
626
|
const t0 = Date.now();
|
|
@@ -527,16 +632,11 @@ function LivePanel({ active: active2 }) {
|
|
|
527
632
|
const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
|
|
528
633
|
for (const e of fresh) seenRef.current.add(e.id);
|
|
529
634
|
if (fresh.length) {
|
|
530
|
-
|
|
635
|
+
setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
531
636
|
}
|
|
532
637
|
const denies = fresh.filter((e) => e.decision !== "ALLOW").length;
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
else
|
|
536
|
-
pushLog(
|
|
537
|
-
`api ${ms}ms \xB7 +${fresh.length} calls${denies ? ` \xB7 ${denies} DENIED` : ""}${dlp ? ` \xB7 ${dlp} DLP` : ""}`,
|
|
538
|
-
denies || dlp ? "bad" : fresh.length ? "warn" : "ok"
|
|
539
|
-
);
|
|
638
|
+
if (firstLoad) pushLog(`cloud link up \xB7 api ${ms}ms \xB7 ${fresh.length} calls`);
|
|
639
|
+
else pushLog(`api ${ms}ms \xB7 +${fresh.length} cloud${denies ? ` \xB7 ${denies} DENIED` : ""}`, denies ? "bad" : fresh.length ? "warn" : "ok");
|
|
540
640
|
} catch (e) {
|
|
541
641
|
onApiError(e);
|
|
542
642
|
}
|
|
@@ -579,62 +679,76 @@ function LivePanel({ active: active2 }) {
|
|
|
579
679
|
usePoll(() => {
|
|
580
680
|
if (!paused()) insights.reload();
|
|
581
681
|
}, 3e4, active2);
|
|
582
|
-
useEffect2(() => {
|
|
583
|
-
const c2 = sessions.data?.counts;
|
|
584
|
-
if (c2) pushLog(`sessions ${c2.active}\u25B2 ${c2.idle}\u25CC ${c2.deactivated}\u25CB`);
|
|
585
|
-
}, [sessions.data, pushLog]);
|
|
586
|
-
useEffect2(() => {
|
|
587
|
-
const ib = insights.data;
|
|
588
|
-
if (ib?.layers) {
|
|
589
|
-
const rl2 = ib.layers.rateLimit;
|
|
590
|
-
const dl2 = ib.layers.dlp;
|
|
591
|
-
pushLog(`layers \xB7 ratelimit ${rl2?.mode ?? "?"} ${rl2?.perMinute || "\u2014"}/m \xB7 dlp ${dl2?.mode ?? "?"} ${(dl2?.patterns ?? []).length} armed`);
|
|
592
|
-
}
|
|
593
|
-
}, [insights.data, pushLog]);
|
|
594
682
|
const [tick, setTick] = useState2(0);
|
|
595
683
|
useEffect2(() => {
|
|
596
|
-
if (!active2) return;
|
|
684
|
+
if (!active2 || frozen) return;
|
|
597
685
|
const t = setInterval(() => setTick((n) => n + 1), 200);
|
|
598
686
|
return () => clearInterval(t);
|
|
599
|
-
}, [active2]);
|
|
687
|
+
}, [active2, frozen]);
|
|
600
688
|
const startRef = useRef2(Date.now());
|
|
601
689
|
const cols = process.stdout.columns ?? 100;
|
|
602
690
|
const rows = process.stdout.rows ?? 30;
|
|
603
691
|
const ins = insights.data ?? {};
|
|
604
692
|
const spin = SPIN[tick % SPIN.length];
|
|
605
693
|
const points = tsData?.timeseries ?? [];
|
|
606
|
-
const
|
|
607
|
-
const
|
|
694
|
+
const mergedAll = [...cloudBuf, ...localBuf].sort((a, b) => a.at - b.at);
|
|
695
|
+
const nowMs = Date.now();
|
|
696
|
+
const minuteCounts = new Array(60).fill(0);
|
|
697
|
+
const minuteHot = new Array(60).fill(false);
|
|
698
|
+
for (const e of mergedAll) {
|
|
699
|
+
const idx = 59 - Math.floor((nowMs - e.at) / 6e4);
|
|
700
|
+
if (idx >= 0 && idx < 60) {
|
|
701
|
+
minuteCounts[idx]++;
|
|
702
|
+
if (e.decision !== "ALLOW") minuteHot[idx] = true;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const localTraffic = localOn === true && minuteCounts.some((c2) => c2 > 0);
|
|
706
|
+
const traffic = localTraffic ? minuteCounts : points.map((p) => p.total);
|
|
707
|
+
const trafficHot = localTraffic ? minuteHot : points.map((p) => p.denied > 0);
|
|
708
|
+
const trafficLabel = localTraffic ? "calls/min \xB7 60m \xB7 local+cloud" : "24h \xB7 cloud";
|
|
608
709
|
const rl = ins.layers?.rateLimit;
|
|
609
710
|
const dl = ins.layers?.dlp;
|
|
610
711
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
611
712
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
612
713
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
714
|
+
const merged = mergedAll;
|
|
715
|
+
const denials = merged.filter((e) => e.decision !== "ALLOW");
|
|
716
|
+
const lastDeny = denials[denials.length - 1];
|
|
613
717
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
614
|
-
for (const e of
|
|
718
|
+
for (const e of merged) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
615
719
|
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
616
|
-
const denials = buffer.filter((e) => e.decision !== "ALLOW");
|
|
617
|
-
const lastDeny = denials[denials.length - 1];
|
|
618
720
|
const latNow = lat[lat.length - 1] ?? 0;
|
|
619
|
-
const
|
|
620
|
-
const
|
|
721
|
+
const sortedLat = [...lat].sort((a, b) => a - b);
|
|
722
|
+
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
723
|
+
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
621
724
|
const backingOff = Date.now() < pausedUntil.current;
|
|
622
725
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
623
726
|
const sessCounts = sessions.data?.counts;
|
|
727
|
+
const localSess = /* @__PURE__ */ new Map();
|
|
728
|
+
for (const e of localBuf) {
|
|
729
|
+
if (!e.session) continue;
|
|
730
|
+
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, lastAt: 0 };
|
|
731
|
+
cur.calls++;
|
|
732
|
+
if (e.decision !== "ALLOW") cur.denies++;
|
|
733
|
+
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
734
|
+
if (e.agent) cur.agent = e.agent;
|
|
735
|
+
localSess.set(e.session, cur);
|
|
736
|
+
}
|
|
737
|
+
const localSessList = [...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt);
|
|
624
738
|
const chartH = rows >= 36 ? 6 : 4;
|
|
625
739
|
const colH = rows >= 32 ? 6 : 5;
|
|
626
|
-
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
627
|
-
const tail =
|
|
740
|
+
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
741
|
+
const tail = merged.slice(-streamRows);
|
|
628
742
|
const innerW = cols - 2;
|
|
629
743
|
const leftW = Math.floor(innerW * 0.55);
|
|
630
744
|
const rightW = innerW - leftW - 2;
|
|
631
745
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
632
|
-
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\
|
|
746
|
+
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
633
747
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
634
748
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
635
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: "
|
|
749
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
636
750
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
637
|
-
|
|
751
|
+
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " PAUSED \xB7 copy freely \xB7 p resume " }) : 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 " })
|
|
638
752
|
] }),
|
|
639
753
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
640
754
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -643,24 +757,24 @@ function LivePanel({ active: active2 }) {
|
|
|
643
757
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
644
758
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
645
759
|
/* @__PURE__ */ jsx2(Text2, { color: theme.bad, bold: true, children: s ? s.denied : "\xB7\xB7" }),
|
|
760
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 local " }),
|
|
761
|
+
localOn ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "\u2713 streaming" }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "off" }),
|
|
646
762
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
647
763
|
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
648
764
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
649
765
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
650
766
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
|
|
651
767
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: sessCounts ? `${sessCounts.active}\u25B2 ${sessCounts.idle}\u25CC ${sessCounts.deactivated}\u25CB` : "\xB7" }),
|
|
652
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 policies " }),
|
|
653
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: s ? s.active_policies : "\xB7" }),
|
|
654
768
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
655
769
|
] }),
|
|
656
770
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
657
771
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
658
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra:
|
|
772
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
|
|
659
773
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
660
774
|
] }),
|
|
661
775
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
662
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `
|
|
663
|
-
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0" })
|
|
776
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "API LATENCY", extra: `now ${latNow}ms \xB7 med ${latMed}ms \xB7 amber >${Math.round(latHotAt)}ms`, width: rightW }),
|
|
777
|
+
/* @__PURE__ */ jsx2(ColumnChart, { series: lat, hot: lat.map((v) => v > latHotAt), height: chartH, width: rightW, color: "#82AAF0", hotColor: "#ffb454" })
|
|
664
778
|
] })
|
|
665
779
|
] }),
|
|
666
780
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -680,18 +794,45 @@ function LivePanel({ active: active2 }) {
|
|
|
680
794
|
" patterns armed"
|
|
681
795
|
] })
|
|
682
796
|
] }),
|
|
683
|
-
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
|
|
797
|
+
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: " no dlp hits in last 7 days" }),
|
|
798
|
+
ring ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
799
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
800
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local \u2713" }),
|
|
801
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ` eval avg ${ring.avgMs}ms \xB7 ${ring.count} recent` })
|
|
802
|
+
] }) : null
|
|
684
803
|
] }),
|
|
685
804
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
686
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra:
|
|
687
|
-
sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
688
|
-
|
|
805
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: `${localSessList.length} local \xB7 ${sessCounts?.active ?? 0} cloud live`, width: colW }),
|
|
806
|
+
localSessList.length === 0 && sess.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
|
|
807
|
+
localSessList.slice(0, Math.max(1, colH - 2)).map(([id, v]) => {
|
|
808
|
+
const fresh = nowMs - v.lastAt < 9e4;
|
|
809
|
+
const isMe = ring?.session === id;
|
|
810
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
811
|
+
/* @__PURE__ */ jsxs2(Text2, { color: fresh ? theme.ok : theme.dim, children: [
|
|
812
|
+
fresh ? "\u25CF" : "\u25CB",
|
|
813
|
+
" "
|
|
814
|
+
] }),
|
|
815
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "LOC " }),
|
|
816
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, bold: isMe, children: truncate(isMe ? "this machine" : v.agent, 12).padEnd(13) }),
|
|
817
|
+
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
818
|
+
String(v.calls).padStart(4),
|
|
819
|
+
"c "
|
|
820
|
+
] }),
|
|
821
|
+
/* @__PURE__ */ jsxs2(Text2, { color: v.denies ? theme.bad : theme.dim, children: [
|
|
822
|
+
String(v.denies).padStart(3),
|
|
823
|
+
"d "
|
|
824
|
+
] }),
|
|
825
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(v.lastAt) })
|
|
826
|
+
] }, id);
|
|
827
|
+
}),
|
|
828
|
+
sess.slice(0, Math.max(0, colH - 1 - Math.min(localSessList.length, Math.max(1, colH - 2)))).map((a) => {
|
|
689
829
|
const dot = sessionDot(a.status);
|
|
690
830
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
691
831
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
692
832
|
dot.ch,
|
|
693
833
|
" "
|
|
694
834
|
] }),
|
|
835
|
+
/* @__PURE__ */ jsx2(Text2, { color: "#4f6db8", children: "CLD " }),
|
|
695
836
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(a.agent_name ?? a.session_id, 12).padEnd(13) }),
|
|
696
837
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
697
838
|
String(a.total_calls).padStart(4),
|
|
@@ -717,7 +858,11 @@ function LivePanel({ active: active2 }) {
|
|
|
717
858
|
] }, l.ts + ":" + i))
|
|
718
859
|
] })
|
|
719
860
|
] }),
|
|
720
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `tail
|
|
861
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TOOL STREAM", extra: `local+cloud tail \xB7 ${merged.length} buffered`, width: innerW }),
|
|
862
|
+
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
863
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
864
|
+
/* @__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`)" })
|
|
865
|
+
] }) : null,
|
|
721
866
|
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
722
867
|
spin,
|
|
723
868
|
" awaiting traffic\u2026"
|
|
@@ -725,25 +870,27 @@ function LivePanel({ active: active2 }) {
|
|
|
725
870
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
726
871
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
727
872
|
"[",
|
|
728
|
-
hhmmss(e.
|
|
729
|
-
"
|
|
873
|
+
hhmmss(e.at),
|
|
874
|
+
" "
|
|
730
875
|
] }),
|
|
876
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.source === "local" ? theme.ok : "#4f6db8", children: e.source === "local" ? "LOC" : "CLD" }),
|
|
877
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "] " }),
|
|
731
878
|
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: e.decision !== "ALLOW", children: e.decision.padEnd(6) }),
|
|
732
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.
|
|
733
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
734
|
-
e.
|
|
735
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
879
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(e.tool, 14).padEnd(15) }),
|
|
880
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.permission.padEnd(5) }),
|
|
881
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
882
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
736
883
|
] }, e.id)),
|
|
737
884
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
738
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
739
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} \xB7 buf ${
|
|
740
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
885
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
886
|
+
/* @__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} ` }),
|
|
887
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " p pause/copy \xB7 esc menu \xB7 q quit " })
|
|
741
888
|
] })
|
|
742
889
|
] });
|
|
743
890
|
}
|
|
744
891
|
|
|
745
892
|
// src/tui/panels/Policies.tsx
|
|
746
|
-
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
893
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
747
894
|
import TextInput from "ink-text-input";
|
|
748
895
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
749
896
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -834,7 +981,7 @@ function PoliciesPanel({ focused }) {
|
|
|
834
981
|
setDirty(false);
|
|
835
982
|
setStatus("discarded");
|
|
836
983
|
};
|
|
837
|
-
|
|
984
|
+
useInput2(
|
|
838
985
|
(input, key) => {
|
|
839
986
|
if (view === "list") {
|
|
840
987
|
if (key.upArrow) setPi((n) => Math.max(0, n - 1));
|
|
@@ -978,7 +1125,7 @@ function PoliciesPanel({ focused }) {
|
|
|
978
1125
|
}
|
|
979
1126
|
|
|
980
1127
|
// src/tui/panels/RateLimit.tsx
|
|
981
|
-
import { Box as Box4, Text as Text4, useInput as
|
|
1128
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
982
1129
|
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
983
1130
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
984
1131
|
var MODES = ["off", "detect", "block"];
|
|
@@ -1029,7 +1176,7 @@ function RateLimitPanel({ focused }) {
|
|
|
1029
1176
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
1030
1177
|
}
|
|
1031
1178
|
};
|
|
1032
|
-
|
|
1179
|
+
useInput3(
|
|
1033
1180
|
(input, key) => {
|
|
1034
1181
|
const step = key.shift ? 10 : 1;
|
|
1035
1182
|
if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
|
|
@@ -1108,7 +1255,7 @@ function FieldRow({ label, active: active2, children }) {
|
|
|
1108
1255
|
}
|
|
1109
1256
|
|
|
1110
1257
|
// src/tui/panels/Dlp.tsx
|
|
1111
|
-
import { Box as Box5, Text as Text5, useInput as
|
|
1258
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
1112
1259
|
import TextInput2 from "ink-text-input";
|
|
1113
1260
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
1114
1261
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
@@ -1153,7 +1300,7 @@ function DlpPanel({ focused }) {
|
|
|
1153
1300
|
};
|
|
1154
1301
|
const customCount = dlp?.custom.length ?? 0;
|
|
1155
1302
|
const total = available.length + customCount;
|
|
1156
|
-
|
|
1303
|
+
useInput4(
|
|
1157
1304
|
(input, key) => {
|
|
1158
1305
|
if (!dlp) return;
|
|
1159
1306
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -1315,7 +1462,7 @@ function StatsPanel({ active: active2 }) {
|
|
|
1315
1462
|
}
|
|
1316
1463
|
|
|
1317
1464
|
// src/tui/panels/Audit.tsx
|
|
1318
|
-
import { Box as Box7, Text as Text7, useInput as
|
|
1465
|
+
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
1319
1466
|
import TextInput3 from "ink-text-input";
|
|
1320
1467
|
import { useState as useState6 } from "react";
|
|
1321
1468
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -1346,7 +1493,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
1346
1493
|
() => view === "detail" && current?.session_id ? api.audit.list({ session_id: current.session_id, limit: 40 }) : Promise.resolve(null),
|
|
1347
1494
|
[view, current?.session_id]
|
|
1348
1495
|
);
|
|
1349
|
-
|
|
1496
|
+
useInput5(
|
|
1350
1497
|
(input, key) => {
|
|
1351
1498
|
if (view === "detail") {
|
|
1352
1499
|
if (key.leftArrow || key.escape) setView("list");
|
|
@@ -1523,7 +1670,7 @@ function App() {
|
|
|
1523
1670
|
const { exit } = useApp();
|
|
1524
1671
|
const [section, setSection] = useState7(0);
|
|
1525
1672
|
const [focus, setFocus] = useState7("nav");
|
|
1526
|
-
|
|
1673
|
+
useInput6((input, key) => {
|
|
1527
1674
|
if (focus === "nav") {
|
|
1528
1675
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
1529
1676
|
else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.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": {
|