@solongate/proxy 0.72.0 → 0.73.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 +195 -212
- package/dist/tui/index.js +165 -182
- package/package.json +1 -1
package/dist/tui/index.js
CHANGED
|
@@ -126,10 +126,9 @@ function KeyHints({ hints }) {
|
|
|
126
126
|
|
|
127
127
|
// src/tui/panels/Live.tsx
|
|
128
128
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
129
|
-
import {
|
|
130
|
-
import { closeSync, mkdirSync, openSync, readSync, statSync, writeFileSync } from "fs";
|
|
129
|
+
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
131
130
|
import { homedir as homedir2 } from "os";
|
|
132
|
-
import {
|
|
131
|
+
import { join as join2 } from "path";
|
|
133
132
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
134
133
|
|
|
135
134
|
// src/api-client/client.ts
|
|
@@ -444,13 +443,12 @@ function usePoll(reload, intervalMs, enabled = true) {
|
|
|
444
443
|
}
|
|
445
444
|
|
|
446
445
|
// src/tui/panels/Live.tsx
|
|
447
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
446
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
448
447
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
449
448
|
var BG = "#12234f";
|
|
450
449
|
var DIM_FLOOR = "#233457";
|
|
451
450
|
var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
452
451
|
var RING = join2(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
453
|
-
var DUMP = join2(homedir2(), ".solongate", "live-stream.log");
|
|
454
452
|
var STREAM_KEEP = 100;
|
|
455
453
|
var hhmmss = (ts) => {
|
|
456
454
|
const d = new Date(ts);
|
|
@@ -476,22 +474,6 @@ function tailLines(file, maxBytes = 131072) {
|
|
|
476
474
|
return [];
|
|
477
475
|
}
|
|
478
476
|
}
|
|
479
|
-
function copyToClipboard(text) {
|
|
480
|
-
const tools = [
|
|
481
|
-
["wl-copy", []],
|
|
482
|
-
["xclip", ["-selection", "clipboard"]],
|
|
483
|
-
["xsel", ["--clipboard", "--input"]]
|
|
484
|
-
];
|
|
485
|
-
for (const [cmd, args] of tools) {
|
|
486
|
-
try {
|
|
487
|
-
const r = spawnSync(cmd, args, { input: text, timeout: 2e3 });
|
|
488
|
-
if (r.status === 0) return cmd;
|
|
489
|
-
} catch {
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf-8").toString("base64")}\x07`);
|
|
493
|
-
return null;
|
|
494
|
-
}
|
|
495
477
|
function cloudItem(e) {
|
|
496
478
|
return {
|
|
497
479
|
id: "c:" + e.id,
|
|
@@ -501,6 +483,7 @@ function cloudItem(e) {
|
|
|
501
483
|
permission: (e.permission ?? "").slice(0, 4),
|
|
502
484
|
detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
|
|
503
485
|
dlp: !!e.dlp_matches?.length,
|
|
486
|
+
burst: !!e.rate_limit_burst,
|
|
504
487
|
source: "cloud",
|
|
505
488
|
session: e.session_id ?? void 0,
|
|
506
489
|
agent: e.agent_name ?? void 0,
|
|
@@ -556,6 +539,12 @@ function PaneTitle({ label, extra, width }) {
|
|
|
556
539
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width - used)) })
|
|
557
540
|
] });
|
|
558
541
|
}
|
|
542
|
+
function Stat({ label, value, color }) {
|
|
543
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginRight: 3, children: [
|
|
544
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: label }),
|
|
545
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color, children: String(value) })
|
|
546
|
+
] });
|
|
547
|
+
}
|
|
559
548
|
function StreamLine({ e, loc }) {
|
|
560
549
|
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
561
550
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
@@ -571,11 +560,14 @@ function StreamLine({ e, loc }) {
|
|
|
571
560
|
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : theme.dim, children: (e.evalMs != null ? `${e.evalMs}ms` : "\u2014").padEnd(7) }),
|
|
572
561
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate(e.agent ?? "-", 11).padEnd(12) }),
|
|
573
562
|
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: "DLP! " }) : null,
|
|
563
|
+
e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "BURST " }) : null,
|
|
574
564
|
e.rule && e.decision !== "ALLOW" ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: truncate(e.rule, 14) + " " }) : null,
|
|
575
565
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.detail })
|
|
576
566
|
] });
|
|
577
567
|
}
|
|
578
568
|
var FILTERS = ["all", "local", "cloud"];
|
|
569
|
+
var SESS_FILTERS = ["all", "active", "idle", "ended"];
|
|
570
|
+
var sessStatus = (lastAt, now) => now - lastAt < 9e4 ? "active" : now - lastAt < 3e5 ? "idle" : "ended";
|
|
579
571
|
function LivePanel({ active: active2 }) {
|
|
580
572
|
const [s, setS] = useState2(null);
|
|
581
573
|
const [tsData, setTsData] = useState2(null);
|
|
@@ -587,12 +579,11 @@ function LivePanel({ active: active2 }) {
|
|
|
587
579
|
const [log, setLog] = useState2([]);
|
|
588
580
|
const [filter, setFilter] = useState2("all");
|
|
589
581
|
const [scroll, setScroll] = useState2(0);
|
|
590
|
-
const [toast, setToast] = useState2(null);
|
|
591
582
|
const [mode, setMode] = useState2("stream");
|
|
583
|
+
const [sessFilter, setSessFilter] = useState2("all");
|
|
592
584
|
const [pickIdx, setPickIdx] = useState2(0);
|
|
593
|
-
const [detail, setDetail] = useState2(null);
|
|
594
585
|
const [detailCloud, setDetailCloud] = useState2([]);
|
|
595
|
-
const [
|
|
586
|
+
const [tlScroll, setTlScroll] = useState2(0);
|
|
596
587
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
597
588
|
const lastLocalTs = useRef2(0);
|
|
598
589
|
const pausedUntil = useRef2(0);
|
|
@@ -637,6 +628,7 @@ function LivePanel({ active: active2 }) {
|
|
|
637
628
|
permission: (j.permission ?? "").slice(0, 4),
|
|
638
629
|
detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
|
|
639
630
|
dlp: !!j.dlp,
|
|
631
|
+
burst: !!j.rate_limit_burst,
|
|
640
632
|
source: "local",
|
|
641
633
|
session: j.session_id,
|
|
642
634
|
agent: j.agent_name,
|
|
@@ -741,19 +733,6 @@ function LivePanel({ active: active2 }) {
|
|
|
741
733
|
usePoll(() => {
|
|
742
734
|
if (!paused()) guard.reload();
|
|
743
735
|
}, 6e4, active2);
|
|
744
|
-
useEffect2(() => {
|
|
745
|
-
if (!detail) return;
|
|
746
|
-
setDetailCloud([]);
|
|
747
|
-
setDetailScroll(0);
|
|
748
|
-
let alive = true;
|
|
749
|
-
api.audit.list({ session_id: detail.id, limit: 100 }).then((r) => {
|
|
750
|
-
if (alive) setDetailCloud(r.entries.map(cloudItem));
|
|
751
|
-
}).catch(() => {
|
|
752
|
-
});
|
|
753
|
-
return () => {
|
|
754
|
-
alive = false;
|
|
755
|
-
};
|
|
756
|
-
}, [detail]);
|
|
757
736
|
const [tick, setTick] = useState2(0);
|
|
758
737
|
useEffect2(() => {
|
|
759
738
|
if (!active2 || frozen) return;
|
|
@@ -773,67 +752,68 @@ function LivePanel({ active: active2 }) {
|
|
|
773
752
|
}
|
|
774
753
|
const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
|
|
775
754
|
const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
|
|
776
|
-
const points = tsData?.timeseries ?? [];
|
|
777
|
-
const nowMin = Math.floor(nowMs / 6e4);
|
|
778
|
-
const minuteCounts = new Array(60).fill(0);
|
|
779
|
-
const minuteHot = new Array(60).fill(false);
|
|
780
|
-
for (const e of mergedAll) {
|
|
781
|
-
const idx = 59 - (nowMin - Math.floor(e.at / 6e4));
|
|
782
|
-
if (idx >= 0 && idx < 60) {
|
|
783
|
-
minuteCounts[idx]++;
|
|
784
|
-
if (e.decision !== "ALLOW") minuteHot[idx] = true;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
const localTraffic = localOn === true && minuteCounts.some((c2) => c2 > 0);
|
|
788
|
-
const traffic = localTraffic ? minuteCounts : points.map((p) => p.total);
|
|
789
|
-
const trafficHot = localTraffic ? minuteHot : points.map((p) => p.denied > 0);
|
|
790
|
-
const trafficLabel = localTraffic ? "calls/min \xB7 60m \xB7 local+cloud" : "24h \xB7 cloud";
|
|
791
|
-
const rl = ins.layers?.rateLimit;
|
|
792
|
-
const dl = ins.layers?.dlp;
|
|
793
|
-
const gh = ins.layers?.ghost;
|
|
794
|
-
const minuteNow = minuteCounts[59] ?? 0;
|
|
795
|
-
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
796
|
-
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
797
|
-
const denialsAll = mergedAll.filter((e) => e.decision !== "ALLOW");
|
|
798
|
-
const lastDeny = denialsAll[denialsAll.length - 1];
|
|
799
|
-
const toolCounts = /* @__PURE__ */ new Map();
|
|
800
|
-
for (const e of mergedAll) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
801
|
-
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
802
|
-
const latNow = lat[lat.length - 1] ?? 0;
|
|
803
|
-
const sortedLat = [...lat].sort((a, b) => a - b);
|
|
804
|
-
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
805
|
-
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
806
|
-
const backingOff = Date.now() < pausedUntil.current;
|
|
807
|
-
const sess = (sessions.data?.agents ?? []).slice().sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at));
|
|
808
|
-
const sessCounts = sessions.data?.counts;
|
|
809
755
|
const localSess = /* @__PURE__ */ new Map();
|
|
810
756
|
for (const e of localBuf) {
|
|
811
757
|
if (!e.session) continue;
|
|
812
|
-
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, lastAt: 0 };
|
|
758
|
+
const cur = localSess.get(e.session) ?? { agent: e.agent ?? "local agent", calls: 0, denies: 0, dlp: 0, lastAt: 0 };
|
|
813
759
|
cur.calls++;
|
|
814
760
|
if (e.decision !== "ALLOW") cur.denies++;
|
|
761
|
+
if (e.dlp) cur.dlp++;
|
|
815
762
|
cur.lastAt = Math.max(cur.lastAt, e.at);
|
|
816
763
|
if (e.agent) cur.agent = e.agent;
|
|
817
764
|
localSess.set(e.session, cur);
|
|
818
765
|
}
|
|
819
|
-
const
|
|
820
|
-
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
821
|
-
...
|
|
766
|
+
const combinedSessAll = [
|
|
767
|
+
...[...localSess.entries()].sort((a, b) => b[1].lastAt - a[1].lastAt).map(([id, v]) => ({ source: "local", id, agent: v.agent, calls: v.calls, denies: v.denies, dlp: v.dlp, lastAt: v.lastAt, isMe: ring?.session === id })),
|
|
768
|
+
...(sessions.data?.agents ?? []).filter((a) => !localSess.has(a.session_id)).sort((a, b) => Date.parse(b.last_seen_at) - Date.parse(a.last_seen_at)).map((a) => ({
|
|
822
769
|
source: "cloud",
|
|
823
770
|
id: a.session_id,
|
|
824
771
|
agent: a.agent_name ?? a.session_id,
|
|
825
772
|
calls: a.total_calls,
|
|
826
773
|
denies: a.denied_calls,
|
|
774
|
+
dlp: a.dlp_events,
|
|
827
775
|
lastAt: Date.parse(a.last_seen_at),
|
|
828
776
|
trust: a.trust_score,
|
|
829
777
|
isMe: false
|
|
830
778
|
}))
|
|
831
779
|
];
|
|
780
|
+
const combinedSess = sessFilter === "all" ? combinedSessAll : combinedSessAll.filter((r) => sessStatus(r.lastAt, nowMs) === sessFilter);
|
|
832
781
|
const localSessIds = new Set(localSess.keys());
|
|
833
782
|
if (ring?.session) localSessIds.add(ring.session);
|
|
834
783
|
const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
|
|
835
784
|
const filtered = filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e));
|
|
836
785
|
const visibleDesc = filtered.slice(-STREAM_KEEP).reverse();
|
|
786
|
+
const points = tsData?.timeseries ?? [];
|
|
787
|
+
const nowSlot = Math.floor(nowMs / 1e4);
|
|
788
|
+
const slotCounts = new Array(60).fill(0);
|
|
789
|
+
const slotHot = new Array(60).fill(false);
|
|
790
|
+
for (const e of mergedAll) {
|
|
791
|
+
const idx = 59 - (nowSlot - Math.floor(e.at / 1e4));
|
|
792
|
+
if (idx >= 0 && idx < 60) {
|
|
793
|
+
slotCounts[idx]++;
|
|
794
|
+
if (e.decision !== "ALLOW") slotHot[idx] = true;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const localTraffic = localOn === true && slotCounts.some((c2) => c2 > 0);
|
|
798
|
+
const traffic = localTraffic ? slotCounts : points.map((p) => p.total);
|
|
799
|
+
const trafficHot = localTraffic ? slotHot : points.map((p) => p.denied > 0);
|
|
800
|
+
const trafficLabel = localTraffic ? "calls/10s \xB7 last 10m" : "24h \xB7 cloud";
|
|
801
|
+
const rl = ins.layers?.rateLimit;
|
|
802
|
+
const dl = ins.layers?.dlp;
|
|
803
|
+
const gh = ins.layers?.ghost;
|
|
804
|
+
const minuteNow = mergedAll.filter((e) => nowMs - e.at < 6e4).length;
|
|
805
|
+
const dlpBars = (ins.dlpByPattern ?? []).slice(0, 2);
|
|
806
|
+
const maxDlpBar = dlpBars[0]?.count ?? 1;
|
|
807
|
+
const denialsAll = mergedAll.filter((e) => e.decision !== "ALLOW");
|
|
808
|
+
const lastDeny = denialsAll[denialsAll.length - 1];
|
|
809
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
810
|
+
for (const e of mergedAll) toolCounts.set(e.tool, (toolCounts.get(e.tool) ?? 0) + 1);
|
|
811
|
+
const topTools = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
812
|
+
const latNow = lat[lat.length - 1] ?? 0;
|
|
813
|
+
const sortedLat = [...lat].sort((a, b) => a - b);
|
|
814
|
+
const latMed = sortedLat.length ? sortedLat[Math.floor(sortedLat.length / 2)] : 0;
|
|
815
|
+
const latHotAt = Math.max(2e3, latMed * 2.5);
|
|
816
|
+
const backingOff = Date.now() < pausedUntil.current;
|
|
837
817
|
const chartH = rows >= 36 ? 6 : 4;
|
|
838
818
|
const colH = rows >= 32 ? 7 : 5;
|
|
839
819
|
const streamRows = Math.max(4, rows - 5 - (1 + chartH) - colH);
|
|
@@ -841,14 +821,25 @@ function LivePanel({ active: active2 }) {
|
|
|
841
821
|
const leftW = Math.floor(innerW * 0.55);
|
|
842
822
|
const rightW = innerW - leftW - 2;
|
|
843
823
|
const colW = Math.max(20, Math.floor((innerW - 4) / 3));
|
|
844
|
-
const pickable = combinedSess.slice(0, colH - 1);
|
|
845
824
|
const maxScroll = Math.max(0, visibleDesc.length - streamRows);
|
|
846
825
|
const clampedScroll = Math.min(scroll, maxScroll);
|
|
847
826
|
const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamRows);
|
|
848
|
-
const
|
|
827
|
+
const selIdx = Math.min(pickIdx, Math.max(0, combinedSess.length - 1));
|
|
828
|
+
const selected = combinedSess[selIdx];
|
|
829
|
+
useEffect2(() => {
|
|
830
|
+
if (mode !== "sessions" || !selected?.id) return;
|
|
831
|
+
setTlScroll(0);
|
|
832
|
+
const t = setTimeout(() => {
|
|
833
|
+
api.audit.list({ session_id: selected.id, limit: 100 }).then((r) => {
|
|
834
|
+
if (!frozenRef.current) setDetailCloud(r.entries.map(cloudItem));
|
|
835
|
+
}).catch(() => setDetailCloud([]));
|
|
836
|
+
}, 400);
|
|
837
|
+
return () => clearTimeout(t);
|
|
838
|
+
}, [mode, selected?.id]);
|
|
839
|
+
const detailEntries = selected ? (() => {
|
|
849
840
|
const seen = /* @__PURE__ */ new Set();
|
|
850
841
|
const all = [];
|
|
851
|
-
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session ===
|
|
842
|
+
for (const e of [...detailCloud, ...mergedAll.filter((x) => x.session === selected.id)]) {
|
|
852
843
|
const key = `${e.at}:${e.tool}:${e.decision}`;
|
|
853
844
|
if (seen.has(key)) continue;
|
|
854
845
|
seen.add(key);
|
|
@@ -858,63 +849,37 @@ function LivePanel({ active: active2 }) {
|
|
|
858
849
|
})() : [];
|
|
859
850
|
useInput(
|
|
860
851
|
(input, key) => {
|
|
861
|
-
if (mode === "detail") {
|
|
862
|
-
const maxD = Math.max(0, detailEntries.length - (rows - 10));
|
|
863
|
-
if (key.leftArrow) {
|
|
864
|
-
setMode("stream");
|
|
865
|
-
setDetail(null);
|
|
866
|
-
} else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
867
|
-
else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
|
|
868
|
-
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
869
|
-
else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
if (mode === "pick") {
|
|
873
|
-
if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
874
|
-
else if (key.downArrow) setPickIdx((n) => Math.min(pickable.length - 1, n + 1));
|
|
875
|
-
else if (key.return) {
|
|
876
|
-
const row = pickable[pickIdx];
|
|
877
|
-
if (row) {
|
|
878
|
-
setDetail(row);
|
|
879
|
-
setMode("detail");
|
|
880
|
-
}
|
|
881
|
-
} else if (input === "s" || key.leftArrow) setMode("stream");
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
852
|
if (input === " ") {
|
|
885
853
|
setFrozen((f) => !f);
|
|
886
854
|
return;
|
|
887
855
|
}
|
|
888
856
|
if (frozen) return;
|
|
857
|
+
if (mode === "sessions") {
|
|
858
|
+
if (key.leftArrow) {
|
|
859
|
+
setMode("stream");
|
|
860
|
+
setDetailCloud([]);
|
|
861
|
+
} else if (key.upArrow) setPickIdx((n) => Math.max(0, n - 1));
|
|
862
|
+
else if (key.downArrow) setPickIdx((n) => Math.min(combinedSess.length - 1, n + 1));
|
|
863
|
+
else if (input === "f") {
|
|
864
|
+
setSessFilter((f) => SESS_FILTERS[(SESS_FILTERS.indexOf(f) + 1) % SESS_FILTERS.length]);
|
|
865
|
+
setPickIdx(0);
|
|
866
|
+
} else if (key.pageUp) setTlScroll((n) => Math.max(0, n - 10));
|
|
867
|
+
else if (key.pageDown) setTlScroll((n) => n + 10);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
889
870
|
if (input === "f") {
|
|
890
871
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
891
872
|
setScroll(0);
|
|
892
873
|
} else if (input === "s") {
|
|
893
874
|
setPickIdx(0);
|
|
894
|
-
setMode("
|
|
875
|
+
setMode("sessions");
|
|
895
876
|
} else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
|
|
896
877
|
else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
|
|
897
878
|
else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
|
|
898
879
|
else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
|
|
899
|
-
else if (input === "c") {
|
|
900
|
-
const dump = visibleDesc.map(
|
|
901
|
-
(e) => `[${new Date(e.at).toISOString()}] ${e.source.toUpperCase().padEnd(5)} ${e.decision.padEnd(6)} ${e.tool.padEnd(16)} ${e.permission.padEnd(5)} ${e.evalMs != null ? String(e.evalMs).padStart(5) + "ms " : " "}${(e.agent ?? "-").padEnd(14)} ${e.session ? e.session.slice(0, 8) : "- "} ${e.detail}`
|
|
902
|
-
).join("\n");
|
|
903
|
-
try {
|
|
904
|
-
mkdirSync(dirname(DUMP), { recursive: true });
|
|
905
|
-
writeFileSync(DUMP, dump + "\n");
|
|
906
|
-
} catch {
|
|
907
|
-
}
|
|
908
|
-
const via = copyToClipboard(dump);
|
|
909
|
-
setToast(
|
|
910
|
-
via ? { msg: `\u2713 ${visibleDesc.length} lines in clipboard (${via})`, until: Date.now() + 4e3 } : { msg: "\u2717 no clipboard tool \u2014 install wl-clipboard or xclip", until: Date.now() + 6e3 }
|
|
911
|
-
);
|
|
912
|
-
}
|
|
913
880
|
},
|
|
914
881
|
{ isActive: active2 }
|
|
915
882
|
);
|
|
916
|
-
const sessionDot = (st) => st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
917
|
-
void sessionDot;
|
|
918
883
|
if (s === null && localOn === null) {
|
|
919
884
|
return /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
920
885
|
spin,
|
|
@@ -924,72 +889,91 @@ function LivePanel({ active: active2 }) {
|
|
|
924
889
|
const titleBar = /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
925
890
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#1432A0", color: "#AAC8FA", bold: true, children: " SOLONGATE LIVE " }),
|
|
926
891
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
927
|
-
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space 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 " })
|
|
928
|
-
toast && nowMs < toast.until ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: ` ${toast.msg} ` }) : null
|
|
892
|
+
frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space 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 " })
|
|
929
893
|
] });
|
|
930
|
-
if (mode === "
|
|
894
|
+
if (mode === "sessions") {
|
|
895
|
+
const listW = Math.min(44, Math.floor(innerW * 0.38));
|
|
896
|
+
const detailW = innerW - listW - 2;
|
|
897
|
+
const bodyH = rows - 4;
|
|
898
|
+
const listRows = bodyH - 1;
|
|
931
899
|
const d = detailEntries;
|
|
932
900
|
const allow = d.filter((e) => e.decision === "ALLOW").length;
|
|
933
901
|
const deny = d.length - allow;
|
|
934
902
|
const dlpN = d.filter((e) => e.dlp).length;
|
|
903
|
+
const burstN = d.filter((e) => e.burst).length;
|
|
935
904
|
const evals = d.filter((e) => e.evalMs != null).map((e) => e.evalMs);
|
|
936
905
|
const evalAvg = evals.length ? Math.round(evals.reduce((a, b) => a + b, 0) / evals.length) : null;
|
|
937
906
|
const tCounts = /* @__PURE__ */ new Map();
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
|
|
941
|
-
if (e.permission) pCounts.set(e.permission, (pCounts.get(e.permission) ?? 0) + 1);
|
|
942
|
-
}
|
|
943
|
-
const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
944
|
-
const perms = [...pCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
907
|
+
for (const e of d) tCounts.set(e.tool, (tCounts.get(e.tool) ?? 0) + 1);
|
|
908
|
+
const top = [...tCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
945
909
|
const first = d[d.length - 1]?.at;
|
|
946
910
|
const last = d[0]?.at;
|
|
947
|
-
const tlRows = Math.max(
|
|
948
|
-
const
|
|
949
|
-
const
|
|
950
|
-
const tl = d.slice(
|
|
911
|
+
const tlRows = Math.max(3, bodyH - 8);
|
|
912
|
+
const maxTl = Math.max(0, d.length - tlRows);
|
|
913
|
+
const tScroll = Math.min(tlScroll, maxTl);
|
|
914
|
+
const tl = d.slice(tScroll, tScroll + tlRows);
|
|
951
915
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
952
916
|
titleBar,
|
|
953
|
-
/* @__PURE__ */
|
|
954
|
-
|
|
955
|
-
/* @__PURE__ */
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
917
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: `${combinedSess.length} shown \xB7 filter ${sessFilter} (f) \xB7 \u2191\u2193 select \xB7 PgUp/Dn timeline \xB7 \u2190 back`, width: innerW }),
|
|
918
|
+
/* @__PURE__ */ jsxs2(Box2, { height: bodyH, children: [
|
|
919
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
|
|
920
|
+
combinedSess.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
921
|
+
"no sessions",
|
|
922
|
+
sessFilter !== "all" ? ` (${sessFilter})` : ""
|
|
923
|
+
] }) : null,
|
|
924
|
+
combinedSess.slice(0, listRows).map((r, i) => {
|
|
925
|
+
const st = sessStatus(r.lastAt, nowMs);
|
|
926
|
+
const dot = st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
927
|
+
const sel = i === selIdx;
|
|
928
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel, children: [
|
|
929
|
+
/* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : dot.color, children: [
|
|
930
|
+
sel ? "\u25B8" : dot.ch,
|
|
931
|
+
" "
|
|
932
|
+
] }),
|
|
933
|
+
/* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
|
|
934
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate(r.isMe ? "this machine" : r.agent, 13).padEnd(14) }),
|
|
935
|
+
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
936
|
+
String(r.calls).padStart(4),
|
|
937
|
+
"c "
|
|
938
|
+
] }),
|
|
939
|
+
/* @__PURE__ */ jsxs2(Text2, { color: r.denies ? theme.bad : theme.dim, children: [
|
|
940
|
+
String(r.denies).padStart(3),
|
|
941
|
+
"d "
|
|
942
|
+
] }),
|
|
943
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(r.lastAt) })
|
|
944
|
+
] }, r.id);
|
|
945
|
+
})
|
|
946
|
+
] }),
|
|
947
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", width: detailW, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "select a session to see its run timeline" }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
948
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
949
|
+
/* @__PURE__ */ jsx2(Text2, { color: sessStatus(selected.lastAt, nowMs) === "active" ? theme.ok : theme.dim, children: sessStatus(selected.lastAt, nowMs) === "active" ? "\u25CF " : "\u25CB " }),
|
|
950
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: truncate(selected.isMe ? selected.agent + " (this machine)" : selected.agent, 30) }),
|
|
951
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " + selected.id })
|
|
952
|
+
] }),
|
|
953
|
+
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
954
|
+
/* @__PURE__ */ jsx2(Stat, { label: "Total", value: d.length }),
|
|
955
|
+
/* @__PURE__ */ jsx2(Stat, { label: "Allowed", value: allow, color: theme.ok }),
|
|
956
|
+
/* @__PURE__ */ jsx2(Stat, { label: "Denied", value: deny, color: deny ? theme.bad : void 0 }),
|
|
957
|
+
/* @__PURE__ */ jsx2(Stat, { label: "DLP", value: dlpN, color: dlpN ? theme.bad : void 0 }),
|
|
958
|
+
/* @__PURE__ */ jsx2(Stat, { label: "Rate limit", value: burstN, color: burstN ? theme.warn : void 0 }),
|
|
959
|
+
/* @__PURE__ */ jsx2(Stat, { label: "Avg eval", value: evalAvg != null ? `${evalAvg}ms` : "\u2014" }),
|
|
960
|
+
selected.trust != null ? /* @__PURE__ */ jsx2(Stat, { label: "Trust", value: `${selected.trust}/100`, color: theme.accent }) : null
|
|
961
|
+
] }),
|
|
962
|
+
/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: `first ${first ? `${hhmmss(first)} (${ago(first)} ago)` : "\u2014"} \xB7 last ${last ? `${hhmmss(last)} (${ago(last)} ago)` : "\u2014"} \xB7 tools ${top.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"}` }),
|
|
963
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} events \xB7 newest first${tScroll ? ` \xB7 \u25BC${tScroll}` : ""}`, width: detailW }),
|
|
964
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", overflow: "hidden", flexGrow: 1, children: [
|
|
965
|
+
tl.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
966
|
+
spin,
|
|
967
|
+
" loading history\u2026"
|
|
968
|
+
] }) : null,
|
|
969
|
+
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
970
|
+
] })
|
|
971
|
+
] }) })
|
|
988
972
|
] }),
|
|
989
973
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
990
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: "
|
|
991
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${
|
|
992
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193
|
|
974
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSIONS " }),
|
|
975
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${combinedSess.length} sessions \xB7 space copy-mode ` }),
|
|
976
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 f status \xB7 \u2190 back \xB7 esc menu " })
|
|
993
977
|
] })
|
|
994
978
|
] });
|
|
995
979
|
}
|
|
@@ -1011,7 +995,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1011
995
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 ghost " }),
|
|
1012
996
|
/* @__PURE__ */ jsx2(Text2, { color: gh?.mode === "on" ? theme.ok : theme.dim, children: gh?.mode ?? "?" }),
|
|
1013
997
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 sess " }),
|
|
1014
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children:
|
|
998
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: String(combinedSessAll.length) }),
|
|
1015
999
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 hooks " }),
|
|
1016
1000
|
/* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
|
|
1017
1001
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
@@ -1050,14 +1034,13 @@ function LivePanel({ active: active2 }) {
|
|
|
1050
1034
|
] })
|
|
1051
1035
|
] }),
|
|
1052
1036
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
|
|
1053
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra:
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
sel ? "\u25B8" : freshDot ? "\u25CF" : "\u25CB",
|
|
1037
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: "press s to inspect", width: colW }),
|
|
1038
|
+
combinedSessAll.slice(0, colH - 1).map((r) => {
|
|
1039
|
+
const st = sessStatus(r.lastAt, nowMs);
|
|
1040
|
+
const dot = st === "active" ? { ch: "\u25CF", color: theme.ok } : st === "idle" ? { ch: "\u25CC", color: theme.warn } : { ch: "\u25CB", color: theme.dim };
|
|
1041
|
+
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", bold: r.isMe, children: [
|
|
1042
|
+
/* @__PURE__ */ jsxs2(Text2, { color: dot.color, children: [
|
|
1043
|
+
dot.ch,
|
|
1061
1044
|
" "
|
|
1062
1045
|
] }),
|
|
1063
1046
|
/* @__PURE__ */ jsx2(Text2, { color: r.source === "local" ? theme.ok : "#4f6db8", children: r.source === "local" ? "LOC " : "CLD " }),
|
|
@@ -1070,7 +1053,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1070
1053
|
String(r.denies).padStart(3),
|
|
1071
1054
|
"d "
|
|
1072
1055
|
] }),
|
|
1073
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children:
|
|
1056
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: ago(r.lastAt) })
|
|
1074
1057
|
] }, r.id);
|
|
1075
1058
|
})
|
|
1076
1059
|
] }),
|
|
@@ -1090,7 +1073,7 @@ function LivePanel({ active: active2 }) {
|
|
|
1090
1073
|
PaneTitle,
|
|
1091
1074
|
{
|
|
1092
1075
|
label: "TOOL STREAM",
|
|
1093
|
-
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7
|
|
1076
|
+
extra: `newest first \xB7 last ${STREAM_KEEP} \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f filter \xB7 \u2191\u2193 scroll \xB7 s sessions`,
|
|
1094
1077
|
width: innerW
|
|
1095
1078
|
}
|
|
1096
1079
|
),
|
|
@@ -1107,8 +1090,8 @@ function LivePanel({ active: active2 }) {
|
|
|
1107
1090
|
] }),
|
|
1108
1091
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1109
1092
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
1110
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${
|
|
1111
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7
|
|
1093
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudDeduped.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
1094
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
|
|
1112
1095
|
] })
|
|
1113
1096
|
] });
|
|
1114
1097
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.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": {
|