@solongate/proxy 0.66.1 → 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 +190 -96
- package/dist/tui/index.js +145 -51
- 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,9 +7104,13 @@ 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);
|
|
7083
7115
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7084
7116
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
@@ -7096,6 +7128,65 @@ function LivePanel({ active: active2 }) {
|
|
|
7096
7128
|
[pushLog]
|
|
7097
7129
|
);
|
|
7098
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]);
|
|
7099
7190
|
const pollFeed = useCallback2(async () => {
|
|
7100
7191
|
if (paused()) return;
|
|
7101
7192
|
const t0 = Date.now();
|
|
@@ -7107,16 +7198,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7107
7198
|
const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
|
|
7108
7199
|
for (const e of fresh) seenRef.current.add(e.id);
|
|
7109
7200
|
if (fresh.length) {
|
|
7110
|
-
|
|
7201
|
+
setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
7111
7202
|
}
|
|
7112
7203
|
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
|
-
);
|
|
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");
|
|
7120
7206
|
} catch (e) {
|
|
7121
7207
|
onApiError(e);
|
|
7122
7208
|
}
|
|
@@ -7159,18 +7245,6 @@ function LivePanel({ active: active2 }) {
|
|
|
7159
7245
|
usePoll(() => {
|
|
7160
7246
|
if (!paused()) insights.reload();
|
|
7161
7247
|
}, 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
7248
|
const [tick, setTick] = useState2(0);
|
|
7175
7249
|
useEffect2(() => {
|
|
7176
7250
|
if (!active2) return;
|
|
@@ -7190,31 +7264,33 @@ function LivePanel({ active: active2 }) {
|
|
|
7190
7264
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
7191
7265
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
7192
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];
|
|
7193
7270
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
7194
|
-
for (const e of
|
|
7271
|
+
for (const e of merged) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
7195
7272
|
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
7273
|
const latNow = lat[lat.length - 1] ?? 0;
|
|
7199
|
-
const
|
|
7200
|
-
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);
|
|
7201
7277
|
const backingOff = Date.now() < pausedUntil.current;
|
|
7202
7278
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
7203
7279
|
const sessCounts = sessions.data?.counts;
|
|
7204
7280
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7205
7281
|
const colH = rows >= 32 ? 6 : 5;
|
|
7206
|
-
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
7207
|
-
const tail =
|
|
7282
|
+
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
7283
|
+
const tail = merged.slice(-streamRows);
|
|
7208
7284
|
const innerW = cols - 2;
|
|
7209
7285
|
const leftW = Math.floor(innerW * 0.55);
|
|
7210
7286
|
const rightW = innerW - leftW - 2;
|
|
7211
7287
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
7212
|
-
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 };
|
|
7213
7289
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7214
7290
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7215
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: "
|
|
7291
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7216
7292
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
7217
|
-
backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: "
|
|
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 " })
|
|
7218
7294
|
] }),
|
|
7219
7295
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7220
7296
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -7223,14 +7299,14 @@ function LivePanel({ active: active2 }) {
|
|
|
7223
7299
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
7224
7300
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
7225
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" }),
|
|
7226
7304
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
7227
7305
|
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
7228
7306
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
7229
7307
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
7230
7308
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
|
|
7231
7309
|
/* @__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
7310
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7235
7311
|
] }),
|
|
7236
7312
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -7239,8 +7315,8 @@ function LivePanel({ active: active2 }) {
|
|
|
7239
7315
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
7240
7316
|
] }),
|
|
7241
7317
|
/* @__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" })
|
|
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" })
|
|
7244
7320
|
] })
|
|
7245
7321
|
] }),
|
|
7246
7322
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -7260,12 +7336,22 @@ function LivePanel({ active: active2 }) {
|
|
|
7260
7336
|
" patterns armed"
|
|
7261
7337
|
] })
|
|
7262
7338
|
] }),
|
|
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 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
|
|
7264
7345
|
] }),
|
|
7265
7346
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7266
7347
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} live` : "", width: colW }),
|
|
7267
|
-
|
|
7268
|
-
|
|
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) => {
|
|
7269
7355
|
const dot = sessionDot(a.status);
|
|
7270
7356
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7271
7357
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
@@ -7297,7 +7383,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7297
7383
|
] }, l.ts + ":" + i))
|
|
7298
7384
|
] })
|
|
7299
7385
|
] }),
|
|
7300
|
-
/* @__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,
|
|
7301
7391
|
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7302
7392
|
spin,
|
|
7303
7393
|
" awaiting traffic\u2026"
|
|
@@ -7305,23 +7395,25 @@ function LivePanel({ active: active2 }) {
|
|
|
7305
7395
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7306
7396
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7307
7397
|
"[",
|
|
7308
|
-
hhmmss(e.
|
|
7309
|
-
"
|
|
7398
|
+
hhmmss(e.at),
|
|
7399
|
+
" "
|
|
7310
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: "] " }),
|
|
7311
7403
|
/* @__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:
|
|
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 })
|
|
7316
7408
|
] }, e.id)),
|
|
7317
7409
|
/* @__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 ${
|
|
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} ` }),
|
|
7320
7412
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
7321
7413
|
] })
|
|
7322
7414
|
] });
|
|
7323
7415
|
}
|
|
7324
|
-
var SPIN, BG, hhmmss, fmtUp;
|
|
7416
|
+
var SPIN, BG, LOCAL_LOG, RING, hhmmss, fmtUp;
|
|
7325
7417
|
var init_Live = __esm({
|
|
7326
7418
|
"src/tui/panels/Live.tsx"() {
|
|
7327
7419
|
"use strict";
|
|
@@ -7330,6 +7422,8 @@ var init_Live = __esm({
|
|
|
7330
7422
|
init_theme();
|
|
7331
7423
|
SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7332
7424
|
BG = "#12234f";
|
|
7425
|
+
LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
7426
|
+
RING = join5(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
7333
7427
|
hhmmss = (ts) => {
|
|
7334
7428
|
const d = new Date(ts);
|
|
7335
7429
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -9001,8 +9095,8 @@ __export(global_install_exports, {
|
|
|
9001
9095
|
unlockProtected: () => unlockProtected
|
|
9002
9096
|
});
|
|
9003
9097
|
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
|
|
9098
|
+
import { resolve as resolve4, join as join6, dirname } from "path";
|
|
9099
|
+
import { homedir as homedir4 } from "os";
|
|
9006
9100
|
import { fileURLToPath } from "url";
|
|
9007
9101
|
import { createInterface } from "readline";
|
|
9008
9102
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -9065,10 +9159,10 @@ function unlockFile(file) {
|
|
|
9065
9159
|
function protectedTargets() {
|
|
9066
9160
|
const p = globalPaths();
|
|
9067
9161
|
return [
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9162
|
+
join6(p.hooksDir, "guard.mjs"),
|
|
9163
|
+
join6(p.hooksDir, "audit.mjs"),
|
|
9164
|
+
join6(p.hooksDir, "stop.mjs"),
|
|
9165
|
+
join6(p.hooksDir, "shield.mjs"),
|
|
9072
9166
|
p.configPath,
|
|
9073
9167
|
p.settingsPath
|
|
9074
9168
|
];
|
|
@@ -9080,25 +9174,25 @@ function unlockProtected() {
|
|
|
9080
9174
|
for (const f of protectedTargets()) unlockFile(f);
|
|
9081
9175
|
}
|
|
9082
9176
|
function globalPaths() {
|
|
9083
|
-
const home =
|
|
9084
|
-
const sgDir =
|
|
9085
|
-
const hooksDir =
|
|
9086
|
-
const claudeDir =
|
|
9177
|
+
const home = homedir4();
|
|
9178
|
+
const sgDir = join6(home, ".solongate");
|
|
9179
|
+
const hooksDir = join6(sgDir, "hooks");
|
|
9180
|
+
const claudeDir = join6(home, ".claude");
|
|
9087
9181
|
return {
|
|
9088
9182
|
home,
|
|
9089
9183
|
sgDir,
|
|
9090
9184
|
hooksDir,
|
|
9091
9185
|
claudeDir,
|
|
9092
|
-
settingsPath:
|
|
9093
|
-
backupPath:
|
|
9094
|
-
configPath:
|
|
9186
|
+
settingsPath: join6(claudeDir, "settings.json"),
|
|
9187
|
+
backupPath: join6(claudeDir, "settings.solongate.bak"),
|
|
9188
|
+
configPath: join6(sgDir, "cloud-guard.json")
|
|
9095
9189
|
};
|
|
9096
9190
|
}
|
|
9097
9191
|
function readHook(filename) {
|
|
9098
|
-
return readFileSync6(
|
|
9192
|
+
return readFileSync6(join6(HOOKS_DIR, filename), "utf-8");
|
|
9099
9193
|
}
|
|
9100
9194
|
function readGuard() {
|
|
9101
|
-
const bundled =
|
|
9195
|
+
const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
|
|
9102
9196
|
return existsSync4(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
|
|
9103
9197
|
}
|
|
9104
9198
|
function ask(question) {
|
|
@@ -9153,7 +9247,7 @@ function shimTargets() {
|
|
|
9153
9247
|
return [];
|
|
9154
9248
|
}
|
|
9155
9249
|
}
|
|
9156
|
-
return [".bashrc", ".zshrc", ".profile"].map((f) =>
|
|
9250
|
+
return [".bashrc", ".zshrc", ".profile"].map((f) => join6(homedir4(), f)).filter((f) => existsSync4(f));
|
|
9157
9251
|
}
|
|
9158
9252
|
function writeShimBlock(file, block) {
|
|
9159
9253
|
const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
|
|
@@ -9224,10 +9318,10 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9224
9318
|
mkdirSync3(p.hooksDir, { recursive: true });
|
|
9225
9319
|
mkdirSync3(p.claudeDir, { recursive: true });
|
|
9226
9320
|
unlockProtected();
|
|
9227
|
-
writeFileSync3(
|
|
9228
|
-
writeFileSync3(
|
|
9229
|
-
writeFileSync3(
|
|
9230
|
-
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"));
|
|
9231
9325
|
console.log(` Installed hooks \u2192 ${p.hooksDir}`);
|
|
9232
9326
|
removeClaudeShim();
|
|
9233
9327
|
writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
|
|
@@ -9245,9 +9339,9 @@ async function runGlobalInstall(opts = {}) {
|
|
|
9245
9339
|
existing = {};
|
|
9246
9340
|
}
|
|
9247
9341
|
}
|
|
9248
|
-
const guardAbs =
|
|
9249
|
-
const auditAbs =
|
|
9250
|
-
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, "/");
|
|
9251
9345
|
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
9252
9346
|
const merged = {
|
|
9253
9347
|
...existing,
|
|
@@ -9437,11 +9531,11 @@ import { createServer, request as httpRequest } from "http";
|
|
|
9437
9531
|
import { request as httpsRequest } from "https";
|
|
9438
9532
|
import { spawn as spawn2 } from "child_process";
|
|
9439
9533
|
import { URL as URL2 } from "url";
|
|
9440
|
-
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";
|
|
9441
9535
|
import { resolve as resolve5 } from "path";
|
|
9442
|
-
import { homedir as
|
|
9536
|
+
import { homedir as homedir5 } from "os";
|
|
9443
9537
|
function findCacheFile() {
|
|
9444
|
-
const dir = resolve5(
|
|
9538
|
+
const dir = resolve5(homedir5(), ".solongate");
|
|
9445
9539
|
const envSel = process.env.SOLONGATE_AGENT_ID;
|
|
9446
9540
|
if (envSel) {
|
|
9447
9541
|
const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
|
|
@@ -9452,7 +9546,7 @@ function findCacheFile() {
|
|
|
9452
9546
|
for (const name of readdirSync(dir)) {
|
|
9453
9547
|
if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
|
|
9454
9548
|
const full = resolve5(dir, name);
|
|
9455
|
-
const ts =
|
|
9549
|
+
const ts = statSync2(full).mtimeMs;
|
|
9456
9550
|
if (ts > bestTs) {
|
|
9457
9551
|
bestTs = ts;
|
|
9458
9552
|
best = full;
|
|
@@ -9743,9 +9837,9 @@ __export(logs_server_exports, {
|
|
|
9743
9837
|
runLogsServer: () => runLogsServer
|
|
9744
9838
|
});
|
|
9745
9839
|
import { createServer as createServer2 } from "http";
|
|
9746
|
-
import { readFileSync as readFileSync8, statSync as
|
|
9747
|
-
import { resolve as resolve6, join as
|
|
9748
|
-
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";
|
|
9749
9843
|
import { readdirSync as readdirSync2 } from "fs";
|
|
9750
9844
|
function allowedOrigins() {
|
|
9751
9845
|
const base = [
|
|
@@ -9762,15 +9856,15 @@ function resolveLocalLogDir(rawPath) {
|
|
|
9762
9856
|
const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
|
|
9763
9857
|
if (!dir) return null;
|
|
9764
9858
|
if (isAbsolute(dir)) return dir;
|
|
9765
|
-
return resolve6(
|
|
9859
|
+
return resolve6(homedir6(), ".solongate", "local-logs");
|
|
9766
9860
|
}
|
|
9767
9861
|
async function findLogDir() {
|
|
9768
|
-
const base = resolve6(
|
|
9862
|
+
const base = resolve6(homedir6(), ".solongate");
|
|
9769
9863
|
try {
|
|
9770
9864
|
const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
|
|
9771
9865
|
for (const f of files) {
|
|
9772
9866
|
try {
|
|
9773
|
-
const c2 = JSON.parse(readFileSync8(
|
|
9867
|
+
const c2 = JSON.parse(readFileSync8(join7(base, f), "utf-8"));
|
|
9774
9868
|
const p = c2?.security?.localLogs?.path;
|
|
9775
9869
|
if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
|
|
9776
9870
|
} catch {
|
|
@@ -9779,7 +9873,7 @@ async function findLogDir() {
|
|
|
9779
9873
|
} catch {
|
|
9780
9874
|
}
|
|
9781
9875
|
try {
|
|
9782
|
-
const cfgRaw = readFileSync8(
|
|
9876
|
+
const cfgRaw = readFileSync8(join7(base, "cloud-guard.json"), "utf-8");
|
|
9783
9877
|
const { apiKey, apiUrl } = JSON.parse(cfgRaw);
|
|
9784
9878
|
if (apiKey) {
|
|
9785
9879
|
const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
|
|
@@ -9808,9 +9902,9 @@ function setCors(req, res) {
|
|
|
9808
9902
|
}
|
|
9809
9903
|
function fileInfo(dir) {
|
|
9810
9904
|
if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
|
|
9811
|
-
const file =
|
|
9905
|
+
const file = join7(dir, LOG_FILENAME);
|
|
9812
9906
|
try {
|
|
9813
|
-
const st =
|
|
9907
|
+
const st = statSync3(file);
|
|
9814
9908
|
return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
|
|
9815
9909
|
} catch {
|
|
9816
9910
|
return { file, exists: false, size: 0, mtimeMs: 0 };
|
|
@@ -10259,7 +10353,7 @@ var init_inject = __esm({
|
|
|
10259
10353
|
// src/create.ts
|
|
10260
10354
|
var create_exports = {};
|
|
10261
10355
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
|
|
10262
|
-
import { resolve as resolve8, join as
|
|
10356
|
+
import { resolve as resolve8, join as join8 } from "path";
|
|
10263
10357
|
import { execSync as execSync2 } from "child_process";
|
|
10264
10358
|
function withSpinner(message, fn) {
|
|
10265
10359
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -10339,7 +10433,7 @@ EXAMPLES
|
|
|
10339
10433
|
}
|
|
10340
10434
|
function createProject(dir, name, _policy) {
|
|
10341
10435
|
writeFileSync5(
|
|
10342
|
-
|
|
10436
|
+
join8(dir, "package.json"),
|
|
10343
10437
|
JSON.stringify(
|
|
10344
10438
|
{
|
|
10345
10439
|
name,
|
|
@@ -10369,7 +10463,7 @@ function createProject(dir, name, _policy) {
|
|
|
10369
10463
|
) + "\n"
|
|
10370
10464
|
);
|
|
10371
10465
|
writeFileSync5(
|
|
10372
|
-
|
|
10466
|
+
join8(dir, "tsconfig.json"),
|
|
10373
10467
|
JSON.stringify(
|
|
10374
10468
|
{
|
|
10375
10469
|
compilerOptions: {
|
|
@@ -10389,9 +10483,9 @@ function createProject(dir, name, _policy) {
|
|
|
10389
10483
|
2
|
|
10390
10484
|
) + "\n"
|
|
10391
10485
|
);
|
|
10392
|
-
mkdirSync4(
|
|
10486
|
+
mkdirSync4(join8(dir, "src"), { recursive: true });
|
|
10393
10487
|
writeFileSync5(
|
|
10394
|
-
|
|
10488
|
+
join8(dir, "src", "index.ts"),
|
|
10395
10489
|
`#!/usr/bin/env node
|
|
10396
10490
|
|
|
10397
10491
|
console.log = (...args: unknown[]) => {
|
|
@@ -10433,7 +10527,7 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10433
10527
|
`
|
|
10434
10528
|
);
|
|
10435
10529
|
writeFileSync5(
|
|
10436
|
-
|
|
10530
|
+
join8(dir, ".mcp.json"),
|
|
10437
10531
|
JSON.stringify(
|
|
10438
10532
|
{
|
|
10439
10533
|
mcpServers: {
|
|
@@ -10451,12 +10545,12 @@ console.log('Press Ctrl+C to stop.');
|
|
|
10451
10545
|
) + "\n"
|
|
10452
10546
|
);
|
|
10453
10547
|
writeFileSync5(
|
|
10454
|
-
|
|
10548
|
+
join8(dir, ".env"),
|
|
10455
10549
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
10456
10550
|
`
|
|
10457
10551
|
);
|
|
10458
10552
|
writeFileSync5(
|
|
10459
|
-
|
|
10553
|
+
join8(dir, ".gitignore"),
|
|
10460
10554
|
`node_modules/
|
|
10461
10555
|
dist/
|
|
10462
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,9 +526,13 @@ 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);
|
|
503
537
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
504
538
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
@@ -516,6 +550,65 @@ function LivePanel({ active: active2 }) {
|
|
|
516
550
|
[pushLog]
|
|
517
551
|
);
|
|
518
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]);
|
|
519
612
|
const pollFeed = useCallback2(async () => {
|
|
520
613
|
if (paused()) return;
|
|
521
614
|
const t0 = Date.now();
|
|
@@ -527,16 +620,11 @@ function LivePanel({ active: active2 }) {
|
|
|
527
620
|
const firstLoad = seenRef.current.size === 0 && fresh.length > 1;
|
|
528
621
|
for (const e of fresh) seenRef.current.add(e.id);
|
|
529
622
|
if (fresh.length) {
|
|
530
|
-
|
|
623
|
+
setCloudBuf((prev) => [...prev, ...fresh.map(cloudItem).sort((a, b) => a.at - b.at)].slice(-400));
|
|
531
624
|
}
|
|
532
625
|
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
|
-
);
|
|
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");
|
|
540
628
|
} catch (e) {
|
|
541
629
|
onApiError(e);
|
|
542
630
|
}
|
|
@@ -579,18 +667,6 @@ function LivePanel({ active: active2 }) {
|
|
|
579
667
|
usePoll(() => {
|
|
580
668
|
if (!paused()) insights.reload();
|
|
581
669
|
}, 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
670
|
const [tick, setTick] = useState2(0);
|
|
595
671
|
useEffect2(() => {
|
|
596
672
|
if (!active2) return;
|
|
@@ -610,31 +686,33 @@ function LivePanel({ active: active2 }) {
|
|
|
610
686
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
611
687
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
612
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];
|
|
613
692
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
614
|
-
for (const e of
|
|
693
|
+
for (const e of merged) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
615
694
|
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
695
|
const latNow = lat[lat.length - 1] ?? 0;
|
|
619
|
-
const
|
|
620
|
-
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);
|
|
621
699
|
const backingOff = Date.now() < pausedUntil.current;
|
|
622
700
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
623
701
|
const sessCounts = sessions.data?.counts;
|
|
624
702
|
const chartH = rows >= 36 ? 6 : 4;
|
|
625
703
|
const colH = rows >= 32 ? 6 : 5;
|
|
626
|
-
const streamRows = Math.max(4, rows - 6 - chartH - colH);
|
|
627
|
-
const tail =
|
|
704
|
+
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
705
|
+
const tail = merged.slice(-streamRows);
|
|
628
706
|
const innerW = cols - 2;
|
|
629
707
|
const leftW = Math.floor(innerW * 0.55);
|
|
630
708
|
const rightW = innerW - leftW - 2;
|
|
631
709
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
632
|
-
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 };
|
|
633
711
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
634
712
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
635
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: "
|
|
713
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
636
714
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
637
|
-
backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: "
|
|
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 " })
|
|
638
716
|
] }),
|
|
639
717
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
640
718
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -643,14 +721,14 @@ function LivePanel({ active: active2 }) {
|
|
|
643
721
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: s ? s.allowed : "\xB7\xB7\xB7" }),
|
|
644
722
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 deny " }),
|
|
645
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" }),
|
|
646
726
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ratelimit " }),
|
|
647
727
|
/* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: rl?.mode ?? "?" }),
|
|
648
728
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 dlp " }),
|
|
649
729
|
/* @__PURE__ */ jsx2(Text2, { color: dl?.mode === "block" ? theme.ok : dl?.mode === "detect" ? theme.warn : theme.dim, children: dl?.mode ?? "?" }),
|
|
650
730
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sessions " }),
|
|
651
731
|
/* @__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
732
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
655
733
|
] }),
|
|
656
734
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -659,8 +737,8 @@ function LivePanel({ active: active2 }) {
|
|
|
659
737
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
660
738
|
] }),
|
|
661
739
|
/* @__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" })
|
|
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" })
|
|
664
742
|
] })
|
|
665
743
|
] }),
|
|
666
744
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
@@ -680,12 +758,22 @@ function LivePanel({ active: active2 }) {
|
|
|
680
758
|
" patterns armed"
|
|
681
759
|
] })
|
|
682
760
|
] }),
|
|
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 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
|
|
684
767
|
] }),
|
|
685
768
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
686
769
|
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: sessCounts ? `${sessCounts.active} live` : "", width: colW }),
|
|
687
|
-
|
|
688
|
-
|
|
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) => {
|
|
689
777
|
const dot = sessionDot(a.status);
|
|
690
778
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
691
779
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
@@ -717,7 +805,11 @@ function LivePanel({ active: active2 }) {
|
|
|
717
805
|
] }, l.ts + ":" + i))
|
|
718
806
|
] })
|
|
719
807
|
] }),
|
|
720
|
-
/* @__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,
|
|
721
813
|
tail.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
722
814
|
spin,
|
|
723
815
|
" awaiting traffic\u2026"
|
|
@@ -725,18 +817,20 @@ function LivePanel({ active: active2 }) {
|
|
|
725
817
|
tail.map((e) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
726
818
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
727
819
|
"[",
|
|
728
|
-
hhmmss(e.
|
|
729
|
-
"
|
|
820
|
+
hhmmss(e.at),
|
|
821
|
+
" "
|
|
730
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: "] " }),
|
|
731
825
|
/* @__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:
|
|
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 })
|
|
736
830
|
] }, e.id)),
|
|
737
831
|
/* @__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 ${
|
|
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} ` }),
|
|
740
834
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
741
835
|
] })
|
|
742
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": {
|