@solongate/proxy 0.81.23 → 0.81.24
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 +569 -449
- package/dist/tui/index.js +517 -393
- package/dist/tui/local-log.d.ts +22 -0
- package/package.json +1 -1
package/dist/tui/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/tui/App.tsx
|
|
11
|
-
import { Box as
|
|
11
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
|
|
12
12
|
import { useState as useState9 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/cli-utils.ts
|
|
@@ -169,15 +169,50 @@ function KeyHints({ hints }) {
|
|
|
169
169
|
// src/tui/panels/Live.tsx
|
|
170
170
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
171
171
|
import TextInput from "ink-text-input";
|
|
172
|
-
import {
|
|
173
|
-
import { homedir as
|
|
174
|
-
import { join as
|
|
172
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
173
|
+
import { homedir as homedir4 } from "os";
|
|
174
|
+
import { join as join4 } from "path";
|
|
175
|
+
|
|
176
|
+
// src/tui/local-log.ts
|
|
177
|
+
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
178
|
+
import { homedir as homedir2 } from "os";
|
|
179
|
+
import { join as join2 } from "path";
|
|
180
|
+
var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
|
|
181
|
+
function tailLines(file, maxBytes = 131072) {
|
|
182
|
+
try {
|
|
183
|
+
const size = statSync(file).size;
|
|
184
|
+
const start = Math.max(0, size - maxBytes);
|
|
185
|
+
const fd = openSync(file, "r");
|
|
186
|
+
const buf = Buffer.alloc(size - start);
|
|
187
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
188
|
+
closeSync(fd);
|
|
189
|
+
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
190
|
+
if (start > 0) lines.shift();
|
|
191
|
+
return lines;
|
|
192
|
+
} catch {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function parseLocalLines(lines) {
|
|
197
|
+
const out = [];
|
|
198
|
+
for (const line of lines) {
|
|
199
|
+
try {
|
|
200
|
+
const j = JSON.parse(line);
|
|
201
|
+
const at = Date.parse(j.ts ?? "");
|
|
202
|
+
if (Number.isFinite(at)) out.push({ ...j, at });
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/tui/panels/Live.tsx
|
|
175
210
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
176
211
|
|
|
177
212
|
// src/api-client/client.ts
|
|
178
213
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
179
|
-
import { resolve, join as
|
|
180
|
-
import { homedir as
|
|
214
|
+
import { resolve, join as join3 } from "path";
|
|
215
|
+
import { homedir as homedir3 } from "os";
|
|
181
216
|
var DEFAULT_API_URL = "https://api.solongate.com";
|
|
182
217
|
var ApiError = class extends Error {
|
|
183
218
|
status;
|
|
@@ -197,7 +232,7 @@ var NotAuthenticatedError = class extends Error {
|
|
|
197
232
|
};
|
|
198
233
|
function loginCredentialFile() {
|
|
199
234
|
try {
|
|
200
|
-
const p =
|
|
235
|
+
const p = join3(homedir3(), ".solongate", "cloud-guard.json");
|
|
201
236
|
if (!existsSync(p)) return {};
|
|
202
237
|
const c2 = JSON.parse(readFileSync2(p, "utf-8"));
|
|
203
238
|
return c2 && typeof c2 === "object" ? c2 : {};
|
|
@@ -614,8 +649,7 @@ var CONFIG = loadConfig();
|
|
|
614
649
|
var SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
615
650
|
var BG = "#12234f";
|
|
616
651
|
var DIM_FLOOR = "#233457";
|
|
617
|
-
var
|
|
618
|
-
var RING = join3(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
652
|
+
var RING = join4(process.cwd(), ".solongate", ".eval-ring.jsonl");
|
|
619
653
|
var hhmmss = (ts) => {
|
|
620
654
|
const d = new Date(ts);
|
|
621
655
|
return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
|
|
@@ -625,21 +659,6 @@ var fmtUp = (ms) => {
|
|
|
625
659
|
const p = (n) => String(n).padStart(2, "0");
|
|
626
660
|
return `${p(Math.floor(s / 3600))}:${p(Math.floor(s % 3600 / 60))}:${p(s % 60)}`;
|
|
627
661
|
};
|
|
628
|
-
function tailLines(file, maxBytes = 131072) {
|
|
629
|
-
try {
|
|
630
|
-
const size = statSync(file).size;
|
|
631
|
-
const start = Math.max(0, size - maxBytes);
|
|
632
|
-
const fd = openSync(file, "r");
|
|
633
|
-
const buf = Buffer.alloc(size - start);
|
|
634
|
-
readSync(fd, buf, 0, buf.length, start);
|
|
635
|
-
closeSync(fd);
|
|
636
|
-
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
637
|
-
if (start > 0) lines.shift();
|
|
638
|
-
return lines;
|
|
639
|
-
} catch {
|
|
640
|
-
return [];
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
662
|
function extractTarget(detail) {
|
|
644
663
|
try {
|
|
645
664
|
const j = JSON.parse(detail);
|
|
@@ -1229,9 +1248,9 @@ function LivePanel({ active: active2 }) {
|
|
|
1229
1248
|
else if (input === "x") toggleSignal("dlp");
|
|
1230
1249
|
else if (input === "r") toggleSignal("ratelimit");
|
|
1231
1250
|
else if (input === "e") {
|
|
1232
|
-
const file =
|
|
1251
|
+
const file = join4(homedir4(), ".solongate", "live-export.jsonl");
|
|
1233
1252
|
try {
|
|
1234
|
-
mkdirSync(
|
|
1253
|
+
mkdirSync(join4(homedir4(), ".solongate"), { recursive: true });
|
|
1235
1254
|
writeFileSync(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
|
|
1236
1255
|
setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
|
|
1237
1256
|
} catch (err) {
|
|
@@ -1593,7 +1612,6 @@ function PoliciesPanel({ focused }) {
|
|
|
1593
1612
|
const [pi, setPi] = useState3(0);
|
|
1594
1613
|
const [ri, setRi] = useState3(0);
|
|
1595
1614
|
const [fi, setFi] = useState3(0);
|
|
1596
|
-
const [vi, setVi] = useState3(0);
|
|
1597
1615
|
const [rules, setRules] = useState3([]);
|
|
1598
1616
|
const [mode, setMode] = useState3("denylist");
|
|
1599
1617
|
const [dirty, setDirty] = useState3(false);
|
|
@@ -1602,15 +1620,6 @@ function PoliciesPanel({ focused }) {
|
|
|
1602
1620
|
const [status, setStatus] = useState3(null);
|
|
1603
1621
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
1604
1622
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
1605
|
-
const versionsQ = useLoader(
|
|
1606
|
-
() => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
|
|
1607
|
-
[view, selected?.id]
|
|
1608
|
-
);
|
|
1609
|
-
const diffVer = versionsQ.data?.versions[vi]?.version;
|
|
1610
|
-
const diffQ = useLoader(
|
|
1611
|
-
() => view === "versions" && selected && diffVer !== void 0 ? api.policies.get(selected.id, diffVer) : Promise.resolve(null),
|
|
1612
|
-
[view, selected?.id, diffVer]
|
|
1613
|
-
);
|
|
1614
1623
|
useEffect3(() => {
|
|
1615
1624
|
if (detail.data) {
|
|
1616
1625
|
setRules(detail.data.rules);
|
|
@@ -1708,32 +1717,10 @@ function PoliciesPanel({ focused }) {
|
|
|
1708
1717
|
} else if (input === "D") {
|
|
1709
1718
|
setStatus("Dry-running\u2026");
|
|
1710
1719
|
void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
1711
|
-
} else if (input === "v") {
|
|
1712
|
-
setVi(0);
|
|
1713
|
-
setView("versions");
|
|
1714
1720
|
} else if (input === "s") void save();
|
|
1715
1721
|
else if (input === "x") discard();
|
|
1716
1722
|
return;
|
|
1717
1723
|
}
|
|
1718
|
-
if (view === "versions") {
|
|
1719
|
-
const vers = versionsQ.data?.versions ?? [];
|
|
1720
|
-
if (key.leftArrow) return void setView("rules");
|
|
1721
|
-
if (key.upArrow) setVi((n) => Math.max(0, n - 1));
|
|
1722
|
-
else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
|
|
1723
|
-
else if (key.return) {
|
|
1724
|
-
const v = vers[vi];
|
|
1725
|
-
if (v && selected) {
|
|
1726
|
-
setStatus("Rolling back\u2026");
|
|
1727
|
-
void api.policies.rollback(selected.id, v.version).then((r) => {
|
|
1728
|
-
setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
|
|
1729
|
-
detail.reload();
|
|
1730
|
-
list5.reload();
|
|
1731
|
-
setView("rules");
|
|
1732
|
-
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
return;
|
|
1736
|
-
}
|
|
1737
1724
|
const rule2 = rules[ri];
|
|
1738
1725
|
if (!rule2) return setView("rules");
|
|
1739
1726
|
const field = FIELDS[fi];
|
|
@@ -1774,8 +1761,7 @@ function PoliciesPanel({ focused }) {
|
|
|
1774
1761
|
p.mode.padEnd(10),
|
|
1775
1762
|
" ",
|
|
1776
1763
|
p.rules.length,
|
|
1777
|
-
" rules
|
|
1778
|
-
p.version
|
|
1764
|
+
" rules"
|
|
1779
1765
|
] }),
|
|
1780
1766
|
p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
|
|
1781
1767
|
] }, p.id)) }),
|
|
@@ -1791,7 +1777,7 @@ function PoliciesPanel({ focused }) {
|
|
|
1791
1777
|
/* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
1792
1778
|
dirtyTag
|
|
1793
1779
|
] }),
|
|
1794
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7
|
|
1780
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 s save \xB7 \u2190 back" }),
|
|
1795
1781
|
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
1796
1782
|
Table,
|
|
1797
1783
|
{
|
|
@@ -1817,50 +1803,6 @@ function PoliciesPanel({ focused }) {
|
|
|
1817
1803
|
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
1818
1804
|
] }) });
|
|
1819
1805
|
}
|
|
1820
|
-
if (view === "versions") {
|
|
1821
|
-
const vers = versionsQ.data?.versions ?? [];
|
|
1822
|
-
return /* @__PURE__ */ jsx3(DataView, { loading: versionsQ.loading && !versionsQ.data, error: versionsQ.error, empty: !!versionsQ.data && vers.length === 0, emptyText: "No versions.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1823
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate(selected?.name ?? "", 28) }),
|
|
1824
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
|
|
1825
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
1826
|
-
Table,
|
|
1827
|
-
{
|
|
1828
|
-
columns: [
|
|
1829
|
-
{ header: "", width: 2 },
|
|
1830
|
-
{ header: "VER", width: 5 },
|
|
1831
|
-
{ header: "RULES", width: 5 },
|
|
1832
|
-
{ header: "REASON", width: 34 },
|
|
1833
|
-
{ header: "WHEN", width: 16 }
|
|
1834
|
-
],
|
|
1835
|
-
rows: vers.map((v, i) => [
|
|
1836
|
-
{ value: i === vi ? "\u25B8" : "", color: theme.accentBright },
|
|
1837
|
-
{ value: `v${v.version}`, bold: i === vi },
|
|
1838
|
-
{ value: String(v.rules_count), dim: true },
|
|
1839
|
-
{ value: truncate(v.reason || "\u2014", 34) },
|
|
1840
|
-
{ value: truncate(v.created_at, 16), dim: true }
|
|
1841
|
-
])
|
|
1842
|
-
}
|
|
1843
|
-
) }),
|
|
1844
|
-
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
1845
|
-
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: `Diff v${diffVer ?? "?"} vs current draft` }),
|
|
1846
|
-
(() => {
|
|
1847
|
-
const old = diffQ.data?.rules ?? [];
|
|
1848
|
-
const cur = rules;
|
|
1849
|
-
const oldIds = new Set(old.map((r) => r.id));
|
|
1850
|
-
const curIds = new Set(cur.map((r) => r.id));
|
|
1851
|
-
const added = cur.filter((r) => !oldIds.has(r.id));
|
|
1852
|
-
const removed = old.filter((r) => !curIds.has(r.id));
|
|
1853
|
-
if (diffQ.loading && !diffQ.data) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " loading\u2026" });
|
|
1854
|
-
if (!added.length && !removed.length) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " identical rule set" });
|
|
1855
|
-
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1856
|
-
added.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.ok, children: ` + ${r.effect} ${truncate(r.toolPattern, 18)} ${truncate(r.description || r.id, 30)}` }, "a" + r.id)),
|
|
1857
|
-
removed.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.bad, children: ` - ${r.effect} ${truncate(r.toolPattern, 18)} ${truncate(r.description || r.id, 30)}` }, "r" + r.id))
|
|
1858
|
-
] });
|
|
1859
|
-
})()
|
|
1860
|
-
] }),
|
|
1861
|
-
status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
1862
|
-
] }) });
|
|
1863
|
-
}
|
|
1864
1806
|
const rule = rules[ri];
|
|
1865
1807
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1866
1808
|
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
@@ -2222,171 +2164,229 @@ function RegexTest({ re }) {
|
|
|
2222
2164
|
] });
|
|
2223
2165
|
}
|
|
2224
2166
|
|
|
2225
|
-
// src/tui/panels/Stats.tsx
|
|
2226
|
-
import { Box as Box6, Text as Text6 } from "ink";
|
|
2227
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2228
|
-
function StatsPanel({ active: active2 }) {
|
|
2229
|
-
const stats = useLoader(() => api.stats.get());
|
|
2230
|
-
const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
|
|
2231
|
-
const drift2 = useLoader(() => api.stats.drift(7));
|
|
2232
|
-
usePoll(() => {
|
|
2233
|
-
stats.reload();
|
|
2234
|
-
ts.reload();
|
|
2235
|
-
}, 5e3, active2);
|
|
2236
|
-
usePoll(drift2.reload, 3e4, active2);
|
|
2237
|
-
const s = stats.data;
|
|
2238
|
-
const points = ts.data?.timeseries ?? [];
|
|
2239
|
-
const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
|
|
2240
|
-
return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2241
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2242
|
-
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
2243
|
-
s.total_calls,
|
|
2244
|
-
" "
|
|
2245
|
-
] }),
|
|
2246
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "calls " }),
|
|
2247
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
2248
|
-
s.allowed,
|
|
2249
|
-
" allowed"
|
|
2250
|
-
] }),
|
|
2251
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " }),
|
|
2252
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
2253
|
-
s.denied,
|
|
2254
|
-
" denied"
|
|
2255
|
-
] })
|
|
2256
|
-
] }),
|
|
2257
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2258
|
-
s.active_policies,
|
|
2259
|
-
" policies \xB7 ",
|
|
2260
|
-
s.registered_tools,
|
|
2261
|
-
" tools"
|
|
2262
|
-
] }),
|
|
2263
|
-
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
2264
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2265
|
-
"Last 24h ",
|
|
2266
|
-
ts.data ? `(per ${ts.data.granularity})` : ""
|
|
2267
|
-
] }),
|
|
2268
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2269
|
-
/* @__PURE__ */ jsx6(Text6, { children: "total " }),
|
|
2270
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 64) })
|
|
2271
|
-
] }),
|
|
2272
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2273
|
-
/* @__PURE__ */ jsx6(Text6, { children: "allowed " }),
|
|
2274
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.ok, children: sparkline(points.map((p) => p.allowed), 64) })
|
|
2275
|
-
] }),
|
|
2276
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2277
|
-
/* @__PURE__ */ jsx6(Text6, { children: "denied " }),
|
|
2278
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 64) })
|
|
2279
|
-
] })
|
|
2280
|
-
] }),
|
|
2281
|
-
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
2282
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Recent" }),
|
|
2283
|
-
/* @__PURE__ */ jsx6(
|
|
2284
|
-
Table,
|
|
2285
|
-
{
|
|
2286
|
-
columns: [
|
|
2287
|
-
{ header: "DECISION", width: 9 },
|
|
2288
|
-
{ header: "TOOL", width: 20 },
|
|
2289
|
-
{ header: "MS", width: 5 },
|
|
2290
|
-
{ header: "WHEN", width: 6 }
|
|
2291
|
-
],
|
|
2292
|
-
rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
|
|
2293
|
-
{ value: a.decision, color: decisionColor(a.decision) },
|
|
2294
|
-
{ value: truncate(a.tool_name, 20), color: theme.accent },
|
|
2295
|
-
{ value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
|
|
2296
|
-
{ value: ago(a.created_at), dim: true }
|
|
2297
|
-
])
|
|
2298
|
-
}
|
|
2299
|
-
)
|
|
2300
|
-
] }),
|
|
2301
|
-
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
2302
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2303
|
-
"Denial drift ",
|
|
2304
|
-
drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
|
|
2305
|
-
] }),
|
|
2306
|
-
driftRules.length ? /* @__PURE__ */ jsx6(
|
|
2307
|
-
Table,
|
|
2308
|
-
{
|
|
2309
|
-
columns: [
|
|
2310
|
-
{ header: "NOW", width: 5 },
|
|
2311
|
-
{ header: "PREV", width: 5 },
|
|
2312
|
-
{ header: "\u0394", width: 6 },
|
|
2313
|
-
{ header: "RULE", width: 22 },
|
|
2314
|
-
{ header: "REASON", width: 26 }
|
|
2315
|
-
],
|
|
2316
|
-
rows: driftRules.map((r) => [
|
|
2317
|
-
{ value: String(r.current), bold: true },
|
|
2318
|
-
{ value: String(r.previous), dim: true },
|
|
2319
|
-
{ value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
|
|
2320
|
-
{ value: truncate(r.rule_id ?? "\u2014", 22), color: theme.accent },
|
|
2321
|
-
{ value: truncate(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
|
|
2322
|
-
])
|
|
2323
|
-
}
|
|
2324
|
-
) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
|
|
2325
|
-
] })
|
|
2326
|
-
] }) : null });
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
2167
|
// src/tui/panels/Audit.tsx
|
|
2330
|
-
import { Box as
|
|
2168
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
2331
2169
|
import TextInput4 from "ink-text-input";
|
|
2332
2170
|
import { useState as useState6 } from "react";
|
|
2333
|
-
import { Fragment as Fragment4, jsx as
|
|
2171
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2334
2172
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
2335
2173
|
var SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
2336
|
-
var
|
|
2174
|
+
var SESS_STATUS = [void 0, "active", "idle", "ended"];
|
|
2175
|
+
var PAGE = 500;
|
|
2176
|
+
var LOCAL_MAX_BYTES = 16 * 1024 * 1024;
|
|
2177
|
+
var cloudRow = (e) => ({
|
|
2178
|
+
id: e.id,
|
|
2179
|
+
at: Date.parse(e.created_at),
|
|
2180
|
+
tool: e.tool_name,
|
|
2181
|
+
decision: e.decision,
|
|
2182
|
+
permission: e.permission,
|
|
2183
|
+
trust: e.trust_level,
|
|
2184
|
+
agent: e.agent_name,
|
|
2185
|
+
session: e.session_id,
|
|
2186
|
+
reason: e.reason,
|
|
2187
|
+
rule: e.matched_rule_id,
|
|
2188
|
+
evalMs: e.evaluation_time_ms,
|
|
2189
|
+
dlp: e.dlp_matches ?? [],
|
|
2190
|
+
burst: !!e.rate_limit_burst,
|
|
2191
|
+
args: e.arguments_summary ? JSON.stringify(e.arguments_summary) : null
|
|
2192
|
+
});
|
|
2193
|
+
function loadLocalRows() {
|
|
2194
|
+
return parseLocalLines(tailLines(LOCAL_LOG, LOCAL_MAX_BYTES)).map((j, i) => ({
|
|
2195
|
+
id: "l:" + j.at + ":" + i,
|
|
2196
|
+
at: j.at,
|
|
2197
|
+
tool: j.tool ?? "?",
|
|
2198
|
+
decision: j.decision ?? "ALLOW",
|
|
2199
|
+
permission: j.permission ?? "\u2014",
|
|
2200
|
+
trust: j.trust_level ?? "\u2014",
|
|
2201
|
+
agent: j.agent_name ?? null,
|
|
2202
|
+
session: j.session_id ?? null,
|
|
2203
|
+
reason: j.reason ?? null,
|
|
2204
|
+
rule: j.matched_rule_id ?? null,
|
|
2205
|
+
evalMs: j.evaluation_time_ms ?? null,
|
|
2206
|
+
dlp: j.dlp ? Array.isArray(j.dlp) ? j.dlp.map(String) : ["dlp"] : [],
|
|
2207
|
+
burst: !!j.rate_limit_burst,
|
|
2208
|
+
args: j.arguments ? JSON.stringify(j.arguments) : null
|
|
2209
|
+
})).sort((a, b) => b.at - a.at);
|
|
2210
|
+
}
|
|
2211
|
+
var sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
|
|
2212
|
+
var STATUS_DOT = {
|
|
2213
|
+
active: { ch: "\u25CF", color: theme.ok },
|
|
2214
|
+
idle: { ch: "\u25D0", color: theme.warn },
|
|
2215
|
+
ended: { ch: "\u25CB", color: theme.dim }
|
|
2216
|
+
};
|
|
2337
2217
|
function AuditPanel({ active: active2, focused }) {
|
|
2338
2218
|
const { cols, rows } = usePanelSize();
|
|
2219
|
+
const [source, setSource] = useState6("cloud");
|
|
2220
|
+
const [view, setView] = useState6("logs");
|
|
2339
2221
|
const [di, setDi] = useState6(0);
|
|
2340
2222
|
const [gi, setGi] = useState6(0);
|
|
2341
2223
|
const [tool, setTool] = useState6("");
|
|
2342
2224
|
const [agent, setAgent] = useState6("");
|
|
2343
2225
|
const [search, setSearch] = useState6("");
|
|
2226
|
+
const [sessFilter, setSessFilter] = useState6("");
|
|
2227
|
+
const [page, setPage] = useState6(0);
|
|
2344
2228
|
const [sel, setSel] = useState6(0);
|
|
2345
|
-
const [view, setView] = useState6("list");
|
|
2346
2229
|
const [detailScroll, setDetailScroll] = useState6(0);
|
|
2347
2230
|
const [editing, setEditing] = useState6(null);
|
|
2231
|
+
const [si, setSi] = useState6(0);
|
|
2232
|
+
const [sessSearch, setSessSearch] = useState6("");
|
|
2233
|
+
const [sessSel, setSessSel] = useState6(0);
|
|
2234
|
+
const toTop = () => setSel(0);
|
|
2235
|
+
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2236
|
+
const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
|
|
2237
|
+
usePoll(() => {
|
|
2238
|
+
statsQ.reload();
|
|
2239
|
+
tsQ.reload();
|
|
2240
|
+
}, 15e3, active2 && source === "cloud");
|
|
2348
2241
|
const query = {
|
|
2349
2242
|
filter: DECISIONS[di],
|
|
2350
2243
|
signal: SIGNALS[gi],
|
|
2351
2244
|
tool: tool || void 0,
|
|
2352
2245
|
agent_name: agent || void 0,
|
|
2353
2246
|
search: search || void 0,
|
|
2354
|
-
|
|
2247
|
+
session_id: sessFilter || void 0,
|
|
2248
|
+
limit: PAGE,
|
|
2249
|
+
offset: page * PAGE
|
|
2355
2250
|
};
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2251
|
+
const cloudQ = useLoader(
|
|
2252
|
+
() => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
|
|
2253
|
+
[source, di, gi, tool, agent, search, sessFilter, page]
|
|
2254
|
+
);
|
|
2255
|
+
usePoll(cloudQ.reload, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
|
|
2256
|
+
const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
|
|
2257
|
+
usePoll(localQ.reload, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
|
|
2258
|
+
let pageRows = [];
|
|
2259
|
+
let total = 0;
|
|
2260
|
+
if (source === "cloud") {
|
|
2261
|
+
pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
|
|
2262
|
+
total = cloudQ.data?.total ?? 0;
|
|
2263
|
+
} else {
|
|
2264
|
+
const all = localQ.data ?? [];
|
|
2265
|
+
const q = search.trim().toLowerCase();
|
|
2266
|
+
const filtered = all.filter((r) => {
|
|
2267
|
+
if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
|
|
2268
|
+
if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
|
|
2269
|
+
if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
|
|
2270
|
+
if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
|
|
2271
|
+
if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
|
|
2272
|
+
if (sessFilter && r.session !== sessFilter) return false;
|
|
2273
|
+
if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
|
|
2274
|
+
return true;
|
|
2275
|
+
});
|
|
2276
|
+
total = filtered.length;
|
|
2277
|
+
pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
|
|
2278
|
+
}
|
|
2279
|
+
const pages = Math.max(1, Math.ceil(total / PAGE));
|
|
2280
|
+
const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
|
|
2281
|
+
const agentsQ = useLoader(
|
|
2282
|
+
() => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
|
|
2283
|
+
[source]
|
|
2284
|
+
);
|
|
2285
|
+
usePoll(agentsQ.reload, 8e3, active2 && source === "cloud" && view === "sessions");
|
|
2286
|
+
let sessions = [];
|
|
2287
|
+
if (source === "cloud") {
|
|
2288
|
+
sessions = (agentsQ.data?.agents ?? []).map((a) => ({
|
|
2289
|
+
id: a.session_id,
|
|
2290
|
+
agent: a.agent_name ?? a.session_id,
|
|
2291
|
+
status: a.status === "deactivated" ? "ended" : a.status,
|
|
2292
|
+
calls: a.total_calls,
|
|
2293
|
+
denies: a.denied_calls,
|
|
2294
|
+
dlp: a.dlp_events,
|
|
2295
|
+
trust: a.trust_score,
|
|
2296
|
+
lastAt: Date.parse(a.last_seen_at)
|
|
2297
|
+
}));
|
|
2298
|
+
} else {
|
|
2299
|
+
const bySess = /* @__PURE__ */ new Map();
|
|
2300
|
+
for (const r of localQ.data ?? []) {
|
|
2301
|
+
if (!r.session) continue;
|
|
2302
|
+
const cur = bySess.get(r.session) ?? { id: r.session, agent: r.agent ?? "local agent", status: "ended", calls: 0, denies: 0, dlp: 0, trust: null, lastAt: 0 };
|
|
2303
|
+
cur.calls++;
|
|
2304
|
+
if (r.decision !== "ALLOW") cur.denies++;
|
|
2305
|
+
if (r.dlp.length) cur.dlp++;
|
|
2306
|
+
cur.lastAt = Math.max(cur.lastAt, r.at);
|
|
2307
|
+
if (r.agent) cur.agent = r.agent;
|
|
2308
|
+
bySess.set(r.session, cur);
|
|
2309
|
+
}
|
|
2310
|
+
sessions = [...bySess.values()];
|
|
2311
|
+
}
|
|
2312
|
+
const sq = sessSearch.trim().toLowerCase();
|
|
2313
|
+
const sessionsFiltered = sessions.map((s) => ({ ...s, status: source === "cloud" ? s.status : sessStatus2(s.lastAt) })).filter((s) => SESS_STATUS[si] ? s.status === SESS_STATUS[si] : true).filter((s) => !sq || `${s.agent} ${s.id}`.toLowerCase().includes(sq)).sort((a, b) => b.lastAt - a.lastAt);
|
|
2314
|
+
const currentSess = sessionsFiltered[Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1))];
|
|
2360
2315
|
const sessionQ = useLoader(
|
|
2361
|
-
() => view === "detail" && current?.
|
|
2362
|
-
[view, current?.
|
|
2316
|
+
() => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
|
|
2317
|
+
[view, current?.session, source]
|
|
2363
2318
|
);
|
|
2364
2319
|
useInput5(
|
|
2365
2320
|
(input, key) => {
|
|
2366
2321
|
if (view === "detail") {
|
|
2367
|
-
if (key.leftArrow || key.escape) setView("
|
|
2322
|
+
if (key.leftArrow || key.escape) setView("logs");
|
|
2368
2323
|
else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
|
|
2369
2324
|
else if (key.downArrow) setDetailScroll((n) => n + 1);
|
|
2370
2325
|
else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
|
|
2371
2326
|
else if (key.pageDown) setDetailScroll((n) => n + 10);
|
|
2372
2327
|
return;
|
|
2373
2328
|
}
|
|
2374
|
-
|
|
2329
|
+
if (input === "s") {
|
|
2330
|
+
setSource((s) => s === "cloud" ? "local" : "cloud");
|
|
2331
|
+
setPage(0);
|
|
2332
|
+
toTop();
|
|
2333
|
+
setSessSel(0);
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (input === "v") {
|
|
2337
|
+
setView((v) => v === "logs" ? "sessions" : "logs");
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
if (view === "sessions") {
|
|
2341
|
+
if (key.upArrow) setSessSel((n) => Math.max(0, n - 1));
|
|
2342
|
+
else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
|
|
2343
|
+
else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
|
|
2344
|
+
else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
|
|
2345
|
+
else if (key.return || key.rightArrow) {
|
|
2346
|
+
if (currentSess) {
|
|
2347
|
+
setSessFilter(currentSess.id);
|
|
2348
|
+
setPage(0);
|
|
2349
|
+
toTop();
|
|
2350
|
+
setView("logs");
|
|
2351
|
+
}
|
|
2352
|
+
} else if (input === "f") {
|
|
2353
|
+
setSi((n) => (n + 1) % SESS_STATUS.length);
|
|
2354
|
+
setSessSel(0);
|
|
2355
|
+
} else if (input === "/") setEditing("sess-search");
|
|
2356
|
+
else if (input === "c") {
|
|
2357
|
+
setSi(0);
|
|
2358
|
+
setSessSearch("");
|
|
2359
|
+
setSessSel(0);
|
|
2360
|
+
}
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2375
2363
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
2376
|
-
else if (key.downArrow) setSel((n) => Math.min(
|
|
2364
|
+
else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
|
|
2377
2365
|
else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
|
|
2378
|
-
else if (key.pageDown) setSel((n) => Math.min(
|
|
2366
|
+
else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
|
|
2379
2367
|
else if (key.return || key.rightArrow) {
|
|
2380
2368
|
if (current) {
|
|
2381
2369
|
setDetailScroll(0);
|
|
2382
2370
|
setView("detail");
|
|
2383
2371
|
}
|
|
2372
|
+
} else if (input === "]") {
|
|
2373
|
+
if (page < pages - 1) {
|
|
2374
|
+
setPage(page + 1);
|
|
2375
|
+
toTop();
|
|
2376
|
+
}
|
|
2377
|
+
} else if (input === "[") {
|
|
2378
|
+
if (page > 0) {
|
|
2379
|
+
setPage(page - 1);
|
|
2380
|
+
toTop();
|
|
2381
|
+
}
|
|
2384
2382
|
} else if (input === "f") {
|
|
2385
2383
|
setDi((n) => (n + 1) % DECISIONS.length);
|
|
2386
|
-
|
|
2384
|
+
setPage(0);
|
|
2385
|
+
toTop();
|
|
2387
2386
|
} else if (input === "g") {
|
|
2388
2387
|
setGi((n) => (n + 1) % SIGNALS.length);
|
|
2389
|
-
|
|
2388
|
+
setPage(0);
|
|
2389
|
+
toTop();
|
|
2390
2390
|
} else if (input === "t") setEditing("tool");
|
|
2391
2391
|
else if (input === "n") setEditing("agent");
|
|
2392
2392
|
else if (input === "/") setEditing("search");
|
|
@@ -2396,46 +2396,89 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2396
2396
|
setTool("");
|
|
2397
2397
|
setAgent("");
|
|
2398
2398
|
setSearch("");
|
|
2399
|
-
|
|
2399
|
+
setSessFilter("");
|
|
2400
|
+
setPage(0);
|
|
2401
|
+
toTop();
|
|
2400
2402
|
}
|
|
2401
2403
|
},
|
|
2402
2404
|
{ isActive: focused && !editing }
|
|
2403
2405
|
);
|
|
2406
|
+
const points = tsQ.data?.timeseries ?? [];
|
|
2407
|
+
const localAll = localQ.data ?? [];
|
|
2408
|
+
const strip = source === "cloud" ? /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
2409
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
2410
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls \xB7 " }),
|
|
2411
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
2412
|
+
statsQ.data?.allowed ?? "\xB7\xB7\xB7",
|
|
2413
|
+
" allow"
|
|
2414
|
+
] }),
|
|
2415
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
|
|
2416
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
2417
|
+
statsQ.data?.denied ?? "\xB7\xB7",
|
|
2418
|
+
" deny"
|
|
2419
|
+
] }),
|
|
2420
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies \xB7 ${statsQ.data?.registered_tools ?? "\xB7"} tools \xB7 24h ` }),
|
|
2421
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: sparkline(points.map((p) => p.total), 24) }),
|
|
2422
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " deny " }),
|
|
2423
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: sparkline(points.map((p) => p.denied), 24) })
|
|
2424
|
+
] }) : /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
2425
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: localAll.length }),
|
|
2426
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls in local file \xB7 " }),
|
|
2427
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
|
|
2428
|
+
localAll.filter((r) => r.decision === "ALLOW").length,
|
|
2429
|
+
" allow"
|
|
2430
|
+
] }),
|
|
2431
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
|
|
2432
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
|
|
2433
|
+
localAll.filter((r) => r.decision !== "ALLOW").length,
|
|
2434
|
+
" deny"
|
|
2435
|
+
] }),
|
|
2436
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2437
|
+
" \xB7 ",
|
|
2438
|
+
LOCAL_LOG
|
|
2439
|
+
] })
|
|
2440
|
+
] });
|
|
2441
|
+
const srcChip = /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
2442
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "src:" }),
|
|
2443
|
+
/* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
2444
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
|
|
2445
|
+
] });
|
|
2404
2446
|
if (view === "detail" && current) {
|
|
2405
|
-
const sessionCalls = sessionQ.data?.entries ?? [];
|
|
2447
|
+
const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
|
|
2406
2448
|
const bodyW = Math.max(20, cols - 2);
|
|
2407
2449
|
const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
|
|
2408
|
-
const argLines = current.
|
|
2409
|
-
const sessionRows = current.
|
|
2450
|
+
const argLines = current.args ? wrapLines(prettyJson(current.args), bodyW) : ["(no arguments recorded)"];
|
|
2451
|
+
const sessionRows = current.session ? Math.min(4, sessionCalls.length) + 2 : 1;
|
|
2410
2452
|
const reasonShown = reasonLines.slice(0, 4);
|
|
2411
2453
|
const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
|
|
2412
2454
|
const maxScroll = Math.max(0, argLines.length - argBudget);
|
|
2413
2455
|
const off = Math.min(detailScroll, maxScroll);
|
|
2414
2456
|
const argWin = argLines.slice(off, off + argBudget);
|
|
2415
|
-
return /* @__PURE__ */
|
|
2416
|
-
/* @__PURE__ */
|
|
2417
|
-
/* @__PURE__ */
|
|
2418
|
-
/* @__PURE__ */
|
|
2419
|
-
/* @__PURE__ */
|
|
2457
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2458
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2459
|
+
/* @__PURE__ */ jsx6(Text6, { color: decisionColor(current.decision), bold: true, children: current.decision }),
|
|
2460
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: " " + current.tool }),
|
|
2461
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust + " " }),
|
|
2462
|
+
srcChip
|
|
2420
2463
|
] }),
|
|
2421
|
-
reasonShown.map((l, i) => /* @__PURE__ */
|
|
2422
|
-
/* @__PURE__ */
|
|
2423
|
-
/* @__PURE__ */
|
|
2424
|
-
] }) : /* @__PURE__ */
|
|
2425
|
-
/* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2427
|
-
/* @__PURE__ */
|
|
2428
|
-
/* @__PURE__ */
|
|
2429
|
-
/* @__PURE__ */
|
|
2430
|
-
/* @__PURE__ */
|
|
2431
|
-
/* @__PURE__ */
|
|
2432
|
-
current.
|
|
2433
|
-
/* @__PURE__ */
|
|
2464
|
+
reasonShown.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
2465
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "reason " }),
|
|
2466
|
+
/* @__PURE__ */ jsx6(Text6, { children: l.slice("reason: ".length) })
|
|
2467
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { children: " " + l }) }, "r" + i)),
|
|
2468
|
+
/* @__PURE__ */ jsx6(Detail, { label: "rule", value: current.rule ?? "\u2014" }),
|
|
2469
|
+
/* @__PURE__ */ jsx6(Detail, { label: "agent", value: current.agent ?? "\u2014" }),
|
|
2470
|
+
/* @__PURE__ */ jsx6(Detail, { label: "session", value: current.session ?? "\u2014" }),
|
|
2471
|
+
/* @__PURE__ */ jsx6(Detail, { label: "dlp", value: current.dlp.length ? current.dlp.join(", ") : "none", color: current.dlp.length ? theme.bad : void 0 }),
|
|
2472
|
+
/* @__PURE__ */ jsx6(Detail, { label: "when", value: new Date(current.at).toLocaleString() + (current.evalMs != null ? ` \xB7 eval ${current.evalMs}ms` : "") }),
|
|
2473
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
|
|
2474
|
+
/* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
2475
|
+
current.session ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2476
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2434
2477
|
"Session \xB7 ",
|
|
2435
2478
|
sessionCalls.length,
|
|
2436
2479
|
" calls"
|
|
2437
2480
|
] }),
|
|
2438
|
-
/* @__PURE__ */
|
|
2481
|
+
/* @__PURE__ */ jsx6(
|
|
2439
2482
|
Table,
|
|
2440
2483
|
{
|
|
2441
2484
|
columns: [
|
|
@@ -2446,59 +2489,136 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2446
2489
|
],
|
|
2447
2490
|
rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
|
|
2448
2491
|
{ value: e.decision, color: decisionColor(e.decision) },
|
|
2449
|
-
{ value: truncate(e.
|
|
2492
|
+
{ value: truncate(e.tool, 20), color: theme.accent },
|
|
2450
2493
|
{ value: truncate(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
|
|
2451
|
-
{ value: ago(e.
|
|
2494
|
+
{ value: ago(e.at), dim: true }
|
|
2452
2495
|
])
|
|
2453
2496
|
}
|
|
2454
2497
|
)
|
|
2455
|
-
] }) : /* @__PURE__ */
|
|
2456
|
-
/* @__PURE__ */
|
|
2498
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "(no session id)" }),
|
|
2499
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to logs" })
|
|
2457
2500
|
] });
|
|
2458
2501
|
}
|
|
2459
|
-
const chip = (label, val, on) => /* @__PURE__ */
|
|
2460
|
-
/* @__PURE__ */
|
|
2502
|
+
const chip = (label, val, on) => /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
2503
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2461
2504
|
label,
|
|
2462
2505
|
":"
|
|
2463
2506
|
] }),
|
|
2464
|
-
/* @__PURE__ */
|
|
2465
|
-
/* @__PURE__ */
|
|
2507
|
+
/* @__PURE__ */ jsx6(Text6, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
2508
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " })
|
|
2466
2509
|
] });
|
|
2467
|
-
|
|
2510
|
+
if (view === "sessions") {
|
|
2511
|
+
const headerRows2 = 5 + (editing ? 1 : 0);
|
|
2512
|
+
const listRows2 = Math.max(4, rows - headerRows2);
|
|
2513
|
+
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
2514
|
+
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
2515
|
+
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
2516
|
+
const loading2 = source === "cloud" ? agentsQ.loading && !agentsQ.data : localQ.loading && !localQ.data;
|
|
2517
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2518
|
+
strip,
|
|
2519
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2520
|
+
srcChip,
|
|
2521
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
2522
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 logs (v) " }),
|
|
2523
|
+
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
2524
|
+
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
2525
|
+
] }),
|
|
2526
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 f status \xB7 / search \xB7 s source \xB7 v logs \xB7 c clear" : "press \u2192 to browse" }),
|
|
2527
|
+
editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2528
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
|
|
2529
|
+
/* @__PURE__ */ jsx6(
|
|
2530
|
+
TextInput4,
|
|
2531
|
+
{
|
|
2532
|
+
value: sessSearch,
|
|
2533
|
+
onChange: (v) => {
|
|
2534
|
+
setSessSearch(v);
|
|
2535
|
+
setSessSel(0);
|
|
2536
|
+
},
|
|
2537
|
+
onSubmit: () => setEditing(null)
|
|
2538
|
+
}
|
|
2539
|
+
)
|
|
2540
|
+
] }) : null,
|
|
2541
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: `${sessionsFiltered.length} sessions \xB7 ${selC + 1}/${sessionsFiltered.length}${start2 ? ` \xB7 \u25B2${start2}` : ""}${start2 + listRows2 < sessionsFiltered.length ? ` \xB7 \u25BC${sessionsFiltered.length - start2 - listRows2}` : ""}` }),
|
|
2542
|
+
/* @__PURE__ */ jsx6(
|
|
2543
|
+
Table,
|
|
2544
|
+
{
|
|
2545
|
+
columns: [
|
|
2546
|
+
{ header: "", width: 2 },
|
|
2547
|
+
{ header: "STATUS", width: 8 },
|
|
2548
|
+
{ header: "AGENT", width: 18 },
|
|
2549
|
+
{ header: "SESSION", width: 10 },
|
|
2550
|
+
{ header: "CALLS", width: 6 },
|
|
2551
|
+
{ header: "DENY", width: 5 },
|
|
2552
|
+
{ header: "DLP", width: 4 },
|
|
2553
|
+
{ header: "TRUST", width: 5 },
|
|
2554
|
+
{ header: "LAST", width: 6 }
|
|
2555
|
+
],
|
|
2556
|
+
rows: win.map((r, i) => {
|
|
2557
|
+
const st = STATUS_DOT[r.status];
|
|
2558
|
+
const isSel = start2 + i === selC && focused;
|
|
2559
|
+
return [
|
|
2560
|
+
{ value: isSel ? "\u25B8" : "", color: theme.accentBright },
|
|
2561
|
+
{ value: `${st.ch} ${r.status}`, color: st.color },
|
|
2562
|
+
{ value: truncate(r.agent, 18), color: theme.accent, bold: isSel },
|
|
2563
|
+
{ value: r.id.slice(0, 8), dim: true },
|
|
2564
|
+
{ value: String(r.calls) },
|
|
2565
|
+
{ value: String(r.denies), color: r.denies ? theme.bad : void 0, dim: !r.denies },
|
|
2566
|
+
{ value: String(r.dlp), color: r.dlp ? theme.bad : void 0, dim: !r.dlp },
|
|
2567
|
+
{ value: r.trust != null ? String(r.trust) : "\u2014", dim: true },
|
|
2568
|
+
{ value: ago(r.lastAt), dim: true }
|
|
2569
|
+
];
|
|
2570
|
+
})
|
|
2571
|
+
}
|
|
2572
|
+
),
|
|
2573
|
+
sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2574
|
+
"(no sessions",
|
|
2575
|
+
source === "local" ? " in the local file" : "",
|
|
2576
|
+
")"
|
|
2577
|
+
] }) : null
|
|
2578
|
+
] }) });
|
|
2579
|
+
}
|
|
2580
|
+
const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
|
|
2468
2581
|
const listRows = Math.max(4, rows - headerRows);
|
|
2469
|
-
const selClamped = Math.min(sel, Math.max(0,
|
|
2470
|
-
const maxStart = Math.max(0,
|
|
2582
|
+
const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
|
|
2583
|
+
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
2471
2584
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
2472
|
-
const windowed =
|
|
2585
|
+
const windowed = pageRows.slice(start, start + listRows);
|
|
2473
2586
|
const reasonW = Math.min(60, Math.max(12, cols - 67));
|
|
2474
|
-
|
|
2475
|
-
|
|
2587
|
+
const loading = source === "cloud" ? cloudQ.loading && !cloudQ.data : localQ.loading && !localQ.data;
|
|
2588
|
+
return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2589
|
+
strip,
|
|
2590
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2591
|
+
srcChip,
|
|
2592
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
2593
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 sessions (v) " }),
|
|
2476
2594
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
2477
2595
|
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
2478
2596
|
chip("tool", tool || "\xB7", !!tool),
|
|
2479
2597
|
chip("agent", agent || "\xB7", !!agent),
|
|
2480
|
-
chip("search", search || "\xB7", !!search)
|
|
2598
|
+
chip("search", search || "\xB7", !!search),
|
|
2599
|
+
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
2481
2600
|
] }),
|
|
2482
|
-
/* @__PURE__ */
|
|
2483
|
-
editing ? /* @__PURE__ */
|
|
2484
|
-
/* @__PURE__ */
|
|
2601
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 [ ] page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
|
|
2602
|
+
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2603
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
|
|
2485
2604
|
editing,
|
|
2486
2605
|
": "
|
|
2487
2606
|
] }),
|
|
2488
|
-
/* @__PURE__ */
|
|
2607
|
+
/* @__PURE__ */ jsx6(
|
|
2489
2608
|
TextInput4,
|
|
2490
2609
|
{
|
|
2491
2610
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
2492
2611
|
onChange: (v) => {
|
|
2493
2612
|
(editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch)(v);
|
|
2494
|
-
|
|
2613
|
+
setPage(0);
|
|
2614
|
+
toTop();
|
|
2495
2615
|
},
|
|
2496
2616
|
onSubmit: () => setEditing(null)
|
|
2497
2617
|
}
|
|
2498
2618
|
)
|
|
2499
2619
|
] }) : null,
|
|
2500
|
-
/* @__PURE__ */
|
|
2501
|
-
/* @__PURE__ */
|
|
2620
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: `${total} matched \xB7 page ${Math.min(page + 1, pages)}/${pages} \xB7 ${PAGE}/page \xB7 ${pageRows.length ? selClamped + 1 : 0}/${pageRows.length}${start ? ` \xB7 \u25B2${start} newer` : ""}${start + listRows < pageRows.length ? ` \xB7 \u25BC${pageRows.length - start - listRows} older` : ""}` }),
|
|
2621
|
+
/* @__PURE__ */ jsx6(
|
|
2502
2622
|
Table,
|
|
2503
2623
|
{
|
|
2504
2624
|
columns: [
|
|
@@ -2512,40 +2632,45 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2512
2632
|
],
|
|
2513
2633
|
rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
|
|
2514
2634
|
}
|
|
2515
|
-
)
|
|
2635
|
+
),
|
|
2636
|
+
pageRows.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
|
|
2637
|
+
"(no entries",
|
|
2638
|
+
source === "local" ? " in the local file" : "",
|
|
2639
|
+
" \u2014 c clears filters)"
|
|
2640
|
+
] }) : null
|
|
2516
2641
|
] }) });
|
|
2517
2642
|
}
|
|
2518
2643
|
function rowFor(e, active2, reasonW) {
|
|
2519
2644
|
return [
|
|
2520
2645
|
{ value: active2 ? "\u25B8" : "", color: theme.accentBright },
|
|
2521
2646
|
{ value: e.decision, color: decisionColor(e.decision) },
|
|
2522
|
-
{ value: truncate(e.
|
|
2523
|
-
{ value: truncate(e.
|
|
2524
|
-
{ value: truncate(e.reason ?? "\u2014", reasonW) },
|
|
2525
|
-
{ value: e.
|
|
2526
|
-
{ value: ago(e.
|
|
2647
|
+
{ value: truncate(e.tool, 18), color: theme.accent },
|
|
2648
|
+
{ value: truncate(e.agent ?? "\u2014", 14), dim: true },
|
|
2649
|
+
{ value: truncate(e.reason ?? e.args ?? "\u2014", reasonW) },
|
|
2650
|
+
{ value: e.dlp.length ? String(e.dlp.length) : "0", color: e.dlp.length ? theme.bad : void 0, dim: !e.dlp.length },
|
|
2651
|
+
{ value: ago(e.at), dim: true }
|
|
2527
2652
|
];
|
|
2528
2653
|
}
|
|
2529
2654
|
function Detail({ label, value, color }) {
|
|
2530
|
-
return /* @__PURE__ */
|
|
2531
|
-
/* @__PURE__ */
|
|
2532
|
-
/* @__PURE__ */
|
|
2655
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2656
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: label.padEnd(9) }),
|
|
2657
|
+
/* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
|
|
2533
2658
|
] });
|
|
2534
2659
|
}
|
|
2535
2660
|
|
|
2536
2661
|
// src/tui/panels/Agents.tsx
|
|
2537
|
-
import { Box as
|
|
2662
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
2538
2663
|
import { useState as useState7 } from "react";
|
|
2539
|
-
import { Fragment as Fragment5, jsx as
|
|
2664
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2540
2665
|
var statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
|
|
2541
2666
|
var sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
|
|
2542
2667
|
function Bar({ label, value, max = 100, width = 16, color = theme.accent }) {
|
|
2543
2668
|
const filled = max > 0 ? Math.round(Math.min(value, max) / max * width) : 0;
|
|
2544
|
-
return /* @__PURE__ */
|
|
2545
|
-
/* @__PURE__ */
|
|
2546
|
-
/* @__PURE__ */
|
|
2547
|
-
/* @__PURE__ */
|
|
2548
|
-
/* @__PURE__ */
|
|
2669
|
+
return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2670
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
|
|
2671
|
+
/* @__PURE__ */ jsx7(Text7, { color, children: "\u2588".repeat(filled) }),
|
|
2672
|
+
/* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
|
|
2673
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + value })
|
|
2549
2674
|
] });
|
|
2550
2675
|
}
|
|
2551
2676
|
function AgentsPanel({ focused }) {
|
|
@@ -2570,60 +2695,60 @@ function AgentsPanel({ focused }) {
|
|
|
2570
2695
|
const tools = tmap?.tools ?? [];
|
|
2571
2696
|
const resources = tmap?.resources ?? [];
|
|
2572
2697
|
const listW = 30;
|
|
2573
|
-
return /* @__PURE__ */
|
|
2574
|
-
/* @__PURE__ */
|
|
2575
|
-
/* @__PURE__ */
|
|
2576
|
-
agents.slice(0, 16).map((a, i) => /* @__PURE__ */
|
|
2577
|
-
/* @__PURE__ */
|
|
2698
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
2699
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
|
|
2700
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
|
|
2701
|
+
agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
|
|
2702
|
+
/* @__PURE__ */ jsxs7(Text7, { color: statusColor(a.status), children: [
|
|
2578
2703
|
a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
|
|
2579
2704
|
" "
|
|
2580
2705
|
] }),
|
|
2581
|
-
/* @__PURE__ */
|
|
2582
|
-
/* @__PURE__ */
|
|
2706
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
|
|
2707
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `t${a.trust_score}` })
|
|
2583
2708
|
] }, a.session_id))
|
|
2584
2709
|
] }),
|
|
2585
|
-
/* @__PURE__ */
|
|
2586
|
-
/* @__PURE__ */
|
|
2587
|
-
/* @__PURE__ */
|
|
2588
|
-
/* @__PURE__ */
|
|
2710
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
2711
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
2712
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: truncate(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
|
|
2713
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
|
|
2589
2714
|
] }),
|
|
2590
|
-
base?.characterBlurb ? /* @__PURE__ */
|
|
2591
|
-
radar ? /* @__PURE__ */
|
|
2592
|
-
/* @__PURE__ */
|
|
2593
|
-
/* @__PURE__ */
|
|
2594
|
-
/* @__PURE__ */
|
|
2595
|
-
/* @__PURE__ */
|
|
2596
|
-
/* @__PURE__ */
|
|
2597
|
-
/* @__PURE__ */
|
|
2715
|
+
base?.characterBlurb ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate(String(base.characterBlurb), 70) }) : null,
|
|
2716
|
+
radar ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2717
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
|
|
2718
|
+
/* @__PURE__ */ jsx7(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
|
|
2719
|
+
/* @__PURE__ */ jsx7(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
|
|
2720
|
+
/* @__PURE__ */ jsx7(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
|
|
2721
|
+
/* @__PURE__ */ jsx7(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
|
|
2722
|
+
/* @__PURE__ */ jsx7(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
|
|
2598
2723
|
] }) : null,
|
|
2599
|
-
/* @__PURE__ */
|
|
2600
|
-
/* @__PURE__ */
|
|
2724
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2725
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
2601
2726
|
"Anomalies ",
|
|
2602
2727
|
anomalies2.length ? `(${anomalies2.length})` : ""
|
|
2603
2728
|
] }),
|
|
2604
|
-
anomalies2.length === 0 ? /* @__PURE__ */
|
|
2605
|
-
/* @__PURE__ */
|
|
2606
|
-
/* @__PURE__ */
|
|
2607
|
-
/* @__PURE__ */
|
|
2729
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2730
|
+
/* @__PURE__ */ jsx7(Text7, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
|
|
2731
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: String(an.kind).padEnd(12) }),
|
|
2732
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate(String(an.description ?? ""), 44) })
|
|
2608
2733
|
] }, i))
|
|
2609
2734
|
] }),
|
|
2610
|
-
/* @__PURE__ */
|
|
2611
|
-
/* @__PURE__ */
|
|
2612
|
-
tools.slice(0, 4).map((t, i) => /* @__PURE__ */
|
|
2613
|
-
/* @__PURE__ */
|
|
2614
|
-
/* @__PURE__ */
|
|
2735
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
|
|
2736
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Trust map \xB7 top tools" }),
|
|
2737
|
+
tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
2738
|
+
/* @__PURE__ */ jsx7(Text7, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate(String(t.name), 16).padEnd(17)}` }),
|
|
2739
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
|
|
2615
2740
|
] }, i)),
|
|
2616
|
-
resources.slice(0, 3).map((r, i) => /* @__PURE__ */
|
|
2741
|
+
resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
|
|
2617
2742
|
] })
|
|
2618
2743
|
] }) })
|
|
2619
2744
|
] }) });
|
|
2620
2745
|
}
|
|
2621
2746
|
|
|
2622
2747
|
// src/tui/panels/Settings.tsx
|
|
2623
|
-
import { Box as
|
|
2748
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
2624
2749
|
import TextInput5 from "ink-text-input";
|
|
2625
2750
|
import { useState as useState8 } from "react";
|
|
2626
|
-
import { jsx as
|
|
2751
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2627
2752
|
var EVENTS = ["denials", "allowed", "all"];
|
|
2628
2753
|
function alertBodyFor(channel) {
|
|
2629
2754
|
const c2 = channel.trim();
|
|
@@ -2749,7 +2874,7 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
2749
2874
|
const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
|
|
2750
2875
|
const error = localQ.error ?? whQ.error ?? alertQ.error;
|
|
2751
2876
|
const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
|
|
2752
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
2877
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
|
|
2753
2878
|
const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
2754
2879
|
const listBudget = Math.max(4, rows - 8);
|
|
2755
2880
|
const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
|
|
@@ -2757,83 +2882,83 @@ function SettingsPanel({ active: active2, focused }) {
|
|
|
2757
2882
|
const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
|
|
2758
2883
|
return i >= start && i < start + listBudget;
|
|
2759
2884
|
};
|
|
2760
|
-
return /* @__PURE__ */
|
|
2761
|
-
/* @__PURE__ */
|
|
2762
|
-
editing ? /* @__PURE__ */
|
|
2763
|
-
/* @__PURE__ */
|
|
2764
|
-
/* @__PURE__ */
|
|
2765
|
-
] }) : msg ? /* @__PURE__ */
|
|
2766
|
-
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */
|
|
2767
|
-
/* @__PURE__ */
|
|
2885
|
+
return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2886
|
+
/* @__PURE__ */ jsx8(Text8, { 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" }),
|
|
2887
|
+
editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
2888
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
|
|
2889
|
+
/* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
2890
|
+
] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
2891
|
+
inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2892
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
2768
2893
|
"LOCAL LOGS ",
|
|
2769
|
-
/* @__PURE__ */
|
|
2894
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
|
|
2770
2895
|
] }),
|
|
2771
|
-
/* @__PURE__ */
|
|
2772
|
-
/* @__PURE__ */
|
|
2773
|
-
/* @__PURE__ */
|
|
2896
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2897
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
|
|
2898
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(10) }),
|
|
2774
2899
|
onOff(!!local?.enabled),
|
|
2775
|
-
/* @__PURE__ */
|
|
2900
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
|
|
2776
2901
|
] }),
|
|
2777
|
-
/* @__PURE__ */
|
|
2778
|
-
/* @__PURE__ */
|
|
2779
|
-
/* @__PURE__ */
|
|
2780
|
-
local?.path ? /* @__PURE__ */
|
|
2902
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2903
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
|
|
2904
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(10) }),
|
|
2905
|
+
local?.path ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(local.path, cols - 14) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "not set \u2014 enter to edit" })
|
|
2781
2906
|
] })
|
|
2782
2907
|
] }) : null,
|
|
2783
|
-
/* @__PURE__ */
|
|
2784
|
-
/* @__PURE__ */
|
|
2908
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2909
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
2785
2910
|
"WEBHOOKS ",
|
|
2786
|
-
/* @__PURE__ */
|
|
2911
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
2787
2912
|
"\u2014 POST every matching event to your endpoint (",
|
|
2788
2913
|
webhooks.length,
|
|
2789
2914
|
")"
|
|
2790
2915
|
] })
|
|
2791
2916
|
] }),
|
|
2792
|
-
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */
|
|
2793
|
-
/* @__PURE__ */
|
|
2917
|
+
webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2918
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
|
|
2794
2919
|
onOff(wh.enabled),
|
|
2795
|
-
/* @__PURE__ */
|
|
2796
|
-
/* @__PURE__ */
|
|
2920
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
|
|
2921
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(wh.url, cols - 16) })
|
|
2797
2922
|
] }, wh.id)),
|
|
2798
|
-
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */
|
|
2799
|
-
/* @__PURE__ */
|
|
2800
|
-
/* @__PURE__ */
|
|
2923
|
+
inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2924
|
+
/* @__PURE__ */ jsx8(Text8, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
|
|
2925
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
2801
2926
|
] }) : null
|
|
2802
2927
|
] }),
|
|
2803
|
-
/* @__PURE__ */
|
|
2804
|
-
/* @__PURE__ */
|
|
2928
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
2929
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
2805
2930
|
"ALERTS ",
|
|
2806
|
-
/* @__PURE__ */
|
|
2931
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
2807
2932
|
"\u2014 notify on a signal spike (",
|
|
2808
2933
|
alerts.length,
|
|
2809
2934
|
")"
|
|
2810
2935
|
] })
|
|
2811
2936
|
] }),
|
|
2812
|
-
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */
|
|
2813
|
-
/* @__PURE__ */
|
|
2937
|
+
alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
2938
|
+
/* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
|
|
2814
2939
|
onOff(rule.enabled),
|
|
2815
|
-
/* @__PURE__ */
|
|
2816
|
-
/* @__PURE__ */
|
|
2817
|
-
/* @__PURE__ */
|
|
2940
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
|
|
2941
|
+
/* @__PURE__ */ jsx8(Text8, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
|
|
2942
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(chanOf(rule), cols - 26) })
|
|
2818
2943
|
] }, rule.id)),
|
|
2819
|
-
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */
|
|
2820
|
-
/* @__PURE__ */
|
|
2821
|
-
/* @__PURE__ */
|
|
2944
|
+
inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2945
|
+
/* @__PURE__ */ jsx8(Text8, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
|
|
2946
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
|
|
2822
2947
|
] }) : null
|
|
2823
2948
|
] })
|
|
2824
2949
|
] }) });
|
|
2825
2950
|
}
|
|
2826
2951
|
|
|
2827
2952
|
// src/tui/App.tsx
|
|
2828
|
-
import { jsx as
|
|
2953
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2829
2954
|
function LiveHint() {
|
|
2830
|
-
return /* @__PURE__ */
|
|
2831
|
-
/* @__PURE__ */
|
|
2832
|
-
/* @__PURE__ */
|
|
2833
|
-
/* @__PURE__ */
|
|
2834
|
-
/* @__PURE__ */
|
|
2955
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
2956
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
2957
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
2958
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
2959
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
2835
2960
|
"press ",
|
|
2836
|
-
/* @__PURE__ */
|
|
2961
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
2837
2962
|
" to go live"
|
|
2838
2963
|
] }) })
|
|
2839
2964
|
] });
|
|
@@ -2844,7 +2969,6 @@ var SECTIONS = [
|
|
|
2844
2969
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
2845
2970
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
2846
2971
|
{ label: "DLP", Panel: DlpPanel },
|
|
2847
|
-
{ label: "Stats", Panel: StatsPanel },
|
|
2848
2972
|
{ label: "Audit", Panel: AuditPanel },
|
|
2849
2973
|
{ label: "Settings", Panel: SettingsPanel }
|
|
2850
2974
|
];
|
|
@@ -2852,14 +2976,14 @@ var BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8F
|
|
|
2852
2976
|
function Banner({ cols }) {
|
|
2853
2977
|
const wide = cols >= 82;
|
|
2854
2978
|
if (!wide) {
|
|
2855
|
-
return /* @__PURE__ */
|
|
2856
|
-
/* @__PURE__ */
|
|
2857
|
-
/* @__PURE__ */
|
|
2979
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
2980
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
2981
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
|
|
2858
2982
|
] });
|
|
2859
2983
|
}
|
|
2860
|
-
return /* @__PURE__ */
|
|
2861
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
2862
|
-
/* @__PURE__ */
|
|
2984
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
2985
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
2986
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
2863
2987
|
] });
|
|
2864
2988
|
}
|
|
2865
2989
|
function App() {
|
|
@@ -2888,44 +3012,44 @@ function App() {
|
|
|
2888
3012
|
});
|
|
2889
3013
|
const { cols, rows } = useTermSize();
|
|
2890
3014
|
const current = SECTIONS[section];
|
|
2891
|
-
if (help) return /* @__PURE__ */
|
|
3015
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
2892
3016
|
if (current.label === "Live" && focus === "panel") {
|
|
2893
|
-
return /* @__PURE__ */
|
|
3017
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
|
|
2894
3018
|
}
|
|
2895
3019
|
const Panel = current.Panel;
|
|
2896
|
-
return /* @__PURE__ */
|
|
2897
|
-
/* @__PURE__ */
|
|
2898
|
-
/* @__PURE__ */
|
|
2899
|
-
/* @__PURE__ */
|
|
2900
|
-
/* @__PURE__ */
|
|
3020
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
|
|
3021
|
+
/* @__PURE__ */ jsx9(Banner, { cols }),
|
|
3022
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
3023
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
|
|
3024
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
|
|
2901
3025
|
] }),
|
|
2902
|
-
/* @__PURE__ */
|
|
3026
|
+
/* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
2903
3027
|
] });
|
|
2904
3028
|
}
|
|
2905
3029
|
var HELP = [
|
|
2906
3030
|
["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
|
|
2907
3031
|
["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)"]]],
|
|
2908
|
-
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["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"], ["
|
|
3032
|
+
["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["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"], ["s", "save"], ["x", "discard"]]],
|
|
2909
3033
|
["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"]]],
|
|
2910
|
-
["Audit", [["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry
|
|
3034
|
+
["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["[ / ]", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
|
|
2911
3035
|
["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
|
|
2912
3036
|
];
|
|
2913
3037
|
function HelpOverlay({ cols, rows }) {
|
|
2914
|
-
return /* @__PURE__ */
|
|
2915
|
-
/* @__PURE__ */
|
|
2916
|
-
/* @__PURE__ */
|
|
2917
|
-
/* @__PURE__ */
|
|
2918
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
2919
|
-
/* @__PURE__ */
|
|
2920
|
-
/* @__PURE__ */
|
|
3038
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
3039
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
3040
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
3041
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
3042
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3043
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
3044
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
2921
3045
|
] }, k))
|
|
2922
3046
|
] }, group)) }),
|
|
2923
|
-
/* @__PURE__ */
|
|
3047
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
2924
3048
|
] });
|
|
2925
3049
|
}
|
|
2926
3050
|
|
|
2927
3051
|
// src/tui/index.tsx
|
|
2928
|
-
import { jsx as
|
|
3052
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
2929
3053
|
async function launchTui() {
|
|
2930
3054
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
2931
3055
|
process.stderr.write(
|
|
@@ -2939,7 +3063,7 @@ async function launchTui() {
|
|
|
2939
3063
|
}
|
|
2940
3064
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
2941
3065
|
try {
|
|
2942
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
3066
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
|
|
2943
3067
|
await waitUntilExit();
|
|
2944
3068
|
} finally {
|
|
2945
3069
|
process.stdout.write("\x1B[?1049l");
|