@solongate/proxy 0.67.0 → 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 +84 -31
- package/dist/tui/index.js +84 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7019,7 +7019,7 @@ 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
7023
|
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
7024
7024
|
import { homedir as homedir3 } from "os";
|
|
7025
7025
|
import { join as join5 } from "path";
|
|
@@ -7112,6 +7112,15 @@ function LivePanel({ active: active2 }) {
|
|
|
7112
7112
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7113
7113
|
const lastLocalTs = useRef2(0);
|
|
7114
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
|
+
);
|
|
7115
7124
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7116
7125
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7117
7126
|
}, []);
|
|
@@ -7127,8 +7136,9 @@ function LivePanel({ active: active2 }) {
|
|
|
7127
7136
|
},
|
|
7128
7137
|
[pushLog]
|
|
7129
7138
|
);
|
|
7130
|
-
const paused = () => Date.now() < pausedUntil.current;
|
|
7139
|
+
const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
|
|
7131
7140
|
const pollLocal = useCallback2(() => {
|
|
7141
|
+
if (frozenRef.current) return;
|
|
7132
7142
|
const lines = tailLines(LOCAL_LOG);
|
|
7133
7143
|
if (!lines.length) {
|
|
7134
7144
|
setLocalOn((prev) => prev === null ? false : prev);
|
|
@@ -7146,10 +7156,12 @@ function LivePanel({ active: active2 }) {
|
|
|
7146
7156
|
at,
|
|
7147
7157
|
tool: j.tool ?? "?",
|
|
7148
7158
|
decision: j.decision ?? "ALLOW",
|
|
7149
|
-
permission: "",
|
|
7159
|
+
permission: (j.permission ?? "").slice(0, 4),
|
|
7150
7160
|
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
7151
7161
|
dlp: !!j.dlp,
|
|
7152
|
-
source: "local"
|
|
7162
|
+
source: "local",
|
|
7163
|
+
session: j.session_id,
|
|
7164
|
+
agent: j.agent_name
|
|
7153
7165
|
});
|
|
7154
7166
|
} catch {
|
|
7155
7167
|
}
|
|
@@ -7247,24 +7259,37 @@ function LivePanel({ active: active2 }) {
|
|
|
7247
7259
|
}, 3e4, active2);
|
|
7248
7260
|
const [tick, setTick] = useState2(0);
|
|
7249
7261
|
useEffect2(() => {
|
|
7250
|
-
if (!active2) return;
|
|
7262
|
+
if (!active2 || frozen) return;
|
|
7251
7263
|
const t = setInterval(() => setTick((n) => n + 1), 200);
|
|
7252
7264
|
return () => clearInterval(t);
|
|
7253
|
-
}, [active2]);
|
|
7265
|
+
}, [active2, frozen]);
|
|
7254
7266
|
const startRef = useRef2(Date.now());
|
|
7255
7267
|
const cols = process.stdout.columns ?? 100;
|
|
7256
7268
|
const rows = process.stdout.rows ?? 30;
|
|
7257
7269
|
const ins = insights.data ?? {};
|
|
7258
7270
|
const spin = SPIN[tick % SPIN.length];
|
|
7259
7271
|
const points = tsData?.timeseries ?? [];
|
|
7260
|
-
const
|
|
7261
|
-
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";
|
|
7262
7287
|
const rl = ins.layers?.rateLimit;
|
|
7263
7288
|
const dl = ins.layers?.dlp;
|
|
7264
7289
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
7265
7290
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
7266
7291
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
7267
|
-
const merged =
|
|
7292
|
+
const merged = mergedAll;
|
|
7268
7293
|
const denials = merged.filter((e) => e.decision !== "ALLOW");
|
|
7269
7294
|
const lastDeny = denials[denials.length - 1];
|
|
7270
7295
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
@@ -7277,6 +7302,17 @@ function LivePanel({ active: active2 }) {
|
|
|
7277
7302
|
const backingOff = Date.now() < pausedUntil.current;
|
|
7278
7303
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
7279
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);
|
|
7280
7316
|
const chartH = rows >= 36 ? 6 : 4;
|
|
7281
7317
|
const colH = rows >= 32 ? 6 : 5;
|
|
7282
7318
|
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
@@ -7290,7 +7326,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7290
7326
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7291
7327
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
7292
7328
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
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 " })
|
|
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 " })
|
|
7294
7330
|
] }),
|
|
7295
7331
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7296
7332
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -7311,7 +7347,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7311
7347
|
] }),
|
|
7312
7348
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
7313
7349
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
7314
|
-
/* @__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 }),
|
|
7315
7351
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
7316
7352
|
] }),
|
|
7317
7353
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
@@ -7336,7 +7372,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7336
7372
|
" patterns armed"
|
|
7337
7373
|
] })
|
|
7338
7374
|
] }),
|
|
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
|
|
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" }),
|
|
7340
7376
|
ring ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7341
7377
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
7342
7378
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local \u2713" }),
|
|
@@ -7344,20 +7380,37 @@ function LivePanel({ active: active2 }) {
|
|
|
7344
7380
|
] }) : null
|
|
7345
7381
|
] }),
|
|
7346
7382
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
7347
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra:
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
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) => {
|
|
7355
7407
|
const dot = sessionDot(a.status);
|
|
7356
7408
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7357
7409
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
7358
7410
|
dot.ch,
|
|
7359
7411
|
" "
|
|
7360
7412
|
] }),
|
|
7413
|
+
/* @__PURE__ */ jsx2(Text2, { color: "#4f6db8", children: "CLD " }),
|
|
7361
7414
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(a.agent_name ?? a.session_id, 12).padEnd(13) }),
|
|
7362
7415
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
7363
7416
|
String(a.total_calls).padStart(4),
|
|
@@ -7409,7 +7462,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7409
7462
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7410
7463
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7411
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} ` }),
|
|
7412
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
7465
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " p pause/copy \xB7 esc menu \xB7 q quit " })
|
|
7413
7466
|
] })
|
|
7414
7467
|
] });
|
|
7415
7468
|
}
|
|
@@ -7437,7 +7490,7 @@ var init_Live = __esm({
|
|
|
7437
7490
|
});
|
|
7438
7491
|
|
|
7439
7492
|
// src/tui/panels/Policies.tsx
|
|
7440
|
-
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
7493
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
7441
7494
|
import TextInput from "ink-text-input";
|
|
7442
7495
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
7443
7496
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -7507,7 +7560,7 @@ function PoliciesPanel({ focused }) {
|
|
|
7507
7560
|
setDirty(false);
|
|
7508
7561
|
setStatus("discarded");
|
|
7509
7562
|
};
|
|
7510
|
-
|
|
7563
|
+
useInput2(
|
|
7511
7564
|
(input, key) => {
|
|
7512
7565
|
if (view === "list") {
|
|
7513
7566
|
if (key.upArrow) setPi((n) => Math.max(0, n - 1));
|
|
@@ -7682,7 +7735,7 @@ var init_Policies = __esm({
|
|
|
7682
7735
|
});
|
|
7683
7736
|
|
|
7684
7737
|
// src/tui/panels/RateLimit.tsx
|
|
7685
|
-
import { Box as Box4, Text as Text4, useInput as
|
|
7738
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
7686
7739
|
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
7687
7740
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
7688
7741
|
function ColoredSpark({ values, limit, width: width2 = 60 }) {
|
|
@@ -7730,7 +7783,7 @@ function RateLimitPanel({ focused }) {
|
|
|
7730
7783
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
7731
7784
|
}
|
|
7732
7785
|
};
|
|
7733
|
-
|
|
7786
|
+
useInput3(
|
|
7734
7787
|
(input, key) => {
|
|
7735
7788
|
const step = key.shift ? 10 : 1;
|
|
7736
7789
|
if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
|
|
@@ -7822,7 +7875,7 @@ var init_RateLimit = __esm({
|
|
|
7822
7875
|
});
|
|
7823
7876
|
|
|
7824
7877
|
// src/tui/panels/Dlp.tsx
|
|
7825
|
-
import { Box as Box5, Text as Text5, useInput as
|
|
7878
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
7826
7879
|
import TextInput2 from "ink-text-input";
|
|
7827
7880
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
7828
7881
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
@@ -7866,7 +7919,7 @@ function DlpPanel({ focused }) {
|
|
|
7866
7919
|
};
|
|
7867
7920
|
const customCount = dlp?.custom.length ?? 0;
|
|
7868
7921
|
const total = available.length + customCount;
|
|
7869
|
-
|
|
7922
|
+
useInput4(
|
|
7870
7923
|
(input, key) => {
|
|
7871
7924
|
if (!dlp) return;
|
|
7872
7925
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -8048,7 +8101,7 @@ var init_Stats = __esm({
|
|
|
8048
8101
|
});
|
|
8049
8102
|
|
|
8050
8103
|
// src/tui/panels/Audit.tsx
|
|
8051
|
-
import { Box as Box7, Text as Text7, useInput as
|
|
8104
|
+
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
8052
8105
|
import TextInput3 from "ink-text-input";
|
|
8053
8106
|
import { useState as useState6 } from "react";
|
|
8054
8107
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -8077,7 +8130,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8077
8130
|
() => view === "detail" && current?.session_id ? api.audit.list({ session_id: current.session_id, limit: 40 }) : Promise.resolve(null),
|
|
8078
8131
|
[view, current?.session_id]
|
|
8079
8132
|
);
|
|
8080
|
-
|
|
8133
|
+
useInput5(
|
|
8081
8134
|
(input, key) => {
|
|
8082
8135
|
if (view === "detail") {
|
|
8083
8136
|
if (key.leftArrow || key.escape) setView("list");
|
|
@@ -8227,7 +8280,7 @@ var init_Audit = __esm({
|
|
|
8227
8280
|
});
|
|
8228
8281
|
|
|
8229
8282
|
// src/tui/App.tsx
|
|
8230
|
-
import { Box as Box8, Text as Text8, useApp, useInput as
|
|
8283
|
+
import { Box as Box8, Text as Text8, useApp, useInput as useInput6 } from "ink";
|
|
8231
8284
|
import { useState as useState7 } from "react";
|
|
8232
8285
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8233
8286
|
function LiveHint() {
|
|
@@ -8259,7 +8312,7 @@ function App() {
|
|
|
8259
8312
|
const { exit } = useApp();
|
|
8260
8313
|
const [section, setSection] = useState7(0);
|
|
8261
8314
|
const [focus, setFocus] = useState7("nav");
|
|
8262
|
-
|
|
8315
|
+
useInput6((input, key) => {
|
|
8263
8316
|
if (focus === "nav") {
|
|
8264
8317
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
8265
8318
|
else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
|
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,7 @@ 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
129
|
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
130
130
|
import { homedir as homedir2 } from "os";
|
|
131
131
|
import { join as join2 } from "path";
|
|
@@ -534,6 +534,15 @@ function LivePanel({ active: active2 }) {
|
|
|
534
534
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
535
535
|
const lastLocalTs = useRef2(0);
|
|
536
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
|
+
);
|
|
537
546
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
538
547
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
539
548
|
}, []);
|
|
@@ -549,8 +558,9 @@ function LivePanel({ active: active2 }) {
|
|
|
549
558
|
},
|
|
550
559
|
[pushLog]
|
|
551
560
|
);
|
|
552
|
-
const paused = () => Date.now() < pausedUntil.current;
|
|
561
|
+
const paused = () => frozenRef.current || Date.now() < pausedUntil.current;
|
|
553
562
|
const pollLocal = useCallback2(() => {
|
|
563
|
+
if (frozenRef.current) return;
|
|
554
564
|
const lines = tailLines(LOCAL_LOG);
|
|
555
565
|
if (!lines.length) {
|
|
556
566
|
setLocalOn((prev) => prev === null ? false : prev);
|
|
@@ -568,10 +578,12 @@ function LivePanel({ active: active2 }) {
|
|
|
568
578
|
at,
|
|
569
579
|
tool: j.tool ?? "?",
|
|
570
580
|
decision: j.decision ?? "ALLOW",
|
|
571
|
-
permission: "",
|
|
581
|
+
permission: (j.permission ?? "").slice(0, 4),
|
|
572
582
|
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
573
583
|
dlp: !!j.dlp,
|
|
574
|
-
source: "local"
|
|
584
|
+
source: "local",
|
|
585
|
+
session: j.session_id,
|
|
586
|
+
agent: j.agent_name
|
|
575
587
|
});
|
|
576
588
|
} catch {
|
|
577
589
|
}
|
|
@@ -669,24 +681,37 @@ function LivePanel({ active: active2 }) {
|
|
|
669
681
|
}, 3e4, active2);
|
|
670
682
|
const [tick, setTick] = useState2(0);
|
|
671
683
|
useEffect2(() => {
|
|
672
|
-
if (!active2) return;
|
|
684
|
+
if (!active2 || frozen) return;
|
|
673
685
|
const t = setInterval(() => setTick((n) => n + 1), 200);
|
|
674
686
|
return () => clearInterval(t);
|
|
675
|
-
}, [active2]);
|
|
687
|
+
}, [active2, frozen]);
|
|
676
688
|
const startRef = useRef2(Date.now());
|
|
677
689
|
const cols = process.stdout.columns ?? 100;
|
|
678
690
|
const rows = process.stdout.rows ?? 30;
|
|
679
691
|
const ins = insights.data ?? {};
|
|
680
692
|
const spin = SPIN[tick % SPIN.length];
|
|
681
693
|
const points = tsData?.timeseries ?? [];
|
|
682
|
-
const
|
|
683
|
-
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";
|
|
684
709
|
const rl = ins.layers?.rateLimit;
|
|
685
710
|
const dl = ins.layers?.dlp;
|
|
686
711
|
const minuteNow = (ins.activity?.minute ?? []).slice(-1)[0]?.count ?? 0;
|
|
687
712
|
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
688
713
|
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
689
|
-
const merged =
|
|
714
|
+
const merged = mergedAll;
|
|
690
715
|
const denials = merged.filter((e) => e.decision !== "ALLOW");
|
|
691
716
|
const lastDeny = denials[denials.length - 1];
|
|
692
717
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
@@ -699,6 +724,17 @@ function LivePanel({ active: active2 }) {
|
|
|
699
724
|
const backingOff = Date.now() < pausedUntil.current;
|
|
700
725
|
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
701
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);
|
|
702
738
|
const chartH = rows >= 36 ? 6 : 4;
|
|
703
739
|
const colH = rows >= 32 ? 6 : 5;
|
|
704
740
|
const streamRows = Math.max(4, rows - 6 - chartH - colH - (localOn === false ? 1 : 0));
|
|
@@ -712,7 +748,7 @@ function LivePanel({ active: active2 }) {
|
|
|
712
748
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
713
749
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
714
750
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(Date.now() - startRef.current)} \xB7 ${hhmmss(Date.now())} \xB7 api ${latNow}ms ` }),
|
|
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 " })
|
|
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 " })
|
|
716
752
|
] }),
|
|
717
753
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
718
754
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 calls " }),
|
|
@@ -733,7 +769,7 @@ function LivePanel({ active: active2 }) {
|
|
|
733
769
|
] }),
|
|
734
770
|
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
735
771
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
|
|
736
|
-
/* @__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 }),
|
|
737
773
|
/* @__PURE__ */ jsx2(ColumnChart, { series: traffic, hot: trafficHot, height: chartH, width: leftW, color: theme.accent })
|
|
738
774
|
] }),
|
|
739
775
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: rightW, children: [
|
|
@@ -758,7 +794,7 @@ function LivePanel({ active: active2 }) {
|
|
|
758
794
|
" patterns armed"
|
|
759
795
|
] })
|
|
760
796
|
] }),
|
|
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
|
|
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" }),
|
|
762
798
|
ring ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
763
799
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "GUARD".padEnd(10) }),
|
|
764
800
|
/* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "local \u2713" }),
|
|
@@ -766,20 +802,37 @@ function LivePanel({ active: active2 }) {
|
|
|
766
802
|
] }) : null
|
|
767
803
|
] }),
|
|
768
804
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, children: [
|
|
769
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra:
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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) => {
|
|
777
829
|
const dot = sessionDot(a.status);
|
|
778
830
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
779
831
|
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
780
832
|
dot.ch,
|
|
781
833
|
" "
|
|
782
834
|
] }),
|
|
835
|
+
/* @__PURE__ */ jsx2(Text2, { color: "#4f6db8", children: "CLD " }),
|
|
783
836
|
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(a.agent_name ?? a.session_id, 12).padEnd(13) }),
|
|
784
837
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
785
838
|
String(a.total_calls).padStart(4),
|
|
@@ -831,13 +884,13 @@ function LivePanel({ active: active2 }) {
|
|
|
831
884
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
832
885
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
833
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} ` }),
|
|
834
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " esc menu \xB7 q quit " })
|
|
887
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " p pause/copy \xB7 esc menu \xB7 q quit " })
|
|
835
888
|
] })
|
|
836
889
|
] });
|
|
837
890
|
}
|
|
838
891
|
|
|
839
892
|
// src/tui/panels/Policies.tsx
|
|
840
|
-
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
893
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
841
894
|
import TextInput from "ink-text-input";
|
|
842
895
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
843
896
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -928,7 +981,7 @@ function PoliciesPanel({ focused }) {
|
|
|
928
981
|
setDirty(false);
|
|
929
982
|
setStatus("discarded");
|
|
930
983
|
};
|
|
931
|
-
|
|
984
|
+
useInput2(
|
|
932
985
|
(input, key) => {
|
|
933
986
|
if (view === "list") {
|
|
934
987
|
if (key.upArrow) setPi((n) => Math.max(0, n - 1));
|
|
@@ -1072,7 +1125,7 @@ function PoliciesPanel({ focused }) {
|
|
|
1072
1125
|
}
|
|
1073
1126
|
|
|
1074
1127
|
// src/tui/panels/RateLimit.tsx
|
|
1075
|
-
import { Box as Box4, Text as Text4, useInput as
|
|
1128
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
1076
1129
|
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
1077
1130
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1078
1131
|
var MODES = ["off", "detect", "block"];
|
|
@@ -1123,7 +1176,7 @@ function RateLimitPanel({ focused }) {
|
|
|
1123
1176
|
setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
|
|
1124
1177
|
}
|
|
1125
1178
|
};
|
|
1126
|
-
|
|
1179
|
+
useInput3(
|
|
1127
1180
|
(input, key) => {
|
|
1128
1181
|
const step = key.shift ? 10 : 1;
|
|
1129
1182
|
if (key.upArrow) setFi((n) => (n - 1 + FIELDS2.length) % FIELDS2.length);
|
|
@@ -1202,7 +1255,7 @@ function FieldRow({ label, active: active2, children }) {
|
|
|
1202
1255
|
}
|
|
1203
1256
|
|
|
1204
1257
|
// src/tui/panels/Dlp.tsx
|
|
1205
|
-
import { Box as Box5, Text as Text5, useInput as
|
|
1258
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
1206
1259
|
import TextInput2 from "ink-text-input";
|
|
1207
1260
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
1208
1261
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
@@ -1247,7 +1300,7 @@ function DlpPanel({ focused }) {
|
|
|
1247
1300
|
};
|
|
1248
1301
|
const customCount = dlp?.custom.length ?? 0;
|
|
1249
1302
|
const total = available.length + customCount;
|
|
1250
|
-
|
|
1303
|
+
useInput4(
|
|
1251
1304
|
(input, key) => {
|
|
1252
1305
|
if (!dlp) return;
|
|
1253
1306
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -1409,7 +1462,7 @@ function StatsPanel({ active: active2 }) {
|
|
|
1409
1462
|
}
|
|
1410
1463
|
|
|
1411
1464
|
// src/tui/panels/Audit.tsx
|
|
1412
|
-
import { Box as Box7, Text as Text7, useInput as
|
|
1465
|
+
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
1413
1466
|
import TextInput3 from "ink-text-input";
|
|
1414
1467
|
import { useState as useState6 } from "react";
|
|
1415
1468
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -1440,7 +1493,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
1440
1493
|
() => view === "detail" && current?.session_id ? api.audit.list({ session_id: current.session_id, limit: 40 }) : Promise.resolve(null),
|
|
1441
1494
|
[view, current?.session_id]
|
|
1442
1495
|
);
|
|
1443
|
-
|
|
1496
|
+
useInput5(
|
|
1444
1497
|
(input, key) => {
|
|
1445
1498
|
if (view === "detail") {
|
|
1446
1499
|
if (key.leftArrow || key.escape) setView("list");
|
|
@@ -1617,7 +1670,7 @@ function App() {
|
|
|
1617
1670
|
const { exit } = useApp();
|
|
1618
1671
|
const [section, setSection] = useState7(0);
|
|
1619
1672
|
const [focus, setFocus] = useState7("nav");
|
|
1620
|
-
|
|
1673
|
+
useInput6((input, key) => {
|
|
1621
1674
|
if (focus === "nav") {
|
|
1622
1675
|
if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
|
|
1623
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": {
|