@solongate/proxy 0.81.19 → 0.81.21
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/api-client/settings.d.ts +16 -0
- package/dist/commands/index.js +17 -1
- package/dist/index.js +539 -92
- package/dist/tui/hooks.d.ts +14 -0
- package/dist/tui/index.js +526 -91
- package/dist/tui/panels/Settings.d.ts +4 -0
- package/dist/tui/theme.d.ts +4 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6750,6 +6750,25 @@ function sparkline(values, width2) {
|
|
|
6750
6750
|
if (max === 0) return BLOCKS[0].repeat(v.length);
|
|
6751
6751
|
return v.map((n) => BLOCKS[Math.min(BLOCKS.length - 1, Math.round(n / max * (BLOCKS.length - 1)))]).join("");
|
|
6752
6752
|
}
|
|
6753
|
+
function prettyJson(s) {
|
|
6754
|
+
try {
|
|
6755
|
+
return JSON.stringify(JSON.parse(s), null, 2);
|
|
6756
|
+
} catch {
|
|
6757
|
+
return s;
|
|
6758
|
+
}
|
|
6759
|
+
}
|
|
6760
|
+
function wrapLines(s, width2) {
|
|
6761
|
+
const w = Math.max(8, width2);
|
|
6762
|
+
const out2 = [];
|
|
6763
|
+
for (const raw of (s ?? "").split("\n")) {
|
|
6764
|
+
if (raw.length <= w) {
|
|
6765
|
+
out2.push(raw);
|
|
6766
|
+
continue;
|
|
6767
|
+
}
|
|
6768
|
+
for (let i = 0; i < raw.length; i += w) out2.push(raw.slice(i, i + w));
|
|
6769
|
+
}
|
|
6770
|
+
return out2;
|
|
6771
|
+
}
|
|
6753
6772
|
function truncate2(s, n) {
|
|
6754
6773
|
if (!s) return "";
|
|
6755
6774
|
return s.length <= n ? s : s.slice(0, Math.max(0, n - 1)) + "\u2026";
|
|
@@ -6903,12 +6922,16 @@ __export(settings_exports, {
|
|
|
6903
6922
|
deleteWebhook: () => deleteWebhook,
|
|
6904
6923
|
getAlerts: () => getAlerts,
|
|
6905
6924
|
getGuardStatus: () => getGuardStatus,
|
|
6925
|
+
getLocalLogs: () => getLocalLogs,
|
|
6906
6926
|
getRateLimitHistory: () => getRateLimitHistory,
|
|
6907
6927
|
getSecurityLayers: () => getSecurityLayers,
|
|
6908
6928
|
getSelfProtection: () => getSelfProtection,
|
|
6909
6929
|
getWebhooks: () => getWebhooks,
|
|
6930
|
+
setAlertEnabled: () => setAlertEnabled,
|
|
6931
|
+
setLocalLogs: () => setLocalLogs,
|
|
6910
6932
|
setSecurityLayers: () => setSecurityLayers,
|
|
6911
|
-
setSelfProtection: () => setSelfProtection
|
|
6933
|
+
setSelfProtection: () => setSelfProtection,
|
|
6934
|
+
updateWebhook: () => updateWebhook
|
|
6912
6935
|
});
|
|
6913
6936
|
function getSecurityLayers() {
|
|
6914
6937
|
return request("GET", "/settings/security-layers");
|
|
@@ -6931,6 +6954,12 @@ function getSelfProtection() {
|
|
|
6931
6954
|
function setSelfProtection(enabled) {
|
|
6932
6955
|
return request("PUT", "/settings/self-protection", { body: { enabled } });
|
|
6933
6956
|
}
|
|
6957
|
+
function getLocalLogs() {
|
|
6958
|
+
return request("GET", "/settings/local-logs");
|
|
6959
|
+
}
|
|
6960
|
+
function setLocalLogs(cfg) {
|
|
6961
|
+
return request("PUT", "/settings/local-logs", { body: cfg });
|
|
6962
|
+
}
|
|
6934
6963
|
function getAlerts() {
|
|
6935
6964
|
return request("GET", "/settings/denial-alerts");
|
|
6936
6965
|
}
|
|
@@ -6940,6 +6969,9 @@ function createAlert(body) {
|
|
|
6940
6969
|
function deleteAlert(id) {
|
|
6941
6970
|
return request("DELETE", "/settings/denial-alerts", { query: { id } });
|
|
6942
6971
|
}
|
|
6972
|
+
function setAlertEnabled(id, enabled) {
|
|
6973
|
+
return request("PATCH", "/settings/denial-alerts", { query: { id }, body: { enabled } });
|
|
6974
|
+
}
|
|
6943
6975
|
function getWebhooks() {
|
|
6944
6976
|
return request("GET", "/settings/denial-webhook");
|
|
6945
6977
|
}
|
|
@@ -6949,6 +6981,9 @@ function createWebhook(body) {
|
|
|
6949
6981
|
function deleteWebhook(id) {
|
|
6950
6982
|
return request("DELETE", "/settings/denial-webhook", { query: { id } });
|
|
6951
6983
|
}
|
|
6984
|
+
function updateWebhook(id, patch) {
|
|
6985
|
+
return request("PATCH", "/settings/denial-webhook", { body: { id, ...patch } });
|
|
6986
|
+
}
|
|
6952
6987
|
var init_settings = __esm({
|
|
6953
6988
|
"src/api-client/settings.ts"() {
|
|
6954
6989
|
"use strict";
|
|
@@ -7160,6 +7195,24 @@ function usePoll(reload, intervalMs, enabled = true) {
|
|
|
7160
7195
|
return () => clearInterval(t);
|
|
7161
7196
|
}, [reload, intervalMs, enabled]);
|
|
7162
7197
|
}
|
|
7198
|
+
function useTermSize() {
|
|
7199
|
+
const read = () => ({ cols: process.stdout.columns ?? 100, rows: process.stdout.rows ?? 30 });
|
|
7200
|
+
const [size, setSize] = useState(read);
|
|
7201
|
+
useEffect(() => {
|
|
7202
|
+
const onResize = () => setSize(read());
|
|
7203
|
+
process.stdout.on("resize", onResize);
|
|
7204
|
+
return () => {
|
|
7205
|
+
process.stdout.off("resize", onResize);
|
|
7206
|
+
};
|
|
7207
|
+
}, []);
|
|
7208
|
+
return size;
|
|
7209
|
+
}
|
|
7210
|
+
function usePanelSize() {
|
|
7211
|
+
const { cols, rows } = useTermSize();
|
|
7212
|
+
const wide = cols >= 82;
|
|
7213
|
+
const shellRows = wide ? 12 : 7;
|
|
7214
|
+
return { cols: Math.max(30, cols - 22), rows: Math.max(8, rows - shellRows) };
|
|
7215
|
+
}
|
|
7163
7216
|
var init_hooks = __esm({
|
|
7164
7217
|
"src/tui/hooks.ts"() {
|
|
7165
7218
|
"use strict";
|
|
@@ -7309,6 +7362,9 @@ function LivePanel({ active: active2 }) {
|
|
|
7309
7362
|
const [detail, setDetail] = useState2(null);
|
|
7310
7363
|
const [detailCloud, setDetailCloud] = useState2([]);
|
|
7311
7364
|
const [detailScroll, setDetailScroll] = useState2(0);
|
|
7365
|
+
const [inspect, setInspect] = useState2(null);
|
|
7366
|
+
const [inspectScroll, setInspectScroll] = useState2(0);
|
|
7367
|
+
const inspectFromRef = useRef2("stream");
|
|
7312
7368
|
const seenRef = useRef2(/* @__PURE__ */ new Set());
|
|
7313
7369
|
const lastLocalTs = useRef2(0);
|
|
7314
7370
|
const pausedUntil = useRef2(0);
|
|
@@ -7319,6 +7375,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7319
7375
|
const [frozen, setFrozen] = useState2(false);
|
|
7320
7376
|
const frozenRef = useRef2(false);
|
|
7321
7377
|
frozenRef.current = frozen;
|
|
7378
|
+
const [showHelp, setShowHelp] = useState2(false);
|
|
7322
7379
|
const pushLog = useCallback2((msg, level = "ok") => {
|
|
7323
7380
|
setLog((prev) => [...prev.slice(-59), { ts: Date.now(), msg, level }]);
|
|
7324
7381
|
}, []);
|
|
@@ -7514,8 +7571,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7514
7571
|
else if (e.burst) fireAlert("sec:" + e.id, "Rate-limit burst", `BURST ${e.tool}${who}`, "warn");
|
|
7515
7572
|
}
|
|
7516
7573
|
}, [tick, active2, fireAlert]);
|
|
7517
|
-
const cols
|
|
7518
|
-
const rows = process.stdout.rows ?? 30;
|
|
7574
|
+
const { cols, rows } = useTermSize();
|
|
7519
7575
|
const ins = insights.data ?? {};
|
|
7520
7576
|
const spin = SPIN[tick % SPIN.length];
|
|
7521
7577
|
const nowMs = Date.now();
|
|
@@ -7657,12 +7713,30 @@ function LivePanel({ active: active2 }) {
|
|
|
7657
7713
|
return;
|
|
7658
7714
|
}
|
|
7659
7715
|
if (frozen) return;
|
|
7716
|
+
if (showHelp) {
|
|
7717
|
+
setShowHelp(false);
|
|
7718
|
+
return;
|
|
7719
|
+
}
|
|
7720
|
+
if (input === "?") {
|
|
7721
|
+
setShowHelp(true);
|
|
7722
|
+
return;
|
|
7723
|
+
}
|
|
7660
7724
|
if (input === "/" && mode !== "pick") {
|
|
7661
7725
|
setEditingSearch(true);
|
|
7662
7726
|
return;
|
|
7663
7727
|
}
|
|
7728
|
+
if (mode === "inspect") {
|
|
7729
|
+
if (key.leftArrow) {
|
|
7730
|
+
setMode(inspectFromRef.current);
|
|
7731
|
+
setInspect(null);
|
|
7732
|
+
} else if (key.upArrow) setInspectScroll((n) => Math.max(0, n - 1));
|
|
7733
|
+
else if (key.downArrow) setInspectScroll((n) => n + 1);
|
|
7734
|
+
else if (key.pageUp) setInspectScroll((n) => Math.max(0, n - 10));
|
|
7735
|
+
else if (key.pageDown) setInspectScroll((n) => n + 10);
|
|
7736
|
+
return;
|
|
7737
|
+
}
|
|
7664
7738
|
if (mode === "detail") {
|
|
7665
|
-
const maxD = Math.max(0, detailEntries.length -
|
|
7739
|
+
const maxD = Math.max(0, detailEntries.length - 1);
|
|
7666
7740
|
if (key.leftArrow) {
|
|
7667
7741
|
setMode("stream");
|
|
7668
7742
|
setDetail(null);
|
|
@@ -7670,6 +7744,15 @@ function LivePanel({ active: active2 }) {
|
|
|
7670
7744
|
else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
|
|
7671
7745
|
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
7672
7746
|
else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
|
|
7747
|
+
else if (key.return) {
|
|
7748
|
+
const e = detailEntries[Math.min(detailScroll, maxD)];
|
|
7749
|
+
if (e) {
|
|
7750
|
+
inspectFromRef.current = "detail";
|
|
7751
|
+
setInspect(e);
|
|
7752
|
+
setInspectScroll(0);
|
|
7753
|
+
setMode("inspect");
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7673
7756
|
return;
|
|
7674
7757
|
}
|
|
7675
7758
|
if (mode === "pick") {
|
|
@@ -7691,6 +7774,13 @@ function LivePanel({ active: active2 }) {
|
|
|
7691
7774
|
if (input === "f") {
|
|
7692
7775
|
setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
|
|
7693
7776
|
setSel(0);
|
|
7777
|
+
} else if (key.return) {
|
|
7778
|
+
if (selEntry) {
|
|
7779
|
+
inspectFromRef.current = "stream";
|
|
7780
|
+
setInspect(selEntry);
|
|
7781
|
+
setInspectScroll(0);
|
|
7782
|
+
setMode("inspect");
|
|
7783
|
+
}
|
|
7694
7784
|
} else if (input === "s") {
|
|
7695
7785
|
setPickIdx(0);
|
|
7696
7786
|
setMode("pick");
|
|
@@ -7753,6 +7843,66 @@ function LivePanel({ active: active2 }) {
|
|
|
7753
7843
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
|
|
7754
7844
|
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 " })
|
|
7755
7845
|
] });
|
|
7846
|
+
if (showHelp) {
|
|
7847
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7848
|
+
titleBar,
|
|
7849
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.accentBright, children: "LIVE \u2014 all keys" }),
|
|
7850
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: LIVE_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
|
|
7851
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.accent, children: group }),
|
|
7852
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7853
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
|
|
7854
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: desc })
|
|
7855
|
+
] }, k))
|
|
7856
|
+
] }, group)) }),
|
|
7857
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "press any key to close" })
|
|
7858
|
+
] });
|
|
7859
|
+
}
|
|
7860
|
+
if (mode === "inspect" && inspect) {
|
|
7861
|
+
const e = inspect;
|
|
7862
|
+
const bodyW = Math.max(20, cols - 4);
|
|
7863
|
+
const lines = wrapLines(prettyJson(e.detail || "(no arguments / reason recorded)"), bodyW);
|
|
7864
|
+
const bodyRows = Math.max(4, rows - 7);
|
|
7865
|
+
const maxScroll2 = Math.max(0, lines.length - bodyRows);
|
|
7866
|
+
const off = Math.min(inspectScroll, maxScroll2);
|
|
7867
|
+
const win = lines.slice(off, off + bodyRows);
|
|
7868
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7869
|
+
titleBar,
|
|
7870
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7871
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: " ENTRY " }),
|
|
7872
|
+
/* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
|
|
7873
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.accent, bold: true, children: " " + e.tool }),
|
|
7874
|
+
/* @__PURE__ */ jsx2(Text2, { color: isLoc(e) ? theme.ok : "#4f6db8", children: " " + (isLoc(e) ? "LOC" : "CLD") }),
|
|
7875
|
+
e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: " DLP!" }) : null,
|
|
7876
|
+
e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: " BURST" }) : null,
|
|
7877
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
|
|
7878
|
+
] }),
|
|
7879
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7880
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 when " }),
|
|
7881
|
+
/* @__PURE__ */ jsx2(Text2, { children: new Date(e.at).toLocaleString() }),
|
|
7882
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perm " }),
|
|
7883
|
+
/* @__PURE__ */ jsx2(Text2, { children: e.permission || "\u2014" }),
|
|
7884
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 eval " }),
|
|
7885
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
|
|
7886
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 agent " }),
|
|
7887
|
+
/* @__PURE__ */ jsx2(Text2, { children: e.agent ?? "\u2014" }),
|
|
7888
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7889
|
+
] }),
|
|
7890
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7891
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 session " }),
|
|
7892
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.session ?? "\u2014" }),
|
|
7893
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 rule " }),
|
|
7894
|
+
/* @__PURE__ */ jsx2(Text2, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
|
|
7895
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7896
|
+
] }),
|
|
7897
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "FULL CONTENT", extra: `${lines.length} lines${maxScroll2 ? ` \xB7 \u25BC${maxScroll2 - off} more \xB7 \u2191\u2193 scroll` : ""} \xB7 space copy \xB7 \u2190 back`, width: innerW }),
|
|
7898
|
+
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
7899
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7900
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " ENTRY " }),
|
|
7901
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${e.id} ` }),
|
|
7902
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
|
|
7903
|
+
] })
|
|
7904
|
+
] });
|
|
7905
|
+
}
|
|
7756
7906
|
if (mode === "detail" && detail) {
|
|
7757
7907
|
const d = detailEntries;
|
|
7758
7908
|
const allow = d.filter((e) => e.decision === "ALLOW").length;
|
|
@@ -7772,9 +7922,10 @@ function LivePanel({ active: active2 }) {
|
|
|
7772
7922
|
const last = d[0]?.at;
|
|
7773
7923
|
const tlRows = Math.max(4, rows - 10);
|
|
7774
7924
|
const tlBody = Math.max(3, tlRows - 1);
|
|
7775
|
-
const
|
|
7776
|
-
const
|
|
7777
|
-
const
|
|
7925
|
+
const dSel = Math.min(detailScroll, Math.max(0, d.length - 1));
|
|
7926
|
+
const maxStart = Math.max(0, d.length - tlBody);
|
|
7927
|
+
const start = Math.min(Math.max(0, dSel - Math.floor((tlBody - 1) / 2)), maxStart);
|
|
7928
|
+
const tl = d.slice(start, start + tlBody);
|
|
7778
7929
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
7779
7930
|
titleBar,
|
|
7780
7931
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
@@ -7812,16 +7963,16 @@ function LivePanel({ active: active2 }) {
|
|
|
7812
7963
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
|
|
7813
7964
|
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
|
|
7814
7965
|
] }),
|
|
7815
|
-
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length}
|
|
7966
|
+
/* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${dSel + 1}/${d.length} \xB7 newest first \xB7 \u2191\u2193 select \xB7 enter full entry \xB7 / search \xB7 \u2190 back`, width: innerW }),
|
|
7816
7967
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
|
|
7817
7968
|
searchRow,
|
|
7818
7969
|
tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
|
|
7819
|
-
tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
|
|
7970
|
+
tl.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: start + i === dSel }, e.id))
|
|
7820
7971
|
] }),
|
|
7821
7972
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7822
7973
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
|
|
7823
7974
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
|
|
7824
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193
|
|
7975
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 enter full entry \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
|
|
7825
7976
|
] })
|
|
7826
7977
|
] });
|
|
7827
7978
|
}
|
|
@@ -7934,7 +8085,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7934
8085
|
PaneTitle,
|
|
7935
8086
|
{
|
|
7936
8087
|
label: "TOOL STREAM",
|
|
7937
|
-
extra: `${selClamped + 1}/${visibleDesc.length}${signal !== "none" ? ` \xB7 ${signal}` : ""}${q ? " \xB7 search" : ""}
|
|
8088
|
+
extra: `${selClamped + 1}/${visibleDesc.length}${signal !== "none" ? ` \xB7 ${signal}` : ""}${q ? " \xB7 search" : ""}${filter !== "all" ? ` \xB7 source:${filter}` : ""} \xB7 enter full entry \xB7 ? all keys`,
|
|
7938
8089
|
width: innerW
|
|
7939
8090
|
}
|
|
7940
8091
|
),
|
|
@@ -7942,7 +8093,7 @@ function LivePanel({ active: active2 }) {
|
|
|
7942
8093
|
searchRow,
|
|
7943
8094
|
localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7944
8095
|
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
|
|
7945
|
-
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable:
|
|
8096
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable: dataroom \u2192 Settings (local logs) or dashboard \u2192 Settings \xB7 hooks write ~/.solongate/local-logs" })
|
|
7946
8097
|
] }) : null,
|
|
7947
8098
|
windowed.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
7948
8099
|
spin,
|
|
@@ -7953,11 +8104,11 @@ function LivePanel({ active: active2 }) {
|
|
|
7953
8104
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7954
8105
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
|
|
7955
8106
|
/* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
|
|
7956
|
-
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children:
|
|
8107
|
+
/* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` \u2191\u2193 select \xB7 enter full entry \xB7 / search \xB7 space copy \xB7 ? all keys \xB7 esc menu \xB7 q quit ` })
|
|
7957
8108
|
] })
|
|
7958
8109
|
] });
|
|
7959
8110
|
}
|
|
7960
|
-
var CONFIG, SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE;
|
|
8111
|
+
var CONFIG, SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE, LIVE_HELP;
|
|
7961
8112
|
var init_Live = __esm({
|
|
7962
8113
|
"src/tui/panels/Live.tsx"() {
|
|
7963
8114
|
"use strict";
|
|
@@ -7988,6 +8139,45 @@ var init_Live = __esm({
|
|
|
7988
8139
|
idle: { dot: "\u25D0", label: "IDLE", color: theme.warn },
|
|
7989
8140
|
ended: { dot: "\u25CB", label: "ENDED", color: theme.dim }
|
|
7990
8141
|
};
|
|
8142
|
+
LIVE_HELP = [
|
|
8143
|
+
[
|
|
8144
|
+
"Stream",
|
|
8145
|
+
[
|
|
8146
|
+
["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
|
|
8147
|
+
["enter", "open the FULL entry content"],
|
|
8148
|
+
["w", "whitelist the selected DENY (adds ALLOW rule)"],
|
|
8149
|
+
["b", "block the selected ALLOW (adds DENY rule)"],
|
|
8150
|
+
["d / x / r", "filter: denies / dlp hits / rate-limit bursts"],
|
|
8151
|
+
["f", "source filter: all \u2192 local \u2192 cloud"],
|
|
8152
|
+
["/", "live search (tool, agent, command\u2026) \xB7 enter done"],
|
|
8153
|
+
["s", "session picker"],
|
|
8154
|
+
["e", "export visible rows \u2192 ~/.solongate/live-export.jsonl"],
|
|
8155
|
+
["space", "copy mode: freeze screen for mouse selection"],
|
|
8156
|
+
["esc", "back to menu"],
|
|
8157
|
+
["q", "quit dataroom"]
|
|
8158
|
+
]
|
|
8159
|
+
],
|
|
8160
|
+
[
|
|
8161
|
+
"Entry (full content)",
|
|
8162
|
+
[
|
|
8163
|
+
["\u2191\u2193 / PgUp PgDn", "scroll the content"],
|
|
8164
|
+
["space", "copy mode (freeze, then select with mouse)"],
|
|
8165
|
+
["\u2190", "back to where you came from"]
|
|
8166
|
+
]
|
|
8167
|
+
],
|
|
8168
|
+
[
|
|
8169
|
+
"Sessions",
|
|
8170
|
+
[
|
|
8171
|
+
["\u2191\u2193 + enter", "pick & open a session (picker)"],
|
|
8172
|
+
["s or \u2190", "cancel the picker"],
|
|
8173
|
+
["\u2191\u2193", "select a timeline row (detail)"],
|
|
8174
|
+
["enter", "full entry content of the selected row"],
|
|
8175
|
+
["/", "search within the timeline"],
|
|
8176
|
+
["\u2190", "back to stream"]
|
|
8177
|
+
]
|
|
8178
|
+
],
|
|
8179
|
+
["Anywhere", [["?", "this help"], ["any key", "close this help"]]]
|
|
8180
|
+
];
|
|
7991
8181
|
}
|
|
7992
8182
|
});
|
|
7993
8183
|
|
|
@@ -8782,8 +8972,9 @@ var init_Stats = __esm({
|
|
|
8782
8972
|
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
8783
8973
|
import TextInput4 from "ink-text-input";
|
|
8784
8974
|
import { useState as useState6 } from "react";
|
|
8785
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
8975
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
8786
8976
|
function AuditPanel({ active: active2, focused }) {
|
|
8977
|
+
const { cols, rows } = usePanelSize();
|
|
8787
8978
|
const [di, setDi] = useState6(0);
|
|
8788
8979
|
const [gi, setGi] = useState6(0);
|
|
8789
8980
|
const [tool, setTool] = useState6("");
|
|
@@ -8791,6 +8982,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8791
8982
|
const [search, setSearch] = useState6("");
|
|
8792
8983
|
const [sel, setSel] = useState6(0);
|
|
8793
8984
|
const [view, setView] = useState6("list");
|
|
8985
|
+
const [detailScroll, setDetailScroll] = useState6(0);
|
|
8794
8986
|
const [editing, setEditing] = useState6(null);
|
|
8795
8987
|
const query = {
|
|
8796
8988
|
filter: DECISIONS[di],
|
|
@@ -8798,7 +8990,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8798
8990
|
tool: tool || void 0,
|
|
8799
8991
|
agent_name: agent || void 0,
|
|
8800
8992
|
search: search || void 0,
|
|
8801
|
-
limit:
|
|
8993
|
+
limit: FETCH_LIMIT
|
|
8802
8994
|
};
|
|
8803
8995
|
const auditQ = useLoader(() => api.audit.list(query), [di, gi, tool, agent, search]);
|
|
8804
8996
|
usePoll(auditQ.reload, 6e3, active2 && view === "list" && !editing);
|
|
@@ -8812,15 +9004,29 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8812
9004
|
(input, key) => {
|
|
8813
9005
|
if (view === "detail") {
|
|
8814
9006
|
if (key.leftArrow || key.escape) setView("list");
|
|
9007
|
+
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
9008
|
+
else if (key.downArrow) setDetailScroll((n) => n + 1);
|
|
9009
|
+
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
9010
|
+
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
8815
9011
|
return;
|
|
8816
9012
|
}
|
|
9013
|
+
const clearSel = () => setSel(0);
|
|
8817
9014
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
8818
9015
|
else if (key.downArrow) setSel((n) => Math.min(entries.length - 1, n + 1));
|
|
9016
|
+
else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
|
|
9017
|
+
else if (key.pageDown) setSel((n) => Math.min(entries.length - 1, n + 10));
|
|
8819
9018
|
else if (key.return || key.rightArrow) {
|
|
8820
|
-
if (current)
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
9019
|
+
if (current) {
|
|
9020
|
+
setDetailScroll(0);
|
|
9021
|
+
setView("detail");
|
|
9022
|
+
}
|
|
9023
|
+
} else if (input === "f") {
|
|
9024
|
+
setDi((n) => (n + 1) % DECISIONS.length);
|
|
9025
|
+
clearSel();
|
|
9026
|
+
} else if (input === "g") {
|
|
9027
|
+
setGi((n) => (n + 1) % SIGNALS.length);
|
|
9028
|
+
clearSel();
|
|
9029
|
+
} else if (input === "t") setEditing("tool");
|
|
8824
9030
|
else if (input === "n") setEditing("agent");
|
|
8825
9031
|
else if (input === "/") setEditing("search");
|
|
8826
9032
|
else if (input === "c") {
|
|
@@ -8829,49 +9035,64 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8829
9035
|
setTool("");
|
|
8830
9036
|
setAgent("");
|
|
8831
9037
|
setSearch("");
|
|
9038
|
+
clearSel();
|
|
8832
9039
|
}
|
|
8833
9040
|
},
|
|
8834
9041
|
{ isActive: focused && !editing }
|
|
8835
9042
|
);
|
|
8836
9043
|
if (view === "detail" && current) {
|
|
8837
9044
|
const sessionCalls = sessionQ.data?.entries ?? [];
|
|
9045
|
+
const bodyW = Math.max(20, cols - 2);
|
|
9046
|
+
const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
|
|
9047
|
+
const argLines = current.arguments_summary ? wrapLines(prettyJson(JSON.stringify(current.arguments_summary)), bodyW) : ["(no arguments recorded)"];
|
|
9048
|
+
const sessionRows = current.session_id ? Math.min(4, sessionCalls.length) + 2 : 1;
|
|
9049
|
+
const reasonShown = reasonLines.slice(0, 4);
|
|
9050
|
+
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
|
|
9051
|
+
const maxScroll = Math.max(0, argLines.length - argBudget);
|
|
9052
|
+
const off = Math.min(detailScroll, maxScroll);
|
|
9053
|
+
const argWin = argLines.slice(off, off + argBudget);
|
|
8838
9054
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
8839
9055
|
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
8840
9056
|
/* @__PURE__ */ jsx7(Text7, { color: decisionColor(current.decision), bold: true, children: current.decision }),
|
|
8841
9057
|
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: " " + current.tool_name }),
|
|
8842
9058
|
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust_level })
|
|
8843
9059
|
] }),
|
|
8844
|
-
/* @__PURE__ */ jsx7(
|
|
9060
|
+
reasonShown.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
9061
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "reason " }),
|
|
9062
|
+
/* @__PURE__ */ jsx7(Text7, { children: l.slice("reason: ".length) })
|
|
9063
|
+
] }) : /* @__PURE__ */ jsx7(Text7, { children: " " + l }) }, "r" + i)),
|
|
8845
9064
|
/* @__PURE__ */ jsx7(Detail, { label: "rule", value: current.matched_rule_id ?? "\u2014" }),
|
|
8846
9065
|
/* @__PURE__ */ jsx7(Detail, { label: "agent", value: current.agent_name ?? "\u2014" }),
|
|
8847
9066
|
/* @__PURE__ */ jsx7(Detail, { label: "session", value: current.session_id ?? "\u2014" }),
|
|
8848
9067
|
/* @__PURE__ */ jsx7(Detail, { label: "dlp", value: current.dlp_matches?.length ? current.dlp_matches.join(", ") : "none", color: current.dlp_matches?.length ? theme.bad : void 0 }),
|
|
8849
|
-
/* @__PURE__ */ jsx7(Detail, { label: "when", value: new Date(current.created_at).toLocaleString() }),
|
|
8850
|
-
|
|
8851
|
-
/* @__PURE__ */
|
|
9068
|
+
/* @__PURE__ */ jsx7(Detail, { label: "when", value: new Date(current.created_at).toLocaleString() + (current.evaluation_time_ms != null ? ` \xB7 eval ${current.evaluation_time_ms}ms` : "") }),
|
|
9069
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
|
|
9070
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
9071
|
+
current.session_id ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
8852
9072
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
8853
|
-
"Session ",
|
|
8854
|
-
|
|
9073
|
+
"Session \xB7 ",
|
|
9074
|
+
sessionCalls.length,
|
|
9075
|
+
" calls"
|
|
8855
9076
|
] }),
|
|
8856
|
-
|
|
9077
|
+
/* @__PURE__ */ jsx7(
|
|
8857
9078
|
Table,
|
|
8858
9079
|
{
|
|
8859
9080
|
columns: [
|
|
8860
9081
|
{ header: "DECISION", width: 9 },
|
|
8861
9082
|
{ header: "TOOL", width: 20 },
|
|
8862
|
-
{ header: "REASON", width:
|
|
9083
|
+
{ header: "REASON", width: Math.max(16, cols - 48) },
|
|
8863
9084
|
{ header: "WHEN", width: 6 }
|
|
8864
9085
|
],
|
|
8865
|
-
rows: sessionCalls.slice(0,
|
|
9086
|
+
rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
|
|
8866
9087
|
{ value: e.decision, color: decisionColor(e.decision) },
|
|
8867
9088
|
{ value: truncate2(e.tool_name, 20), color: theme.accent },
|
|
8868
|
-
{ value: truncate2(e.reason ?? "\u2014",
|
|
9089
|
+
{ value: truncate2(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
|
|
8869
9090
|
{ value: ago(e.created_at), dim: true }
|
|
8870
9091
|
])
|
|
8871
9092
|
}
|
|
8872
|
-
)
|
|
8873
|
-
] }),
|
|
8874
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2190 back to list" })
|
|
9093
|
+
)
|
|
9094
|
+
] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "(no session id)" }),
|
|
9095
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to list" })
|
|
8875
9096
|
] });
|
|
8876
9097
|
}
|
|
8877
9098
|
const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
@@ -8882,6 +9103,13 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8882
9103
|
/* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
8883
9104
|
/* @__PURE__ */ jsx7(Text7, { children: " " })
|
|
8884
9105
|
] });
|
|
9106
|
+
const headerRows = 4 + (editing ? 1 : 0);
|
|
9107
|
+
const listRows = Math.max(4, rows - headerRows);
|
|
9108
|
+
const selClamped = Math.min(sel, Math.max(0, entries.length - 1));
|
|
9109
|
+
const maxStart = Math.max(0, entries.length - listRows);
|
|
9110
|
+
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
9111
|
+
const windowed = entries.slice(start, start + listRows);
|
|
9112
|
+
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
8885
9113
|
return /* @__PURE__ */ jsx7(DataView, { loading: auditQ.loading && !auditQ.data, error: auditQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
8886
9114
|
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
8887
9115
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
@@ -8890,8 +9118,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8890
9118
|
chip("agent", agent || "\xB7", !!agent),
|
|
8891
9119
|
chip("search", search || "\xB7", !!search)
|
|
8892
9120
|
] }),
|
|
8893
|
-
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 \xB7 enter
|
|
8894
|
-
editing ? /* @__PURE__ */ jsxs7(Box7, {
|
|
9121
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 c clear" : "press \u2192 to browse" }),
|
|
9122
|
+
editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
8895
9123
|
/* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
|
|
8896
9124
|
editing,
|
|
8897
9125
|
": "
|
|
@@ -8900,16 +9128,15 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8900
9128
|
TextInput4,
|
|
8901
9129
|
{
|
|
8902
9130
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
8903
|
-
onChange:
|
|
9131
|
+
onChange: (v) => {
|
|
9132
|
+
(editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch)(v);
|
|
9133
|
+
setSel(0);
|
|
9134
|
+
},
|
|
8904
9135
|
onSubmit: () => setEditing(null)
|
|
8905
9136
|
}
|
|
8906
9137
|
)
|
|
8907
9138
|
] }) : null,
|
|
8908
|
-
/* @__PURE__ */ jsx7(
|
|
8909
|
-
auditQ.data?.total ?? 0,
|
|
8910
|
-
" matched \xB7 showing ",
|
|
8911
|
-
Math.min(entries.length, 12)
|
|
8912
|
-
] }) }),
|
|
9139
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: `${auditQ.data?.total ?? 0} matched \xB7 ${entries.length} loaded \xB7 ${selClamped + 1}/${entries.length}${start ? ` \xB7 \u25B2${start} newer` : ""}${start + listRows < entries.length ? ` \xB7 \u25BC${entries.length - start - listRows} older` : ""}` }),
|
|
8913
9140
|
/* @__PURE__ */ jsx7(
|
|
8914
9141
|
Table,
|
|
8915
9142
|
{
|
|
@@ -8918,22 +9145,22 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
8918
9145
|
{ header: "DECISION", width: 9 },
|
|
8919
9146
|
{ header: "TOOL", width: 18 },
|
|
8920
9147
|
{ header: "AGENT", width: 14 },
|
|
8921
|
-
{ header: "REASON", width:
|
|
9148
|
+
{ header: "REASON", width: reasonW },
|
|
8922
9149
|
{ header: "DLP", width: 4 },
|
|
8923
9150
|
{ header: "WHEN", width: 6 }
|
|
8924
9151
|
],
|
|
8925
|
-
rows:
|
|
9152
|
+
rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
|
|
8926
9153
|
}
|
|
8927
9154
|
)
|
|
8928
9155
|
] }) });
|
|
8929
9156
|
}
|
|
8930
|
-
function rowFor(e, active2) {
|
|
9157
|
+
function rowFor(e, active2, reasonW) {
|
|
8931
9158
|
return [
|
|
8932
9159
|
{ value: active2 ? "\u25B8" : "", color: theme.accentBright },
|
|
8933
9160
|
{ value: e.decision, color: decisionColor(e.decision) },
|
|
8934
9161
|
{ value: truncate2(e.tool_name, 18), color: theme.accent },
|
|
8935
9162
|
{ value: truncate2(e.agent_name ?? "\u2014", 14), dim: true },
|
|
8936
|
-
{ value: truncate2(e.reason ?? "\u2014",
|
|
9163
|
+
{ value: truncate2(e.reason ?? "\u2014", reasonW) },
|
|
8937
9164
|
{ value: e.dlp_matches?.length ? String(e.dlp_matches.length) : "0", color: e.dlp_matches?.length ? theme.bad : void 0, dim: !e.dlp_matches?.length },
|
|
8938
9165
|
{ value: ago(e.created_at), dim: true }
|
|
8939
9166
|
];
|
|
@@ -8941,10 +9168,10 @@ function rowFor(e, active2) {
|
|
|
8941
9168
|
function Detail({ label, value, color }) {
|
|
8942
9169
|
return /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
8943
9170
|
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
|
|
8944
|
-
/* @__PURE__ */ jsx7(Text7, { color, children: value })
|
|
9171
|
+
/* @__PURE__ */ jsx7(Text7, { color, wrap: "truncate", children: value })
|
|
8945
9172
|
] });
|
|
8946
9173
|
}
|
|
8947
|
-
var DECISIONS, SIGNALS;
|
|
9174
|
+
var DECISIONS, SIGNALS, FETCH_LIMIT;
|
|
8948
9175
|
var init_Audit = __esm({
|
|
8949
9176
|
"src/tui/panels/Audit.tsx"() {
|
|
8950
9177
|
"use strict";
|
|
@@ -8954,13 +9181,14 @@ var init_Audit = __esm({
|
|
|
8954
9181
|
init_theme();
|
|
8955
9182
|
DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
8956
9183
|
SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
9184
|
+
FETCH_LIMIT = 500;
|
|
8957
9185
|
}
|
|
8958
9186
|
});
|
|
8959
9187
|
|
|
8960
9188
|
// src/tui/panels/Agents.tsx
|
|
8961
9189
|
import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
|
|
8962
9190
|
import { useState as useState7 } from "react";
|
|
8963
|
-
import { Fragment as
|
|
9191
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8964
9192
|
function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
|
|
8965
9193
|
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
|
|
8966
9194
|
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
@@ -9004,7 +9232,7 @@ function AgentsPanel({ focused }) {
|
|
|
9004
9232
|
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `t${a.trust_score}` })
|
|
9005
9233
|
] }, a.session_id))
|
|
9006
9234
|
] }),
|
|
9007
|
-
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(
|
|
9235
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
9008
9236
|
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
9009
9237
|
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
|
|
9010
9238
|
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
|
|
@@ -9053,41 +9281,256 @@ var init_Agents = __esm({
|
|
|
9053
9281
|
}
|
|
9054
9282
|
});
|
|
9055
9283
|
|
|
9056
|
-
// src/tui/
|
|
9057
|
-
import { Box as Box9, Text as Text9,
|
|
9284
|
+
// src/tui/panels/Settings.tsx
|
|
9285
|
+
import { Box as Box9, Text as Text9, useInput as useInput7 } from "ink";
|
|
9286
|
+
import TextInput5 from "ink-text-input";
|
|
9058
9287
|
import { useState as useState8 } from "react";
|
|
9059
9288
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
9289
|
+
function alertBodyFor(channel) {
|
|
9290
|
+
const c2 = channel.trim();
|
|
9291
|
+
const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
|
|
9292
|
+
if (/^https?:\/\//i.test(c2)) return { ...base, slackUrl: c2 };
|
|
9293
|
+
if (c2.includes("@")) return { ...base, emails: [c2] };
|
|
9294
|
+
return { ...base, telegram: [c2] };
|
|
9295
|
+
}
|
|
9296
|
+
function SettingsPanel({ active: active2, focused }) {
|
|
9297
|
+
void active2;
|
|
9298
|
+
const { cols, rows } = usePanelSize();
|
|
9299
|
+
const [sel, setSel] = useState8(0);
|
|
9300
|
+
const [editing, setEditing] = useState8(null);
|
|
9301
|
+
const [input, setInput] = useState8("");
|
|
9302
|
+
const [confirmDel, setConfirmDel] = useState8(null);
|
|
9303
|
+
const [msg, setMsg] = useState8(null);
|
|
9304
|
+
const [busy, setBusy] = useState8(false);
|
|
9305
|
+
const localQ = useLoader(() => api.settings.getLocalLogs());
|
|
9306
|
+
const whQ = useLoader(() => api.settings.getWebhooks());
|
|
9307
|
+
const alertQ = useLoader(() => api.settings.getAlerts());
|
|
9308
|
+
const local = localQ.data;
|
|
9309
|
+
const webhooks = whQ.data?.webhooks ?? [];
|
|
9310
|
+
const alerts = alertQ.data?.rules ?? [];
|
|
9311
|
+
const rowsAll = [
|
|
9312
|
+
{ kind: "ll-enabled" },
|
|
9313
|
+
{ kind: "ll-path" },
|
|
9314
|
+
...webhooks.map((wh) => ({ kind: "wh", wh })),
|
|
9315
|
+
{ kind: "wh-add" },
|
|
9316
|
+
...alerts.map((rule) => ({ kind: "alert", rule })),
|
|
9317
|
+
{ kind: "alert-add" }
|
|
9318
|
+
];
|
|
9319
|
+
const selClamped = Math.min(sel, rowsAll.length - 1);
|
|
9320
|
+
const cur = rowsAll[selClamped];
|
|
9321
|
+
const keyOf = (r) => r.kind === "wh" ? "wh:" + r.wh.id : r.kind === "alert" ? "alert:" + r.rule.id : r.kind;
|
|
9322
|
+
const reloadAll = () => {
|
|
9323
|
+
localQ.reload();
|
|
9324
|
+
whQ.reload();
|
|
9325
|
+
alertQ.reload();
|
|
9326
|
+
};
|
|
9327
|
+
const run12 = (label, fn, reload) => {
|
|
9328
|
+
if (busy) return;
|
|
9329
|
+
setBusy(true);
|
|
9330
|
+
setMsg({ text: label + "\u2026", level: "ok" });
|
|
9331
|
+
fn().then(() => {
|
|
9332
|
+
setMsg({ text: "\u2713 " + label, level: "ok" });
|
|
9333
|
+
reload();
|
|
9334
|
+
}).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" })).finally(() => setBusy(false));
|
|
9335
|
+
};
|
|
9336
|
+
const activate = (r) => {
|
|
9337
|
+
if (r.kind === "ll-enabled") {
|
|
9338
|
+
if (!local) return;
|
|
9339
|
+
if (!local.enabled && !local.path.trim()) {
|
|
9340
|
+
setMsg({ text: "set a path first (\u2193 then enter)", level: "bad" });
|
|
9341
|
+
return;
|
|
9342
|
+
}
|
|
9343
|
+
run12(local.enabled ? "local logs disabled" : "local logs enabled", () => api.settings.setLocalLogs({ enabled: !local.enabled, path: local.path }), localQ.reload);
|
|
9344
|
+
} else if (r.kind === "ll-path") {
|
|
9345
|
+
setInput(local?.path ?? "");
|
|
9346
|
+
setEditing("path");
|
|
9347
|
+
} else if (r.kind === "wh") {
|
|
9348
|
+
run12(r.wh.enabled ? "webhook disabled" : "webhook enabled", () => api.settings.updateWebhook(r.wh.id, { enabled: !r.wh.enabled }), whQ.reload);
|
|
9349
|
+
} else if (r.kind === "wh-add") {
|
|
9350
|
+
setInput("");
|
|
9351
|
+
setEditing("wh-url");
|
|
9352
|
+
} else if (r.kind === "alert") {
|
|
9353
|
+
run12(r.rule.enabled ? "alert disabled" : "alert enabled", () => api.settings.setAlertEnabled(r.rule.id, !r.rule.enabled), alertQ.reload);
|
|
9354
|
+
} else {
|
|
9355
|
+
setInput("");
|
|
9356
|
+
setEditing("alert-channel");
|
|
9357
|
+
}
|
|
9358
|
+
};
|
|
9359
|
+
const submitInput = () => {
|
|
9360
|
+
const v = input.trim();
|
|
9361
|
+
const which = editing;
|
|
9362
|
+
setEditing(null);
|
|
9363
|
+
if (which === "path") {
|
|
9364
|
+
const enabled = (local?.enabled ?? false) && v.length > 0;
|
|
9365
|
+
run12(v ? `path saved${enabled ? "" : " (press enter on enabled to turn on)"}` : "path cleared (local logs off)", () => api.settings.setLocalLogs({ enabled, path: v }), localQ.reload);
|
|
9366
|
+
} else if (which === "wh-url") {
|
|
9367
|
+
if (!v) return;
|
|
9368
|
+
run12("webhook added", () => api.settings.createWebhook({ url: v, events: "denials" }), whQ.reload);
|
|
9369
|
+
} else if (which === "alert-channel") {
|
|
9370
|
+
if (!v) return;
|
|
9371
|
+
run12("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
|
|
9372
|
+
}
|
|
9373
|
+
};
|
|
9374
|
+
useInput7(
|
|
9375
|
+
(inp, key) => {
|
|
9376
|
+
setMsg(null);
|
|
9377
|
+
if (key.upArrow) {
|
|
9378
|
+
setSel((n) => Math.max(0, n - 1));
|
|
9379
|
+
setConfirmDel(null);
|
|
9380
|
+
} else if (key.downArrow) {
|
|
9381
|
+
setSel((n) => Math.min(rowsAll.length - 1, n + 1));
|
|
9382
|
+
setConfirmDel(null);
|
|
9383
|
+
} else if (key.return || inp === " ") activate(cur);
|
|
9384
|
+
else if (inp === "e" && cur.kind === "wh") {
|
|
9385
|
+
const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
|
|
9386
|
+
run12(`webhook events \u2192 ${next}`, () => api.settings.updateWebhook(cur.wh.id, { events: next }), whQ.reload);
|
|
9387
|
+
} else if (inp === "e" && cur.kind === "ll-path") activate(cur);
|
|
9388
|
+
else if (inp === "a") {
|
|
9389
|
+
if (cur.kind === "wh" || cur.kind === "wh-add") {
|
|
9390
|
+
setInput("");
|
|
9391
|
+
setEditing("wh-url");
|
|
9392
|
+
} else if (cur.kind === "alert" || cur.kind === "alert-add") {
|
|
9393
|
+
setInput("");
|
|
9394
|
+
setEditing("alert-channel");
|
|
9395
|
+
}
|
|
9396
|
+
} else if (inp === "d" && (cur.kind === "wh" || cur.kind === "alert")) {
|
|
9397
|
+
const k = keyOf(cur);
|
|
9398
|
+
if (confirmDel !== k) {
|
|
9399
|
+
setConfirmDel(k);
|
|
9400
|
+
setMsg({ text: "press d again to delete", level: "bad" });
|
|
9401
|
+
return;
|
|
9402
|
+
}
|
|
9403
|
+
setConfirmDel(null);
|
|
9404
|
+
if (cur.kind === "wh") run12("webhook deleted", () => api.settings.deleteWebhook(cur.wh.id), whQ.reload);
|
|
9405
|
+
else run12("alert deleted", () => api.settings.deleteAlert(cur.rule.id), alertQ.reload);
|
|
9406
|
+
} else if (inp === "r") reloadAll();
|
|
9407
|
+
},
|
|
9408
|
+
{ isActive: focused && !editing }
|
|
9409
|
+
);
|
|
9410
|
+
const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
|
|
9411
|
+
const error = localQ.error ?? whQ.error ?? alertQ.error;
|
|
9412
|
+
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
9413
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "off" });
|
|
9414
|
+
const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
9415
|
+
const listBudget = Math.max(4, rows - 8);
|
|
9416
|
+
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
9417
|
+
const inWindow = (r) => {
|
|
9418
|
+
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
9419
|
+
return i >= start && i < start + listBudget;
|
|
9420
|
+
};
|
|
9421
|
+
return /* @__PURE__ */ jsx9(DataView, { loading, error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
9422
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
|
|
9423
|
+
editing ? /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
9424
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
|
|
9425
|
+
/* @__PURE__ */ jsx9(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
9426
|
+
] }) : msg ? /* @__PURE__ */ jsx9(Text9, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
9427
|
+
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
9428
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
|
|
9429
|
+
"LOCAL LOGS ",
|
|
9430
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
|
|
9431
|
+
] }),
|
|
9432
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
9433
|
+
/* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
|
|
9434
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "enabled ".padEnd(10) }),
|
|
9435
|
+
onOff(!!local?.enabled),
|
|
9436
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " enter toggles" })
|
|
9437
|
+
] }),
|
|
9438
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
9439
|
+
/* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
|
|
9440
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "path ".padEnd(10) }),
|
|
9441
|
+
local?.path ? /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
9442
|
+
] })
|
|
9443
|
+
] }) : null,
|
|
9444
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
9445
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
|
|
9446
|
+
"WEBHOOKS ",
|
|
9447
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
9448
|
+
"\u2014 POST every matching event to your endpoint (",
|
|
9449
|
+
webhooks.length,
|
|
9450
|
+
")"
|
|
9451
|
+
] })
|
|
9452
|
+
] }),
|
|
9453
|
+
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
9454
|
+
/* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
|
|
9455
|
+
onOff(wh.enabled),
|
|
9456
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
|
|
9457
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
|
|
9458
|
+
] }, wh.id)),
|
|
9459
|
+
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9460
|
+
/* @__PURE__ */ jsx9(Text9, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
|
|
9461
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
9462
|
+
] }) : null
|
|
9463
|
+
] }),
|
|
9464
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
9465
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
|
|
9466
|
+
"ALERTS ",
|
|
9467
|
+
/* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
|
|
9468
|
+
"\u2014 notify on a signal spike (",
|
|
9469
|
+
alerts.length,
|
|
9470
|
+
")"
|
|
9471
|
+
] })
|
|
9472
|
+
] }),
|
|
9473
|
+
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
9474
|
+
/* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
9475
|
+
onOff(rule.enabled),
|
|
9476
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
|
|
9477
|
+
/* @__PURE__ */ jsx9(Text9, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
|
|
9478
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
|
|
9479
|
+
] }, rule.id)),
|
|
9480
|
+
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
9481
|
+
/* @__PURE__ */ jsx9(Text9, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
|
|
9482
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
|
|
9483
|
+
] }) : null
|
|
9484
|
+
] })
|
|
9485
|
+
] }) });
|
|
9486
|
+
}
|
|
9487
|
+
var EVENTS;
|
|
9488
|
+
var init_Settings = __esm({
|
|
9489
|
+
"src/tui/panels/Settings.tsx"() {
|
|
9490
|
+
"use strict";
|
|
9491
|
+
init_api_client();
|
|
9492
|
+
init_components();
|
|
9493
|
+
init_hooks();
|
|
9494
|
+
init_theme();
|
|
9495
|
+
EVENTS = ["denials", "allowed", "all"];
|
|
9496
|
+
}
|
|
9497
|
+
});
|
|
9498
|
+
|
|
9499
|
+
// src/tui/App.tsx
|
|
9500
|
+
import { Box as Box10, Text as Text10, useApp, useInput as useInput8 } from "ink";
|
|
9501
|
+
import { useState as useState9 } from "react";
|
|
9502
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
9060
9503
|
function LiveHint() {
|
|
9061
|
-
return /* @__PURE__ */
|
|
9062
|
-
/* @__PURE__ */
|
|
9063
|
-
/* @__PURE__ */
|
|
9064
|
-
/* @__PURE__ */
|
|
9065
|
-
/* @__PURE__ */
|
|
9504
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
9505
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
9506
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
9507
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
9508
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
9066
9509
|
"press ",
|
|
9067
|
-
/* @__PURE__ */
|
|
9510
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: "enter" }),
|
|
9068
9511
|
" to go live"
|
|
9069
9512
|
] }) })
|
|
9070
9513
|
] });
|
|
9071
9514
|
}
|
|
9072
|
-
function Banner() {
|
|
9073
|
-
const wide =
|
|
9515
|
+
function Banner({ cols }) {
|
|
9516
|
+
const wide = cols >= 82;
|
|
9074
9517
|
if (!wide) {
|
|
9075
|
-
return /* @__PURE__ */
|
|
9076
|
-
/* @__PURE__ */
|
|
9077
|
-
/* @__PURE__ */
|
|
9518
|
+
return /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
9519
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
9520
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \u2014 security control center" })
|
|
9078
9521
|
] });
|
|
9079
9522
|
}
|
|
9080
|
-
return /* @__PURE__ */
|
|
9081
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
9082
|
-
/* @__PURE__ */
|
|
9523
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
9524
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx10(Text10, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
9525
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
9083
9526
|
] });
|
|
9084
9527
|
}
|
|
9085
9528
|
function App() {
|
|
9086
9529
|
const { exit } = useApp();
|
|
9087
|
-
const [section, setSection] =
|
|
9088
|
-
const [focus, setFocus] =
|
|
9089
|
-
const [help, setHelp] =
|
|
9090
|
-
|
|
9530
|
+
const [section, setSection] = useState9(0);
|
|
9531
|
+
const [focus, setFocus] = useState9("nav");
|
|
9532
|
+
const [help, setHelp] = useState9(false);
|
|
9533
|
+
useInput8((input, key) => {
|
|
9091
9534
|
if (help) {
|
|
9092
9535
|
setHelp(false);
|
|
9093
9536
|
return;
|
|
@@ -9106,34 +9549,33 @@ function App() {
|
|
|
9106
9549
|
else if (input === "q" && SECTIONS[section].label === "Live") exit();
|
|
9107
9550
|
}
|
|
9108
9551
|
});
|
|
9109
|
-
const cols
|
|
9110
|
-
const rows = process.stdout.rows ?? 30;
|
|
9552
|
+
const { cols, rows } = useTermSize();
|
|
9111
9553
|
const current = SECTIONS[section];
|
|
9112
|
-
if (help) return /* @__PURE__ */
|
|
9554
|
+
if (help) return /* @__PURE__ */ jsx10(HelpOverlay, { cols, rows });
|
|
9113
9555
|
if (current.label === "Live" && focus === "panel") {
|
|
9114
|
-
return /* @__PURE__ */
|
|
9556
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx10(LivePanel, { active: true, focused: true }) });
|
|
9115
9557
|
}
|
|
9116
9558
|
const Panel = current.Panel;
|
|
9117
|
-
return /* @__PURE__ */
|
|
9118
|
-
/* @__PURE__ */
|
|
9119
|
-
/* @__PURE__ */
|
|
9120
|
-
/* @__PURE__ */
|
|
9121
|
-
/* @__PURE__ */
|
|
9559
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
9560
|
+
/* @__PURE__ */ jsx10(Banner, { cols }),
|
|
9561
|
+
/* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexGrow: 1, children: [
|
|
9562
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx10(Text10, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
9563
|
+
/* @__PURE__ */ jsx10(Box10, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx10(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
9122
9564
|
] }),
|
|
9123
|
-
/* @__PURE__ */
|
|
9565
|
+
/* @__PURE__ */ jsx10(Box10, { children: focus === "nav" ? /* @__PURE__ */ jsx10(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx10(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
9124
9566
|
] });
|
|
9125
9567
|
}
|
|
9126
9568
|
function HelpOverlay({ cols, rows }) {
|
|
9127
|
-
return /* @__PURE__ */
|
|
9128
|
-
/* @__PURE__ */
|
|
9129
|
-
/* @__PURE__ */
|
|
9130
|
-
/* @__PURE__ */
|
|
9131
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
9132
|
-
/* @__PURE__ */
|
|
9133
|
-
/* @__PURE__ */
|
|
9569
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
9570
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
9571
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
9572
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accent, children: group }),
|
|
9573
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
9574
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
9575
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: desc })
|
|
9134
9576
|
] }, k))
|
|
9135
9577
|
] }, group)) }),
|
|
9136
|
-
/* @__PURE__ */
|
|
9578
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "press any key to close" })
|
|
9137
9579
|
] });
|
|
9138
9580
|
}
|
|
9139
9581
|
var SECTIONS, BANNER_HEX, HELP;
|
|
@@ -9150,6 +9592,8 @@ var init_App = __esm({
|
|
|
9150
9592
|
init_Stats();
|
|
9151
9593
|
init_Audit();
|
|
9152
9594
|
init_Agents();
|
|
9595
|
+
init_Settings();
|
|
9596
|
+
init_hooks();
|
|
9153
9597
|
SECTIONS = [
|
|
9154
9598
|
{ label: "Live", Panel: LiveHint },
|
|
9155
9599
|
{ label: "Agents", Panel: AgentsPanel },
|
|
@@ -9157,14 +9601,17 @@ var init_App = __esm({
|
|
|
9157
9601
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
9158
9602
|
{ label: "DLP", Panel: DlpPanel },
|
|
9159
9603
|
{ label: "Stats", Panel: StatsPanel },
|
|
9160
|
-
{ label: "Audit", Panel: AuditPanel }
|
|
9604
|
+
{ label: "Audit", Panel: AuditPanel },
|
|
9605
|
+
{ label: "Settings", Panel: SettingsPanel }
|
|
9161
9606
|
];
|
|
9162
9607
|
BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
|
|
9163
9608
|
HELP = [
|
|
9164
9609
|
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
9165
|
-
["Live", [["\u2191\u2193", "select a stream row"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
9610
|
+
["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
|
|
9166
9611
|
["Policies", [["\u2191\u2193", "browse / select"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["v", "versions / rollback"], ["s", "save"], ["x", "discard"]]],
|
|
9167
|
-
["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]]
|
|
9612
|
+
["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]],
|
|
9613
|
+
["Audit", [["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry detail"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
|
|
9614
|
+
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
9168
9615
|
];
|
|
9169
9616
|
}
|
|
9170
9617
|
});
|
|
@@ -9175,7 +9622,7 @@ __export(tui_exports, {
|
|
|
9175
9622
|
launchTui: () => launchTui
|
|
9176
9623
|
});
|
|
9177
9624
|
import { render } from "ink";
|
|
9178
|
-
import { jsx as
|
|
9625
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
9179
9626
|
async function launchTui() {
|
|
9180
9627
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
9181
9628
|
process.stderr.write(
|
|
@@ -9189,7 +9636,7 @@ async function launchTui() {
|
|
|
9189
9636
|
}
|
|
9190
9637
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
9191
9638
|
try {
|
|
9192
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
9639
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx11(App, {}));
|
|
9193
9640
|
await waitUntilExit();
|
|
9194
9641
|
} finally {
|
|
9195
9642
|
process.stdout.write("\x1B[?1049l");
|