@solongate/proxy 0.82.31 → 0.82.32
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/policies.d.ts +63 -0
- package/dist/commands/index.js +4 -0
- package/dist/index.js +595 -447
- package/dist/tui/index.js +583 -443
- package/dist/tui/panels/DryRun.d.ts +5 -0
- package/package.json +1 -1
package/dist/tui/index.js
CHANGED
|
@@ -11,8 +11,8 @@ import { join as join9 } from "path";
|
|
|
11
11
|
import { render } from "ink";
|
|
12
12
|
|
|
13
13
|
// src/tui/App.tsx
|
|
14
|
-
import { Box as
|
|
15
|
-
import { useEffect as
|
|
14
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
|
|
15
|
+
import { useEffect as useEffect8, useState as useState9 } from "react";
|
|
16
16
|
|
|
17
17
|
// src/cli-utils.ts
|
|
18
18
|
var c = {
|
|
@@ -566,6 +566,7 @@ var policies_exports = {};
|
|
|
566
566
|
__export(policies_exports, {
|
|
567
567
|
active: () => active,
|
|
568
568
|
addRule: () => addRule,
|
|
569
|
+
backtest: () => backtest,
|
|
569
570
|
create: () => create,
|
|
570
571
|
dryRun: () => dryRun,
|
|
571
572
|
get: () => get,
|
|
@@ -615,6 +616,9 @@ function setActive(policyId) {
|
|
|
615
616
|
function dryRun(body) {
|
|
616
617
|
return request("POST", "/policies/dry-run", { body });
|
|
617
618
|
}
|
|
619
|
+
function backtest(body) {
|
|
620
|
+
return request("POST", "/policies/backtest", { body });
|
|
621
|
+
}
|
|
618
622
|
|
|
619
623
|
// src/api-client/settings.ts
|
|
620
624
|
var settings_exports = {};
|
|
@@ -1899,11 +1903,167 @@ function LivePanel({ active: active2 }) {
|
|
|
1899
1903
|
}
|
|
1900
1904
|
|
|
1901
1905
|
// src/tui/panels/Policies.tsx
|
|
1902
|
-
import { Box as
|
|
1906
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
1903
1907
|
import TextInput2 from "ink-text-input";
|
|
1908
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
1909
|
+
|
|
1910
|
+
// src/tui/panels/DryRun.tsx
|
|
1911
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
1904
1912
|
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
1905
|
-
import {
|
|
1906
|
-
var
|
|
1913
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1914
|
+
var SAMPLES = [100, 250, 500, 1e3, 2500, 5e3];
|
|
1915
|
+
var pendingPolicyId = null;
|
|
1916
|
+
function requestDryRun(policyId) {
|
|
1917
|
+
pendingPolicyId = policyId;
|
|
1918
|
+
}
|
|
1919
|
+
var pct = (n, d) => d ? (n / d * 100).toFixed(1) + "%" : "0%";
|
|
1920
|
+
function DryRunPanel({ focused }) {
|
|
1921
|
+
const list5 = useLoader(() => api.policies.list());
|
|
1922
|
+
const policies = list5.data?.policies ?? [];
|
|
1923
|
+
const [pi, setPi] = useState3(0);
|
|
1924
|
+
const [si, setSi] = useState3(3);
|
|
1925
|
+
const [res, setRes] = useState3(null);
|
|
1926
|
+
const [running, setRunning] = useState3(false);
|
|
1927
|
+
const [err, setErr] = useState3(null);
|
|
1928
|
+
const [off, setOff] = useState3(0);
|
|
1929
|
+
const { cols, rows } = usePanelSize();
|
|
1930
|
+
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
1931
|
+
function run(policyIdx = pi, sampleIdx = si) {
|
|
1932
|
+
const p = policies[policyIdx];
|
|
1933
|
+
if (!p) return;
|
|
1934
|
+
setRunning(true);
|
|
1935
|
+
setErr(null);
|
|
1936
|
+
setRes(null);
|
|
1937
|
+
setOff(0);
|
|
1938
|
+
api.policies.backtest({ rules: p.rules ?? [], mode: p.mode, limit: SAMPLES[sampleIdx] }).then((r) => setRes(r)).catch((e) => setErr(e instanceof Error ? e.message : String(e))).finally(() => setRunning(false));
|
|
1939
|
+
}
|
|
1940
|
+
useEffect3(() => {
|
|
1941
|
+
if (policies.length === 0 || res || running || err) return;
|
|
1942
|
+
let idx = 0;
|
|
1943
|
+
if (pendingPolicyId) {
|
|
1944
|
+
const found = policies.findIndex((p) => p.id === pendingPolicyId);
|
|
1945
|
+
if (found >= 0) idx = found;
|
|
1946
|
+
pendingPolicyId = null;
|
|
1947
|
+
}
|
|
1948
|
+
setPi(idx);
|
|
1949
|
+
run(idx, si);
|
|
1950
|
+
}, [policies.length]);
|
|
1951
|
+
useInput2((input, key) => {
|
|
1952
|
+
if (!focused) return;
|
|
1953
|
+
if (key.upArrow) setOff((n) => Math.max(0, n - 1));
|
|
1954
|
+
else if (key.downArrow) setOff((n) => n + 1);
|
|
1955
|
+
else if (input === "p") {
|
|
1956
|
+
const n = policies.length ? (pi + 1) % policies.length : 0;
|
|
1957
|
+
setPi(n);
|
|
1958
|
+
run(n, si);
|
|
1959
|
+
} else if (input === "n") {
|
|
1960
|
+
const n = (si + 1) % SAMPLES.length;
|
|
1961
|
+
setSi(n);
|
|
1962
|
+
run(pi, n);
|
|
1963
|
+
} else if (input === "r") run(pi, si);
|
|
1964
|
+
});
|
|
1965
|
+
const w = Math.max(40, cols);
|
|
1966
|
+
const lines = [];
|
|
1967
|
+
const head = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: t }, `h${lines.length}`));
|
|
1968
|
+
const dim = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: t }, `d${lines.length}`));
|
|
1969
|
+
const row = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { wrap: "truncate", children: t }, `r${lines.length}`));
|
|
1970
|
+
const blank = () => lines.push(/* @__PURE__ */ jsx3(Text3, { children: " " }, `b${lines.length}`));
|
|
1971
|
+
if (res) {
|
|
1972
|
+
const s = res.summary;
|
|
1973
|
+
const denyAfter = s.would_deny;
|
|
1974
|
+
const denyBefore = s.would_deny - s.newly_blocked + s.newly_allowed;
|
|
1975
|
+
const before = pct(denyBefore, s.evaluated);
|
|
1976
|
+
const after = pct(denyAfter, s.evaluated);
|
|
1977
|
+
const ppShift = s.evaluated ? ((denyAfter - denyBefore) / s.evaluated * 100).toFixed(1) : "0.0";
|
|
1978
|
+
head("Summary");
|
|
1979
|
+
row(` evaluated ${s.evaluated} would allow ${s.would_allow} would deny ${s.would_deny}`);
|
|
1980
|
+
lines.push(
|
|
1981
|
+
/* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
|
|
1982
|
+
" newly blocked ",
|
|
1983
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: String(s.newly_blocked) }),
|
|
1984
|
+
" newly allowed ",
|
|
1985
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: String(s.newly_allowed) }),
|
|
1986
|
+
" unchanged ",
|
|
1987
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: String(s.unchanged) })
|
|
1988
|
+
] }, `sum${lines.length}`)
|
|
1989
|
+
);
|
|
1990
|
+
row(` deny rate ${before} -> ${after} (${Number(ppShift) > 0 ? "+" : ""}${ppShift}pp)`);
|
|
1991
|
+
blank();
|
|
1992
|
+
if (res.per_rule.length) {
|
|
1993
|
+
head("Per-rule impact");
|
|
1994
|
+
dim(` ${"RULE".padEnd(38)}${"MATCHED".padEnd(9)}${"%".padEnd(8)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
|
|
1995
|
+
for (const r of res.per_rule.slice(0, 30)) {
|
|
1996
|
+
row(` ${truncate(r.rule_id, 36).padEnd(38)}${String(r.matched).padEnd(9)}${pct(r.matched, s.evaluated).padEnd(8)}${String(r.newly_blocked).padEnd(9)}${r.newly_allowed}`);
|
|
1997
|
+
}
|
|
1998
|
+
blank();
|
|
1999
|
+
}
|
|
2000
|
+
if (res.per_agent.length) {
|
|
2001
|
+
head("Per-agent impact");
|
|
2002
|
+
dim(` ${"AGENT".padEnd(24)}${"EVALUATED".padEnd(11)}${"CHANGED".padEnd(9)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
|
|
2003
|
+
for (const a of res.per_agent) {
|
|
2004
|
+
row(` ${truncate(a.agent || "-", 22).padEnd(24)}${String(a.evaluated).padEnd(11)}${String(a.changed).padEnd(9)}${String(a.newly_blocked).padEnd(9)}${a.newly_allowed}`);
|
|
2005
|
+
}
|
|
2006
|
+
blank();
|
|
2007
|
+
}
|
|
2008
|
+
if (res.per_tool.length) {
|
|
2009
|
+
head("Per-tool breakdown");
|
|
2010
|
+
dim(` ${"TOOL".padEnd(24)}${"EVALUATED".padEnd(11)}${"CHANGED".padEnd(9)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
|
|
2011
|
+
for (const t of res.per_tool) {
|
|
2012
|
+
row(` ${truncate(t.tool || "-", 22).padEnd(24)}${String(t.evaluated).padEnd(11)}${String(t.changed).padEnd(9)}${String(t.newly_blocked).padEnd(9)}${t.newly_allowed}`);
|
|
2013
|
+
}
|
|
2014
|
+
blank();
|
|
2015
|
+
}
|
|
2016
|
+
if (res.samples.length) {
|
|
2017
|
+
head(`Changed calls (${res.samples.length})`);
|
|
2018
|
+
for (const c2 of res.samples) {
|
|
2019
|
+
const flip = `${c2.original}->${c2.predicted}`;
|
|
2020
|
+
const newlyAllowed = c2.predicted === "ALLOW";
|
|
2021
|
+
lines.push(
|
|
2022
|
+
/* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
|
|
2023
|
+
" ",
|
|
2024
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: truncate(c2.tool, 16).padEnd(17) }),
|
|
2025
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate(c2.agent || "-", 14).padEnd(15) }),
|
|
2026
|
+
/* @__PURE__ */ jsx3(Text3, { color: newlyAllowed ? theme.ok : theme.bad, children: flip.padEnd(14) }),
|
|
2027
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate(c2.preview.replace(/\s+/g, " "), Math.max(10, w - 50)) })
|
|
2028
|
+
] }, `c${lines.length}`)
|
|
2029
|
+
);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
const headerRows = 3;
|
|
2034
|
+
const budget = Math.max(3, rows - headerRows);
|
|
2035
|
+
const maxOff = Math.max(0, lines.length - budget);
|
|
2036
|
+
const start = Math.min(off, maxOff);
|
|
2037
|
+
const win = lines.slice(start, start + budget);
|
|
2038
|
+
const above = start;
|
|
2039
|
+
const below = Math.max(0, lines.length - (start + budget));
|
|
2040
|
+
const sampleLabel = `${SAMPLES[si]}${res?.sampled != null ? ` (ran ${res.sampled})` : ""}`;
|
|
2041
|
+
return /* @__PURE__ */ jsx3(DataView, { loading: list5.loading && !list5.data, error: list5.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
2042
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
2043
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "Policy Dry Run" }),
|
|
2044
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
2045
|
+
" ",
|
|
2046
|
+
truncate(selected?.name ?? "no policy", 26)
|
|
2047
|
+
] }),
|
|
2048
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
2049
|
+
" mode: ",
|
|
2050
|
+
selected?.mode ?? "-"
|
|
2051
|
+
] }),
|
|
2052
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
2053
|
+
" sample: ",
|
|
2054
|
+
sampleLabel
|
|
2055
|
+
] })
|
|
2056
|
+
] }),
|
|
2057
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 scroll \xB7 p policy \xB7 n sample \xB7 r re-run${above ? ` \xB7 \u25B2${above}` : ""}${below ? ` \xB7 \u25BC${below}` : ""}` : "press \u2192 to open" }),
|
|
2058
|
+
running ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: "replaying recent traffic\u2026" }) : null,
|
|
2059
|
+
err ? /* @__PURE__ */ jsx3(Text3, { color: theme.bad, wrap: "truncate", children: "\u2717 " + err }) : null,
|
|
2060
|
+
!running && !err && !res ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "no result yet \u2014 press r to run" }) : null,
|
|
2061
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: win })
|
|
2062
|
+
] }) });
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
// src/tui/panels/Policies.tsx
|
|
2066
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1907
2067
|
var constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
|
|
1908
2068
|
var setConstr = (r, g, side, val) => {
|
|
1909
2069
|
const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -1941,19 +2101,19 @@ function PoliciesPanel({ focused }) {
|
|
|
1941
2101
|
const activeId = activeQ.data?.policy?.id ?? null;
|
|
1942
2102
|
const activeBy = activeQ.data?.matched_by;
|
|
1943
2103
|
const { rows: panelRows } = usePanelSize();
|
|
1944
|
-
const [view, setView] =
|
|
1945
|
-
const [pi, setPi] =
|
|
1946
|
-
const [ri, setRi] =
|
|
1947
|
-
const [fi, setFi] =
|
|
1948
|
-
const [rules, setRules] =
|
|
1949
|
-
const [mode, setMode] =
|
|
1950
|
-
const [dirty, setDirty] =
|
|
1951
|
-
const [editing, setEditing] =
|
|
1952
|
-
const [editVal, setEditVal] =
|
|
1953
|
-
const [status, setStatus] =
|
|
2104
|
+
const [view, setView] = useState4("list");
|
|
2105
|
+
const [pi, setPi] = useState4(0);
|
|
2106
|
+
const [ri, setRi] = useState4(0);
|
|
2107
|
+
const [fi, setFi] = useState4(0);
|
|
2108
|
+
const [rules, setRules] = useState4([]);
|
|
2109
|
+
const [mode, setMode] = useState4("denylist");
|
|
2110
|
+
const [dirty, setDirty] = useState4(false);
|
|
2111
|
+
const [editing, setEditing] = useState4(false);
|
|
2112
|
+
const [editVal, setEditVal] = useState4("");
|
|
2113
|
+
const [status, setStatus] = useState4(null);
|
|
1954
2114
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
1955
2115
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
1956
|
-
|
|
2116
|
+
useEffect4(() => {
|
|
1957
2117
|
if (detail.data) {
|
|
1958
2118
|
setRules(detail.data.rules);
|
|
1959
2119
|
setMode(detail.data.mode ?? "denylist");
|
|
@@ -1999,7 +2159,7 @@ function PoliciesPanel({ focused }) {
|
|
|
1999
2159
|
setDirty(false);
|
|
2000
2160
|
setStatus("discarded");
|
|
2001
2161
|
};
|
|
2002
|
-
|
|
2162
|
+
useInput3(
|
|
2003
2163
|
(input, key) => {
|
|
2004
2164
|
if (key.ctrl && input === "r" && !editing) {
|
|
2005
2165
|
list5.reload();
|
|
@@ -2060,8 +2220,8 @@ function PoliciesPanel({ focused }) {
|
|
|
2060
2220
|
setView("rule");
|
|
2061
2221
|
} else if (input === "D") {
|
|
2062
2222
|
if (selected) {
|
|
2063
|
-
|
|
2064
|
-
setStatus(
|
|
2223
|
+
requestDryRun(selected.id);
|
|
2224
|
+
setStatus('Sent to Dry Run - open the "Dry Run" section in the left nav for the full report.');
|
|
2065
2225
|
}
|
|
2066
2226
|
} else if (input === "s") void save();
|
|
2067
2227
|
else if (input === "x") discard();
|
|
@@ -2095,35 +2255,35 @@ function PoliciesPanel({ focused }) {
|
|
|
2095
2255
|
const lWin = policies.slice(lStart, lStart + lBudget);
|
|
2096
2256
|
const lAbove = lStart;
|
|
2097
2257
|
const lBelow = Math.max(0, policies.length - (lStart + lBudget));
|
|
2098
|
-
return /* @__PURE__ */
|
|
2099
|
-
/* @__PURE__ */
|
|
2100
|
-
/* @__PURE__ */
|
|
2101
|
-
activeQ.loading && !activeQ.data ? /* @__PURE__ */
|
|
2102
|
-
/* @__PURE__ */
|
|
2258
|
+
return /* @__PURE__ */ jsx4(DataView, { loading: list5.loading && !list5.data, error: list5.error, empty: !!list5.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
2259
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2260
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "active: " }),
|
|
2261
|
+
activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2262
|
+
/* @__PURE__ */ jsxs4(Text4, { color: theme.ok, bold: true, children: [
|
|
2103
2263
|
"\u25CF ",
|
|
2104
2264
|
truncate(activeName, 28)
|
|
2105
2265
|
] }),
|
|
2106
|
-
/* @__PURE__ */
|
|
2107
|
-
] }) : /* @__PURE__ */
|
|
2266
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
2267
|
+
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
2108
2268
|
] }),
|
|
2109
|
-
/* @__PURE__ */
|
|
2110
|
-
/* @__PURE__ */
|
|
2269
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 a activate \xB7 x deactivate \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
|
|
2270
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
|
|
2111
2271
|
const i = lStart + wi;
|
|
2112
|
-
return /* @__PURE__ */
|
|
2272
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
|
|
2113
2273
|
(i === pi ? "\u25B8 " : " ") + truncate(p.name, 26).padEnd(27),
|
|
2114
|
-
/* @__PURE__ */
|
|
2274
|
+
/* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
2115
2275
|
p.mode.padEnd(10),
|
|
2116
2276
|
" ",
|
|
2117
2277
|
p.rules.length,
|
|
2118
2278
|
" rules"
|
|
2119
2279
|
] }),
|
|
2120
|
-
p.id === activeId ? /* @__PURE__ */
|
|
2280
|
+
p.id === activeId ? /* @__PURE__ */ jsx4(Text4, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
|
|
2121
2281
|
] }, p.id);
|
|
2122
2282
|
}) }),
|
|
2123
|
-
status ? /* @__PURE__ */
|
|
2283
|
+
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
2124
2284
|
] }) });
|
|
2125
2285
|
}
|
|
2126
|
-
const dirtyTag = dirty ? /* @__PURE__ */
|
|
2286
|
+
const dirtyTag = dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
|
|
2127
2287
|
if (view === "rules") {
|
|
2128
2288
|
const rStatusLines = status ? status.split("\n").length : 0;
|
|
2129
2289
|
const rHead = 4 + rStatusLines;
|
|
@@ -2133,16 +2293,16 @@ function PoliciesPanel({ focused }) {
|
|
|
2133
2293
|
const rWin = rules.slice(rStart, rStart + rBudget);
|
|
2134
2294
|
const rAbove = rStart;
|
|
2135
2295
|
const rBelow = Math.max(0, rules.length - (rStart + rBudget));
|
|
2136
|
-
return /* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
2138
|
-
/* @__PURE__ */
|
|
2139
|
-
/* @__PURE__ */
|
|
2140
|
-
/* @__PURE__ */
|
|
2141
|
-
/* @__PURE__ */
|
|
2296
|
+
return /* @__PURE__ */ jsx4(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
2297
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2298
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: truncate(selected?.name ?? "", 28) }),
|
|
2299
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " mode: " }),
|
|
2300
|
+
/* @__PURE__ */ jsx4(Text4, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
|
|
2301
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${rules.length} rules` }),
|
|
2142
2302
|
dirtyTag
|
|
2143
2303
|
] }),
|
|
2144
|
-
/* @__PURE__ */
|
|
2145
|
-
/* @__PURE__ */
|
|
2304
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", 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${rAbove ? ` \xB7 \u25B2${rAbove}` : ""}${rBelow ? ` \xB7 \u25BC${rBelow}` : ""}` }),
|
|
2305
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(
|
|
2146
2306
|
Table,
|
|
2147
2307
|
{
|
|
2148
2308
|
columns: [
|
|
@@ -2168,24 +2328,24 @@ function PoliciesPanel({ focused }) {
|
|
|
2168
2328
|
})
|
|
2169
2329
|
}
|
|
2170
2330
|
) }),
|
|
2171
|
-
rules.length === 0 ? /* @__PURE__ */
|
|
2172
|
-
status ? /* @__PURE__ */
|
|
2331
|
+
rules.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "(no rules)" }) : null,
|
|
2332
|
+
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
2173
2333
|
] }) });
|
|
2174
2334
|
}
|
|
2175
2335
|
const rule = rules[ri];
|
|
2176
|
-
return /* @__PURE__ */
|
|
2177
|
-
/* @__PURE__ */
|
|
2178
|
-
/* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */
|
|
2336
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
2337
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2338
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: "Rule " }),
|
|
2339
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
|
|
2180
2340
|
dirtyTag
|
|
2181
2341
|
] }),
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2342
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
|
|
2343
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
2184
2344
|
const val = rule ? f.get(rule) : "";
|
|
2185
2345
|
const active2 = i === fi;
|
|
2186
|
-
return /* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
active2 && editing && f.kind === "text" ? /* @__PURE__ */
|
|
2346
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2347
|
+
/* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) }),
|
|
2348
|
+
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx4(
|
|
2189
2349
|
TextInput2,
|
|
2190
2350
|
{
|
|
2191
2351
|
value: editVal,
|
|
@@ -2195,8 +2355,8 @@ function PoliciesPanel({ focused }) {
|
|
|
2195
2355
|
setEditing(false);
|
|
2196
2356
|
}
|
|
2197
2357
|
}
|
|
2198
|
-
) : /* @__PURE__ */
|
|
2199
|
-
|
|
2358
|
+
) : /* @__PURE__ */ jsx4(
|
|
2359
|
+
Text4,
|
|
2200
2360
|
{
|
|
2201
2361
|
color: f.kind === "effect" ? val === "ALLOW" ? theme.ok : theme.bad : void 0,
|
|
2202
2362
|
dimColor: !val,
|
|
@@ -2205,14 +2365,14 @@ function PoliciesPanel({ focused }) {
|
|
|
2205
2365
|
)
|
|
2206
2366
|
] }, f.label);
|
|
2207
2367
|
}) }),
|
|
2208
|
-
status ? /* @__PURE__ */
|
|
2368
|
+
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
2209
2369
|
] });
|
|
2210
2370
|
}
|
|
2211
2371
|
|
|
2212
2372
|
// src/tui/panels/RateLimit.tsx
|
|
2213
|
-
import { Box as
|
|
2214
|
-
import { useEffect as
|
|
2215
|
-
import { Fragment as Fragment4, jsx as
|
|
2373
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
2374
|
+
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
2375
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2216
2376
|
var MODES = ["off", "detect", "block"];
|
|
2217
2377
|
var FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
|
|
2218
2378
|
var when = (ms) => {
|
|
@@ -2224,11 +2384,11 @@ function RateLimitPanel({ focused }) {
|
|
|
2224
2384
|
const layersQ = useLoader(() => api.settings.getSecurityLayers());
|
|
2225
2385
|
const historyQ = useLoader(() => api.settings.getRateLimitHistory());
|
|
2226
2386
|
const insightsQ = useLoader(() => api.stats.securityInsights(7));
|
|
2227
|
-
const [draft, setDraft] =
|
|
2228
|
-
const [dirty, setDirty] =
|
|
2229
|
-
const [sel, setSel] =
|
|
2230
|
-
const [status, setStatus] =
|
|
2231
|
-
|
|
2387
|
+
const [draft, setDraft] = useState5(null);
|
|
2388
|
+
const [dirty, setDirty] = useState5(false);
|
|
2389
|
+
const [sel, setSel] = useState5(0);
|
|
2390
|
+
const [status, setStatus] = useState5(null);
|
|
2391
|
+
useEffect5(() => {
|
|
2232
2392
|
if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
|
|
2233
2393
|
}, [layersQ.data]);
|
|
2234
2394
|
usePoll(() => {
|
|
@@ -2276,7 +2436,7 @@ function RateLimitPanel({ focused }) {
|
|
|
2276
2436
|
const fi = onField ? selC : -1;
|
|
2277
2437
|
const bcur = onField ? -1 : selC - 4;
|
|
2278
2438
|
const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
|
|
2279
|
-
|
|
2439
|
+
useInput4(
|
|
2280
2440
|
(input, key) => {
|
|
2281
2441
|
const step = key.shift ? 10 : 1;
|
|
2282
2442
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -2295,48 +2455,48 @@ function RateLimitPanel({ focused }) {
|
|
|
2295
2455
|
},
|
|
2296
2456
|
{ isActive: focused }
|
|
2297
2457
|
);
|
|
2298
|
-
return /* @__PURE__ */
|
|
2458
|
+
return /* @__PURE__ */ jsx5(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
|
|
2299
2459
|
const lim = draft.perMinute;
|
|
2300
2460
|
const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
|
|
2301
2461
|
const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
|
|
2302
2462
|
const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
|
|
2303
2463
|
const shown = anomalies2.slice(off, off + burstBudget);
|
|
2304
2464
|
const bw = Math.max(10, cols - 30);
|
|
2305
|
-
return /* @__PURE__ */
|
|
2306
|
-
/* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2309
|
-
/* @__PURE__ */
|
|
2465
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", height: rows, overflow: "hidden", children: [
|
|
2466
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 move (fields + bursts) \xB7 \u2190\u2192 \xB11 \xB7 shift+\u2190\u2192 \xB110 \xB7 s save \xB7 ^R refresh" : "press \u2192 to edit" }),
|
|
2467
|
+
/* @__PURE__ */ jsxs5(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
|
|
2468
|
+
/* @__PURE__ */ jsx5(Text5, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
|
|
2469
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
|
|
2310
2470
|
] }),
|
|
2311
|
-
/* @__PURE__ */
|
|
2312
|
-
/* @__PURE__ */
|
|
2313
|
-
/* @__PURE__ */
|
|
2314
|
-
showExtras && /* @__PURE__ */
|
|
2315
|
-
hasLoad ? /* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
/* @__PURE__ */
|
|
2471
|
+
/* @__PURE__ */ jsx5(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perMinute || "off" }) }),
|
|
2472
|
+
/* @__PURE__ */ jsx5(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perHour || "off" }) }),
|
|
2473
|
+
/* @__PURE__ */ jsx5(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perDay || "off" }) }),
|
|
2474
|
+
showExtras && /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2475
|
+
hasLoad ? /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
2476
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "load now".padEnd(13) }),
|
|
2477
|
+
/* @__PURE__ */ jsx5(Text5, { color: loadPct >= 100 ? theme.bad : loadPct >= 80 ? theme.warn : theme.ok, children: "\u2588".repeat(Math.min(18, Math.round(now / lim * 18))) }),
|
|
2478
|
+
/* @__PURE__ */ jsx5(Text5, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
|
|
2479
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
|
|
2320
2480
|
] }) : null,
|
|
2321
|
-
/* @__PURE__ */
|
|
2481
|
+
/* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
2322
2482
|
"Busiest 7d",
|
|
2323
|
-
/* @__PURE__ */
|
|
2483
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " most calls in one window vs your limit" })
|
|
2324
2484
|
] }),
|
|
2325
|
-
/* @__PURE__ */
|
|
2326
|
-
/* @__PURE__ */
|
|
2327
|
-
/* @__PURE__ */
|
|
2328
|
-
/* @__PURE__ */
|
|
2485
|
+
/* @__PURE__ */ jsx5(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
|
|
2486
|
+
/* @__PURE__ */ jsx5(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
|
|
2487
|
+
/* @__PURE__ */ jsx5(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
|
|
2488
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` })
|
|
2329
2489
|
] }),
|
|
2330
|
-
/* @__PURE__ */
|
|
2331
|
-
/* @__PURE__ */
|
|
2490
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
2491
|
+
/* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
2332
2492
|
`Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
|
|
2333
|
-
maxOff > 0 ? /* @__PURE__ */
|
|
2334
|
-
/* @__PURE__ */
|
|
2493
|
+
maxOff > 0 ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
|
|
2494
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
|
|
2335
2495
|
] }),
|
|
2336
|
-
anomalies2.length === 0 ? /* @__PURE__ */
|
|
2496
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
|
|
2337
2497
|
" ",
|
|
2338
2498
|
insightsQ.loading ? "loading\u2026" : insightsQ.error ? `couldn't load bursts: ${String(insightsQ.error).slice(0, 60)}` : "no bursts in the last 7 days"
|
|
2339
|
-
] }) : /* @__PURE__ */
|
|
2499
|
+
] }) : /* @__PURE__ */ jsx5(
|
|
2340
2500
|
Table,
|
|
2341
2501
|
{
|
|
2342
2502
|
columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
|
|
@@ -2352,13 +2512,13 @@ function RateLimitPanel({ focused }) {
|
|
|
2352
2512
|
}
|
|
2353
2513
|
)
|
|
2354
2514
|
] }),
|
|
2355
|
-
/* @__PURE__ */
|
|
2356
|
-
/* @__PURE__ */
|
|
2357
|
-
/* @__PURE__ */
|
|
2358
|
-
/* @__PURE__ */
|
|
2359
|
-
lastChange ? /* @__PURE__ */
|
|
2360
|
-
dirty ? /* @__PURE__ */
|
|
2361
|
-
status ? /* @__PURE__ */
|
|
2515
|
+
/* @__PURE__ */ jsx5(Text5, { children: " " }),
|
|
2516
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
2517
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "limit now " }),
|
|
2518
|
+
/* @__PURE__ */ jsx5(Text5, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
|
|
2519
|
+
lastChange ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
|
|
2520
|
+
dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
|
|
2521
|
+
status ? /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
|
|
2362
2522
|
] })
|
|
2363
2523
|
] });
|
|
2364
2524
|
})() : null });
|
|
@@ -2366,39 +2526,39 @@ function RateLimitPanel({ focused }) {
|
|
|
2366
2526
|
function BusiestRow({ label, peak, limit }) {
|
|
2367
2527
|
const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
|
|
2368
2528
|
const hot = limit > 0 && peak > limit;
|
|
2369
|
-
return /* @__PURE__ */
|
|
2370
|
-
/* @__PURE__ */
|
|
2371
|
-
/* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2529
|
+
return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
2530
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: (" " + label).padEnd(11) }),
|
|
2531
|
+
/* @__PURE__ */ jsx5(Text5, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
|
|
2532
|
+
/* @__PURE__ */ jsx5(Text5, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
|
|
2373
2533
|
] });
|
|
2374
2534
|
}
|
|
2375
2535
|
function FieldRow({ label, active: active2, children }) {
|
|
2376
|
-
return /* @__PURE__ */
|
|
2377
|
-
/* @__PURE__ */
|
|
2536
|
+
return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
2537
|
+
/* @__PURE__ */ jsx5(Text5, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
|
|
2378
2538
|
children
|
|
2379
2539
|
] });
|
|
2380
2540
|
}
|
|
2381
2541
|
|
|
2382
2542
|
// src/tui/panels/Dlp.tsx
|
|
2383
|
-
import { Box as
|
|
2543
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
2384
2544
|
import TextInput3 from "ink-text-input";
|
|
2385
|
-
import { useEffect as
|
|
2386
|
-
import { jsx as
|
|
2545
|
+
import { useEffect as useEffect6, useState as useState6 } from "react";
|
|
2546
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2387
2547
|
var MODES2 = ["off", "detect", "block"];
|
|
2388
2548
|
function DlpPanel({ focused }) {
|
|
2389
2549
|
const { cols, rows } = usePanelSize();
|
|
2390
2550
|
const q = useLoader(() => api.settings.getSecurityLayers());
|
|
2391
|
-
const [dlp, setDlp] =
|
|
2392
|
-
const [available, setAvailable] =
|
|
2393
|
-
const [ghostOn, setGhostOn] =
|
|
2394
|
-
const [ghostPats, setGhostPats] =
|
|
2395
|
-
const [sel, setSel] =
|
|
2396
|
-
const [dirty, setDirty] =
|
|
2397
|
-
const [status, setStatus] =
|
|
2398
|
-
const [adding, setAdding] =
|
|
2399
|
-
const [newName, setNewName] =
|
|
2400
|
-
const [input, setInput] =
|
|
2401
|
-
|
|
2551
|
+
const [dlp, setDlp] = useState6(null);
|
|
2552
|
+
const [available, setAvailable] = useState6([]);
|
|
2553
|
+
const [ghostOn, setGhostOn] = useState6(false);
|
|
2554
|
+
const [ghostPats, setGhostPats] = useState6([]);
|
|
2555
|
+
const [sel, setSel] = useState6(0);
|
|
2556
|
+
const [dirty, setDirty] = useState6(false);
|
|
2557
|
+
const [status, setStatus] = useState6(null);
|
|
2558
|
+
const [adding, setAdding] = useState6(null);
|
|
2559
|
+
const [newName, setNewName] = useState6("");
|
|
2560
|
+
const [input, setInput] = useState6("");
|
|
2561
|
+
useEffect6(() => {
|
|
2402
2562
|
if (q.data && !dirty) {
|
|
2403
2563
|
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
2404
2564
|
setAvailable(q.data.availablePatterns);
|
|
@@ -2445,7 +2605,7 @@ function DlpPanel({ focused }) {
|
|
|
2445
2605
|
];
|
|
2446
2606
|
const selC = Math.min(sel, Math.max(0, entries.length - 1));
|
|
2447
2607
|
const cur = entries[selC];
|
|
2448
|
-
|
|
2608
|
+
useInput5(
|
|
2449
2609
|
(input2, key) => {
|
|
2450
2610
|
if (!dlp) return;
|
|
2451
2611
|
if (key.ctrl && input2 === "r") {
|
|
@@ -2490,13 +2650,13 @@ function DlpPanel({ focused }) {
|
|
|
2490
2650
|
const enabled = new Set(dlp?.patterns ?? []);
|
|
2491
2651
|
const lines = [];
|
|
2492
2652
|
const header = (key, label, extra) => lines.push({
|
|
2493
|
-
node: /* @__PURE__ */
|
|
2494
|
-
/* @__PURE__ */
|
|
2495
|
-
extra ? /* @__PURE__ */
|
|
2653
|
+
node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
2654
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: label }),
|
|
2655
|
+
extra ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + extra }) : null
|
|
2496
2656
|
] }, key),
|
|
2497
2657
|
entry: -1
|
|
2498
2658
|
});
|
|
2499
|
-
const note = (key, text) => lines.push({ node: /* @__PURE__ */
|
|
2659
|
+
const note = (key, text) => lines.push({ node: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: text }, key), entry: -1 });
|
|
2500
2660
|
let ei = 0;
|
|
2501
2661
|
header("h:builtin", "built-in secret patterns", "space toggles on/off");
|
|
2502
2662
|
available.forEach((p) => {
|
|
@@ -2504,10 +2664,10 @@ function DlpPanel({ focused }) {
|
|
|
2504
2664
|
const isCur = focused && ei === selC;
|
|
2505
2665
|
const idx = ei++;
|
|
2506
2666
|
lines.push({
|
|
2507
|
-
node: /* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
2509
|
-
/* @__PURE__ */
|
|
2510
|
-
/* @__PURE__ */
|
|
2667
|
+
node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
2668
|
+
/* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
2669
|
+
/* @__PURE__ */ jsx6(Text6, { color: on ? theme.ok : theme.dim, children: on ? "\u25CF " : "\u25CB " }),
|
|
2670
|
+
/* @__PURE__ */ jsx6(Text6, { color: on ? void 0 : theme.dim, children: p })
|
|
2511
2671
|
] }, "b:" + p),
|
|
2512
2672
|
entry: idx
|
|
2513
2673
|
});
|
|
@@ -2519,10 +2679,10 @@ function DlpPanel({ focused }) {
|
|
|
2519
2679
|
const isCur = focused && ei === selC;
|
|
2520
2680
|
const idx = ei++;
|
|
2521
2681
|
lines.push({
|
|
2522
|
-
node: /* @__PURE__ */
|
|
2523
|
-
/* @__PURE__ */
|
|
2524
|
-
/* @__PURE__ */
|
|
2525
|
-
/* @__PURE__ */
|
|
2682
|
+
node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
2683
|
+
/* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
2684
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: c2.name }),
|
|
2685
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + truncate(c2.re, Math.max(10, cols - c2.name.length - 8)) })
|
|
2526
2686
|
] }, "c:" + c2.name + i),
|
|
2527
2687
|
entry: idx
|
|
2528
2688
|
});
|
|
@@ -2534,10 +2694,10 @@ function DlpPanel({ focused }) {
|
|
|
2534
2694
|
const isCur = focused && ei === selC;
|
|
2535
2695
|
const idx = ei++;
|
|
2536
2696
|
lines.push({
|
|
2537
|
-
node: /* @__PURE__ */
|
|
2538
|
-
/* @__PURE__ */
|
|
2539
|
-
/* @__PURE__ */
|
|
2540
|
-
!ghostOn ? /* @__PURE__ */
|
|
2697
|
+
node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
|
|
2698
|
+
/* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
|
|
2699
|
+
/* @__PURE__ */ jsx6(Text6, { color: ghostOn ? theme.accent : theme.dim, children: truncate(p, Math.max(10, cols - 6)) }),
|
|
2700
|
+
!ghostOn ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (ghost off)" }) : null
|
|
2541
2701
|
] }, "g:" + p + i),
|
|
2542
2702
|
entry: idx
|
|
2543
2703
|
});
|
|
@@ -2551,17 +2711,17 @@ function DlpPanel({ focused }) {
|
|
|
2551
2711
|
const win = lines.slice(start, start + budget);
|
|
2552
2712
|
const moreAbove = start;
|
|
2553
2713
|
const moreBelow = Math.max(0, lines.length - (start + budget));
|
|
2554
|
-
return /* @__PURE__ */
|
|
2555
|
-
/* @__PURE__ */
|
|
2556
|
-
/* @__PURE__ */
|
|
2557
|
-
/* @__PURE__ */
|
|
2558
|
-
/* @__PURE__ */
|
|
2559
|
-
dirty ? /* @__PURE__ */
|
|
2714
|
+
return /* @__PURE__ */ jsx6(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2715
|
+
/* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
|
|
2716
|
+
/* @__PURE__ */ jsx6(Text6, { children: "mode: " }),
|
|
2717
|
+
/* @__PURE__ */ jsx6(Text6, { color: modeColor(dlp.mode), bold: true, children: dlp.mode }),
|
|
2718
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: dlp.mode === "off" ? " (scanning disabled)" : dlp.mode === "detect" ? " (flag dlp:yes, redact output)" : " (deny + redact secrets)" }),
|
|
2719
|
+
dirty ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
|
|
2560
2720
|
] }),
|
|
2561
|
-
/* @__PURE__ */
|
|
2562
|
-
adding ? /* @__PURE__ */
|
|
2563
|
-
/* @__PURE__ */
|
|
2564
|
-
/* @__PURE__ */
|
|
2721
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 move \xB7 space on/off \xB7 m mode \xB7 a add pattern \xB7 r add route \xB7 d remove \xB7 g ghost \xB7 s save \xB7 ^R refresh${moreAbove ? ` \xB7 \u25B2${moreAbove}` : ""}${moreBelow ? ` \xB7 \u25BC${moreBelow}` : ""}` : "press \u2192 to edit" }),
|
|
2722
|
+
adding ? /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
2723
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: adding === "name" ? "new pattern name: " : adding === "re" ? `pattern for "${newName}" (glob, * = any chars, e.g. sk-* AKIA*): ` : "ghost path to hide (glob, * = any chars, e.g. */.env *.pem): " }),
|
|
2724
|
+
/* @__PURE__ */ jsx6(
|
|
2565
2725
|
TextInput3,
|
|
2566
2726
|
{
|
|
2567
2727
|
value: input,
|
|
@@ -2588,19 +2748,19 @@ function DlpPanel({ focused }) {
|
|
|
2588
2748
|
}
|
|
2589
2749
|
)
|
|
2590
2750
|
] }) }) : null,
|
|
2591
|
-
/* @__PURE__ */
|
|
2592
|
-
status ? /* @__PURE__ */
|
|
2751
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
|
|
2752
|
+
status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
|
|
2593
2753
|
] }) : null });
|
|
2594
2754
|
}
|
|
2595
2755
|
|
|
2596
2756
|
// src/tui/panels/Audit.tsx
|
|
2597
|
-
import { Box as
|
|
2757
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
2598
2758
|
import TextInput4 from "ink-text-input";
|
|
2599
2759
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2600
2760
|
import { homedir as homedir5 } from "os";
|
|
2601
2761
|
import { join as join5 } from "path";
|
|
2602
|
-
import { useState as
|
|
2603
|
-
import { jsx as
|
|
2762
|
+
import { useState as useState7 } from "react";
|
|
2763
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2604
2764
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
2605
2765
|
var SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
2606
2766
|
var SESS_STATUS = [void 0, "active", "idle", "ended"];
|
|
@@ -2708,25 +2868,25 @@ var STATUS_DOT = {
|
|
|
2708
2868
|
};
|
|
2709
2869
|
function AuditPanel({ active: active2, focused }) {
|
|
2710
2870
|
const { cols, rows } = usePanelSize();
|
|
2711
|
-
const [source, setSource] =
|
|
2712
|
-
const [view, setView] =
|
|
2713
|
-
const [di, setDi] =
|
|
2714
|
-
const [gi, setGi] =
|
|
2715
|
-
const [tool, setTool] =
|
|
2716
|
-
const [agent, setAgent] =
|
|
2717
|
-
const [search, setSearch] =
|
|
2718
|
-
const [sessFilter, setSessFilter] =
|
|
2719
|
-
const [page, setPage] =
|
|
2720
|
-
const [sel, setSel] =
|
|
2721
|
-
const [detailScroll, setDetailScroll] =
|
|
2722
|
-
const [editing, setEditing] =
|
|
2723
|
-
const [si, setSi] =
|
|
2724
|
-
const [sessSearch, setSessSearch] =
|
|
2725
|
-
const [sessSel, setSessSel] =
|
|
2726
|
-
const [confirm, setConfirm] =
|
|
2727
|
-
const [msg, setMsg] =
|
|
2728
|
-
const [showHelp, setShowHelp] =
|
|
2729
|
-
const [frozen, setFrozen] =
|
|
2871
|
+
const [source, setSource] = useState7("cloud");
|
|
2872
|
+
const [view, setView] = useState7("logs");
|
|
2873
|
+
const [di, setDi] = useState7(0);
|
|
2874
|
+
const [gi, setGi] = useState7(0);
|
|
2875
|
+
const [tool, setTool] = useState7("");
|
|
2876
|
+
const [agent, setAgent] = useState7("");
|
|
2877
|
+
const [search, setSearch] = useState7("");
|
|
2878
|
+
const [sessFilter, setSessFilter] = useState7("");
|
|
2879
|
+
const [page, setPage] = useState7(0);
|
|
2880
|
+
const [sel, setSel] = useState7(0);
|
|
2881
|
+
const [detailScroll, setDetailScroll] = useState7(0);
|
|
2882
|
+
const [editing, setEditing] = useState7(null);
|
|
2883
|
+
const [si, setSi] = useState7(0);
|
|
2884
|
+
const [sessSearch, setSessSearch] = useState7("");
|
|
2885
|
+
const [sessSel, setSessSel] = useState7(0);
|
|
2886
|
+
const [confirm, setConfirm] = useState7(null);
|
|
2887
|
+
const [msg, setMsg] = useState7(null);
|
|
2888
|
+
const [showHelp, setShowHelp] = useState7(false);
|
|
2889
|
+
const [frozen, setFrozen] = useState7(false);
|
|
2730
2890
|
const toTop = () => setSel(0);
|
|
2731
2891
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2732
2892
|
usePoll(statsQ.reloadQuiet, 15e3, active2 && source === "cloud" && !frozen);
|
|
@@ -2845,7 +3005,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2845
3005
|
};
|
|
2846
3006
|
run().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
|
|
2847
3007
|
};
|
|
2848
|
-
|
|
3008
|
+
useInput6(
|
|
2849
3009
|
(input, key) => {
|
|
2850
3010
|
if (input === " ") {
|
|
2851
3011
|
setFrozen((f) => !f);
|
|
@@ -2979,58 +3139,58 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2979
3139
|
{ isActive: focused && !editing }
|
|
2980
3140
|
);
|
|
2981
3141
|
const localAll = localQ.data ?? [];
|
|
2982
|
-
const strip = source === "cloud" ? /* @__PURE__ */
|
|
2983
|
-
/* @__PURE__ */
|
|
2984
|
-
/* @__PURE__ */
|
|
2985
|
-
/* @__PURE__ */
|
|
3142
|
+
const strip = source === "cloud" ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3143
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
|
|
3144
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " calls \xB7 " }),
|
|
3145
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
|
|
2986
3146
|
statsQ.data?.allowed ?? "\xB7\xB7\xB7",
|
|
2987
3147
|
" allow"
|
|
2988
3148
|
] }),
|
|
2989
|
-
/* @__PURE__ */
|
|
2990
|
-
/* @__PURE__ */
|
|
3149
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
|
|
3150
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
|
|
2991
3151
|
statsQ.data?.denied ?? "\xB7\xB7",
|
|
2992
3152
|
" deny"
|
|
2993
3153
|
] }),
|
|
2994
|
-
/* @__PURE__ */
|
|
2995
|
-
] }) : /* @__PURE__ */
|
|
2996
|
-
/* @__PURE__ */
|
|
2997
|
-
/* @__PURE__ */
|
|
2998
|
-
/* @__PURE__ */
|
|
3154
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies` })
|
|
3155
|
+
] }) : /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3156
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: localAll.length }),
|
|
3157
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " calls in local file \xB7 " }),
|
|
3158
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
|
|
2999
3159
|
localAll.filter((r) => r.decision === "ALLOW").length,
|
|
3000
3160
|
" allow"
|
|
3001
3161
|
] }),
|
|
3002
|
-
/* @__PURE__ */
|
|
3003
|
-
/* @__PURE__ */
|
|
3162
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
|
|
3163
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
|
|
3004
3164
|
localAll.filter((r) => r.decision !== "ALLOW").length,
|
|
3005
3165
|
" deny"
|
|
3006
3166
|
] }),
|
|
3007
|
-
/* @__PURE__ */
|
|
3167
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3008
3168
|
" \xB7 ",
|
|
3009
3169
|
LOCAL_LOG
|
|
3010
3170
|
] })
|
|
3011
3171
|
] });
|
|
3012
|
-
const copyBanner = frozen ? /* @__PURE__ */
|
|
3013
|
-
const srcChip = /* @__PURE__ */
|
|
3014
|
-
/* @__PURE__ */
|
|
3015
|
-
/* @__PURE__ */
|
|
3016
|
-
/* @__PURE__ */
|
|
3172
|
+
const copyBanner = frozen ? /* @__PURE__ */ jsx7(Text7, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, wrap: "truncate", children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : null;
|
|
3173
|
+
const srcChip = /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3174
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "src:" }),
|
|
3175
|
+
/* @__PURE__ */ jsx7(Text7, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
|
|
3176
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " (s) " })
|
|
3017
3177
|
] });
|
|
3018
3178
|
if (showHelp) {
|
|
3019
|
-
const colOf = (groups) => /* @__PURE__ */
|
|
3020
|
-
/* @__PURE__ */
|
|
3021
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
3022
|
-
/* @__PURE__ */
|
|
3023
|
-
/* @__PURE__ */
|
|
3179
|
+
const colOf = (groups) => /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", width: "50%", paddingRight: 2, children: groups.map(([group, keys]) => /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, children: [
|
|
3180
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accent, children: group }),
|
|
3181
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3182
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, children: (" " + k).padEnd(19) }),
|
|
3183
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: desc })
|
|
3024
3184
|
] }, k))
|
|
3025
3185
|
] }, group)) });
|
|
3026
|
-
return /* @__PURE__ */
|
|
3186
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3027
3187
|
copyBanner,
|
|
3028
|
-
/* @__PURE__ */
|
|
3188
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3029
3189
|
"AUDIT \u2014 all keys",
|
|
3030
3190
|
" ",
|
|
3031
|
-
/* @__PURE__ */
|
|
3191
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: frozen ? "\xB7 screen frozen \u2014 space resumes, other keys are locked" : "\xB7 press any key to close \xB7 space = copy mode (works here too)" })
|
|
3032
3192
|
] }),
|
|
3033
|
-
/* @__PURE__ */
|
|
3193
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
|
|
3034
3194
|
colOf(AUDIT_HELP.slice(0, 1)),
|
|
3035
3195
|
colOf(AUDIT_HELP.slice(1))
|
|
3036
3196
|
] })
|
|
@@ -3049,53 +3209,53 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3049
3209
|
const maxScroll = Math.max(0, contentLines.length - bodyRows);
|
|
3050
3210
|
const off = Math.min(detailScroll, maxScroll);
|
|
3051
3211
|
const win = contentLines.slice(off, off + bodyRows);
|
|
3052
|
-
return /* @__PURE__ */
|
|
3212
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3053
3213
|
copyBanner,
|
|
3054
|
-
/* @__PURE__ */
|
|
3055
|
-
/* @__PURE__ */
|
|
3056
|
-
/* @__PURE__ */
|
|
3057
|
-
/* @__PURE__ */
|
|
3058
|
-
/* @__PURE__ */
|
|
3059
|
-
e.dlp.length ? /* @__PURE__ */
|
|
3060
|
-
e.burst ? /* @__PURE__ */
|
|
3061
|
-
/* @__PURE__ */
|
|
3214
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3215
|
+
/* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
|
|
3216
|
+
/* @__PURE__ */ jsx7(Text7, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
|
|
3217
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: " " + e.tool }),
|
|
3218
|
+
/* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
|
|
3219
|
+
e.dlp.length ? /* @__PURE__ */ jsx7(Text7, { color: theme.bad, children: " DLP!" }) : null,
|
|
3220
|
+
e.burst ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " BURST" }) : null,
|
|
3221
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2190 back" })
|
|
3062
3222
|
] }),
|
|
3063
|
-
/* @__PURE__ */
|
|
3064
|
-
/* @__PURE__ */
|
|
3065
|
-
/* @__PURE__ */
|
|
3066
|
-
/* @__PURE__ */
|
|
3067
|
-
/* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3069
|
-
/* @__PURE__ */
|
|
3070
|
-
/* @__PURE__ */
|
|
3071
|
-
/* @__PURE__ */
|
|
3072
|
-
/* @__PURE__ */
|
|
3073
|
-
/* @__PURE__ */
|
|
3074
|
-
/* @__PURE__ */
|
|
3223
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3224
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2502 when " }),
|
|
3225
|
+
/* @__PURE__ */ jsx7(Text7, { children: new Date(e.at).toLocaleString() }),
|
|
3226
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 perm " }),
|
|
3227
|
+
/* @__PURE__ */ jsx7(Text7, { children: e.permission || "\u2014" }),
|
|
3228
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 trust " }),
|
|
3229
|
+
/* @__PURE__ */ jsx7(Text7, { children: e.trust || "\u2014" }),
|
|
3230
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 eval " }),
|
|
3231
|
+
/* @__PURE__ */ jsx7(Text7, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
|
|
3232
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 agent " }),
|
|
3233
|
+
/* @__PURE__ */ jsx7(Text7, { children: e.agent ?? "\u2014" }),
|
|
3234
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502" })
|
|
3075
3235
|
] }),
|
|
3076
|
-
/* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3080
|
-
/* @__PURE__ */
|
|
3081
|
-
/* @__PURE__ */
|
|
3236
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3237
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2502 session " }),
|
|
3238
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: e.session ?? "\u2014" }),
|
|
3239
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 rule " }),
|
|
3240
|
+
/* @__PURE__ */ jsx7(Text7, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
|
|
3241
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502" })
|
|
3082
3242
|
] }),
|
|
3083
|
-
/* @__PURE__ */
|
|
3084
|
-
/* @__PURE__ */
|
|
3085
|
-
/* @__PURE__ */
|
|
3086
|
-
/* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3088
|
-
/* @__PURE__ */
|
|
3243
|
+
/* @__PURE__ */ jsx7(PaneTitle, { label: "FULL CONTENT", extra: `${contentLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""} \xB7 space copy \xB7 \u2190 back`, width: innerW }),
|
|
3244
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: l || " " }, off + i)) }),
|
|
3245
|
+
/* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
|
|
3246
|
+
/* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
|
|
3247
|
+
/* @__PURE__ */ jsx7(Text7, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
|
|
3248
|
+
/* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
|
|
3089
3249
|
] })
|
|
3090
3250
|
] });
|
|
3091
3251
|
}
|
|
3092
|
-
const chip = (label, val, on) => /* @__PURE__ */
|
|
3093
|
-
/* @__PURE__ */
|
|
3252
|
+
const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3253
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3094
3254
|
label,
|
|
3095
3255
|
":"
|
|
3096
3256
|
] }),
|
|
3097
|
-
/* @__PURE__ */
|
|
3098
|
-
/* @__PURE__ */
|
|
3257
|
+
/* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
3258
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " })
|
|
3099
3259
|
] });
|
|
3100
3260
|
if (view === "sessions") {
|
|
3101
3261
|
const headerRows2 = 5 + (editing ? 1 : 0);
|
|
@@ -3103,20 +3263,20 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3103
3263
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
3104
3264
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
3105
3265
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
3106
|
-
return /* @__PURE__ */
|
|
3266
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3107
3267
|
strip,
|
|
3108
3268
|
copyBanner,
|
|
3109
|
-
/* @__PURE__ */
|
|
3269
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3110
3270
|
srcChip,
|
|
3111
|
-
/* @__PURE__ */
|
|
3112
|
-
/* @__PURE__ */
|
|
3271
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
3272
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 logs (v) " }),
|
|
3113
3273
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
3114
3274
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
3115
3275
|
] }),
|
|
3116
|
-
/* @__PURE__ */
|
|
3117
|
-
editing === "sess-search" ? /* @__PURE__ */
|
|
3118
|
-
/* @__PURE__ */
|
|
3119
|
-
/* @__PURE__ */
|
|
3276
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 v logs \xB7 ^R refresh \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
3277
|
+
editing === "sess-search" ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3278
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "search: " }),
|
|
3279
|
+
/* @__PURE__ */ jsx7(
|
|
3120
3280
|
TextInput4,
|
|
3121
3281
|
{
|
|
3122
3282
|
value: sessSearch,
|
|
@@ -3128,8 +3288,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3128
3288
|
}
|
|
3129
3289
|
)
|
|
3130
3290
|
] }) : null,
|
|
3131
|
-
/* @__PURE__ */
|
|
3132
|
-
/* @__PURE__ */
|
|
3291
|
+
/* @__PURE__ */ jsx7(Text7, { 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}` : ""}` }),
|
|
3292
|
+
/* @__PURE__ */ jsx7(
|
|
3133
3293
|
Table,
|
|
3134
3294
|
{
|
|
3135
3295
|
columns: [
|
|
@@ -3160,7 +3320,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3160
3320
|
})
|
|
3161
3321
|
}
|
|
3162
3322
|
),
|
|
3163
|
-
sessionsFiltered.length === 0 ? /* @__PURE__ */
|
|
3323
|
+
sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3164
3324
|
"(no sessions",
|
|
3165
3325
|
source === "local" ? " in the local file" : "",
|
|
3166
3326
|
")"
|
|
@@ -3173,13 +3333,13 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3173
3333
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
3174
3334
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
3175
3335
|
const windowed = pageRows.slice(start, start + listRows);
|
|
3176
|
-
return /* @__PURE__ */
|
|
3336
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3177
3337
|
strip,
|
|
3178
3338
|
copyBanner,
|
|
3179
|
-
/* @__PURE__ */
|
|
3339
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3180
3340
|
srcChip,
|
|
3181
|
-
/* @__PURE__ */
|
|
3182
|
-
/* @__PURE__ */
|
|
3341
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
3342
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 sessions (v) " }),
|
|
3183
3343
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
3184
3344
|
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
3185
3345
|
chip("tool", tool || "\xB7", !!tool),
|
|
@@ -3187,14 +3347,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3187
3347
|
chip("search", search || "\xB7", !!search),
|
|
3188
3348
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
3189
3349
|
] }),
|
|
3190
|
-
/* @__PURE__ */
|
|
3191
|
-
msg ? /* @__PURE__ */
|
|
3192
|
-
editing && editing !== "sess-search" ? /* @__PURE__ */
|
|
3193
|
-
/* @__PURE__ */
|
|
3350
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 v sessions \xB7 ^R refresh \xB7 ? all keys" : "press \u2192 to browse" }),
|
|
3351
|
+
msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
|
|
3352
|
+
editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3353
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
|
|
3194
3354
|
editing,
|
|
3195
3355
|
": "
|
|
3196
3356
|
] }),
|
|
3197
|
-
/* @__PURE__ */
|
|
3357
|
+
/* @__PURE__ */ jsx7(
|
|
3198
3358
|
TextInput4,
|
|
3199
3359
|
{
|
|
3200
3360
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
@@ -3207,9 +3367,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3207
3367
|
}
|
|
3208
3368
|
)
|
|
3209
3369
|
] }) : null,
|
|
3210
|
-
/* @__PURE__ */
|
|
3211
|
-
/* @__PURE__ */
|
|
3212
|
-
pageRows.length === 0 ? /* @__PURE__ */
|
|
3370
|
+
/* @__PURE__ */ jsx7(Text7, { 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` : ""}` }),
|
|
3371
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", overflow: "hidden", children: windowed.map((e, i) => /* @__PURE__ */ jsx7(StreamLine, { e: toStream(e), loc: source === "local", selected: start + i === selClamped && focused, date: true }, e.id)) }),
|
|
3372
|
+
pageRows.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3213
3373
|
"(no entries",
|
|
3214
3374
|
source === "local" ? " in the local file" : "",
|
|
3215
3375
|
" \u2014 c clears filters)"
|
|
@@ -3218,9 +3378,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3218
3378
|
}
|
|
3219
3379
|
|
|
3220
3380
|
// src/tui/panels/Settings.tsx
|
|
3221
|
-
import { Box as
|
|
3381
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
3222
3382
|
import TextInput5 from "ink-text-input";
|
|
3223
|
-
import { useEffect as
|
|
3383
|
+
import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
|
|
3224
3384
|
|
|
3225
3385
|
// src/global-install.ts
|
|
3226
3386
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync2, mkdirSync as mkdirSync4, rmSync } from "fs";
|
|
@@ -3704,7 +3864,7 @@ async function tuiUpdateFlow(onStatus) {
|
|
|
3704
3864
|
}
|
|
3705
3865
|
|
|
3706
3866
|
// src/tui/panels/Settings.tsx
|
|
3707
|
-
import { Fragment as Fragment5, jsx as
|
|
3867
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3708
3868
|
var EVENTS = ["denials", "allowed", "all"];
|
|
3709
3869
|
var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3710
3870
|
var SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
|
|
@@ -3730,16 +3890,16 @@ function SettingsPanel({
|
|
|
3730
3890
|
}) {
|
|
3731
3891
|
void active2;
|
|
3732
3892
|
const { cols, rows } = usePanelSize();
|
|
3733
|
-
const [sel, setSel] =
|
|
3734
|
-
const [editing, setEditing] =
|
|
3735
|
-
const [input, setInput] =
|
|
3736
|
-
const [confirmDel, setConfirmDel] =
|
|
3737
|
-
const [msg, setMsg] =
|
|
3738
|
-
const [busy, setBusy] =
|
|
3739
|
-
const [editor, setEditor] =
|
|
3740
|
-
const [latest, setLatest] =
|
|
3893
|
+
const [sel, setSel] = useState8(0);
|
|
3894
|
+
const [editing, setEditing] = useState8(null);
|
|
3895
|
+
const [input, setInput] = useState8("");
|
|
3896
|
+
const [confirmDel, setConfirmDel] = useState8(null);
|
|
3897
|
+
const [msg, setMsg] = useState8(null);
|
|
3898
|
+
const [busy, setBusy] = useState8(false);
|
|
3899
|
+
const [editor, setEditor] = useState8(null);
|
|
3900
|
+
const [latest, setLatest] = useState8(null);
|
|
3741
3901
|
const ver = currentVersion();
|
|
3742
|
-
|
|
3902
|
+
useEffect7(() => {
|
|
3743
3903
|
let live2 = true;
|
|
3744
3904
|
latestVersion().then((v) => {
|
|
3745
3905
|
if (live2) setLatest(v);
|
|
@@ -3749,26 +3909,26 @@ function SettingsPanel({
|
|
|
3749
3909
|
live2 = false;
|
|
3750
3910
|
};
|
|
3751
3911
|
}, []);
|
|
3752
|
-
const [accounts, setAccounts] =
|
|
3753
|
-
const [login, setLogin] =
|
|
3754
|
-
const [tick, setTick] =
|
|
3912
|
+
const [accounts, setAccounts] = useState8(() => listAccounts());
|
|
3913
|
+
const [login, setLogin] = useState8(null);
|
|
3914
|
+
const [tick, setTick] = useState8(0);
|
|
3755
3915
|
const abort = useRef3(false);
|
|
3756
3916
|
const refreshAccounts = () => setAccounts(listAccounts());
|
|
3757
|
-
const [guardHere, setGuardHere] =
|
|
3758
|
-
const [guardVer, setGuardVer] =
|
|
3759
|
-
const [guardOld, setGuardOld] =
|
|
3917
|
+
const [guardHere, setGuardHere] = useState8(() => isGuardInstalled());
|
|
3918
|
+
const [guardVer, setGuardVer] = useState8(() => installedGuardVersion());
|
|
3919
|
+
const [guardOld, setGuardOld] = useState8(() => guardHookOutdated());
|
|
3760
3920
|
const refreshGuard = () => {
|
|
3761
3921
|
setGuardHere(isGuardInstalled());
|
|
3762
3922
|
setGuardVer(installedGuardVersion());
|
|
3763
3923
|
setGuardOld(guardHookOutdated());
|
|
3764
3924
|
};
|
|
3765
3925
|
const locked = accounts.length === 0;
|
|
3766
|
-
const [srv, setSrv] =
|
|
3767
|
-
|
|
3926
|
+
const [srv, setSrv] = useState8(() => logsServerStatus());
|
|
3927
|
+
useEffect7(() => {
|
|
3768
3928
|
const t = setInterval(() => setSrv(logsServerStatus()), 3e3);
|
|
3769
3929
|
return () => clearInterval(t);
|
|
3770
3930
|
}, []);
|
|
3771
|
-
|
|
3931
|
+
useEffect7(() => {
|
|
3772
3932
|
if (!login || login.phase === "done" || login.phase === "error") return;
|
|
3773
3933
|
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
3774
3934
|
return () => clearInterval(t);
|
|
@@ -3781,7 +3941,7 @@ function SettingsPanel({
|
|
|
3781
3941
|
const local = localQ.data;
|
|
3782
3942
|
const selfProt = selfQ.data;
|
|
3783
3943
|
const autoInstalledRef = useRef3(false);
|
|
3784
|
-
|
|
3944
|
+
useEffect7(() => {
|
|
3785
3945
|
if (autoInstalledRef.current) return;
|
|
3786
3946
|
if (guardHere && guardOld) {
|
|
3787
3947
|
autoInstalledRef.current = true;
|
|
@@ -3793,7 +3953,7 @@ function SettingsPanel({
|
|
|
3793
3953
|
}
|
|
3794
3954
|
}
|
|
3795
3955
|
}, [guardHere, guardOld]);
|
|
3796
|
-
const [hidden, setHidden] =
|
|
3956
|
+
const [hidden, setHidden] = useState8(/* @__PURE__ */ new Set());
|
|
3797
3957
|
const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
|
|
3798
3958
|
const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
|
|
3799
3959
|
const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
|
|
@@ -3974,7 +4134,7 @@ function SettingsPanel({
|
|
|
3974
4134
|
setEditor({ ...editor, target: v, field: 1 });
|
|
3975
4135
|
}
|
|
3976
4136
|
};
|
|
3977
|
-
|
|
4137
|
+
useInput7(
|
|
3978
4138
|
(inp, key) => {
|
|
3979
4139
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
3980
4140
|
if (key.escape) {
|
|
@@ -4107,156 +4267,156 @@ function SettingsPanel({
|
|
|
4107
4267
|
);
|
|
4108
4268
|
const spin = SPIN2[tick % SPIN2.length];
|
|
4109
4269
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
4110
|
-
return /* @__PURE__ */
|
|
4111
|
-
/* @__PURE__ */
|
|
4112
|
-
login.phase === "starting" ? /* @__PURE__ */
|
|
4270
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
4271
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "Add an account" }),
|
|
4272
|
+
login.phase === "starting" ? /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
4113
4273
|
spin,
|
|
4114
4274
|
" starting device pairing\u2026"
|
|
4115
|
-
] }) : /* @__PURE__ */
|
|
4116
|
-
/* @__PURE__ */
|
|
4117
|
-
/* @__PURE__ */
|
|
4118
|
-
/* @__PURE__ */
|
|
4119
|
-
/* @__PURE__ */
|
|
4275
|
+
] }) : /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
4276
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
|
|
4277
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
|
|
4278
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
4279
|
+
/* @__PURE__ */ jsxs8(Text8, { color: theme.warn, children: [
|
|
4120
4280
|
spin,
|
|
4121
4281
|
" waiting for authorization\u2026 "
|
|
4122
4282
|
] }),
|
|
4123
|
-
/* @__PURE__ */
|
|
4283
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "esc to cancel" })
|
|
4124
4284
|
] })
|
|
4125
4285
|
] })
|
|
4126
4286
|
] });
|
|
4127
4287
|
}
|
|
4128
4288
|
if (editor) {
|
|
4129
|
-
const fieldRow = (idx, label, value, hint) => /* @__PURE__ */
|
|
4130
|
-
/* @__PURE__ */
|
|
4131
|
-
/* @__PURE__ */
|
|
4289
|
+
const fieldRow = (idx, label, value, hint) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4290
|
+
/* @__PURE__ */ jsx8(Text8, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
|
|
4291
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(11) }),
|
|
4132
4292
|
value,
|
|
4133
|
-
editor.field === idx && hint ? /* @__PURE__ */
|
|
4293
|
+
editor.field === idx && hint ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + hint }) : null
|
|
4134
4294
|
] });
|
|
4135
|
-
return /* @__PURE__ */
|
|
4136
|
-
/* @__PURE__ */
|
|
4295
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
4296
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
4137
4297
|
editor.id ? "Edit" : "New",
|
|
4138
4298
|
" ",
|
|
4139
4299
|
editor.channel,
|
|
4140
4300
|
" alert"
|
|
4141
4301
|
] }),
|
|
4142
|
-
/* @__PURE__ */
|
|
4143
|
-
editing === "alert-target" ? /* @__PURE__ */
|
|
4144
|
-
/* @__PURE__ */
|
|
4145
|
-
/* @__PURE__ */
|
|
4146
|
-
] }) : msg ? /* @__PURE__ */
|
|
4147
|
-
/* @__PURE__ */
|
|
4148
|
-
fieldRow(0, "target", editor.target ? /* @__PURE__ */
|
|
4149
|
-
fieldRow(1, "signal", /* @__PURE__ */
|
|
4150
|
-
fieldRow(2, "threshold", /* @__PURE__ */
|
|
4151
|
-
fieldRow(3, "window", /* @__PURE__ */
|
|
4152
|
-
editor.id ? fieldRow(4, "status", editor.enabled ? /* @__PURE__ */
|
|
4302
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
|
|
4303
|
+
editing === "alert-target" ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
4304
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
|
|
4305
|
+
/* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
4306
|
+
] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
4307
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
4308
|
+
fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(editor.target, cols - 16) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
|
|
4309
|
+
fieldRow(1, "signal", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
|
|
4310
|
+
fieldRow(2, "threshold", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
|
|
4311
|
+
fieldRow(3, "window", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change"),
|
|
4312
|
+
editor.id ? fieldRow(4, "status", editor.enabled ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on" }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" }), "\u2190\u2192 turn on/off") : null
|
|
4153
4313
|
] }),
|
|
4154
|
-
/* @__PURE__ */
|
|
4314
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: editor.id && !editor.enabled ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "disabled \u2014 this alert will NOT fire until you set status back to on" }) : /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
|
|
4155
4315
|
"fires when ",
|
|
4156
|
-
/* @__PURE__ */
|
|
4316
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
|
|
4157
4317
|
" reach ",
|
|
4158
|
-
/* @__PURE__ */
|
|
4318
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: editor.threshold }),
|
|
4159
4319
|
" within",
|
|
4160
4320
|
" ",
|
|
4161
|
-
/* @__PURE__ */
|
|
4321
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) })
|
|
4162
4322
|
] }) })
|
|
4163
4323
|
] });
|
|
4164
4324
|
}
|
|
4165
4325
|
const loading = !locked && (localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data || guardQ.loading && !guardQ.data || selfQ.loading && !selfQ.data);
|
|
4166
4326
|
const error = locked ? null : localQ.error ?? whQ.error ?? alertQ.error ?? guardQ.error ?? selfQ.error;
|
|
4167
|
-
const onOff = (on) => on ? /* @__PURE__ */
|
|
4327
|
+
const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
|
|
4168
4328
|
const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
4169
4329
|
const isCur = (r) => keyOf(r) === keyOf(cur) && focused;
|
|
4170
|
-
const cursor = (r) => /* @__PURE__ */
|
|
4330
|
+
const cursor = (r) => /* @__PURE__ */ jsx8(Text8, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
|
|
4171
4331
|
const rowLine = (r) => {
|
|
4172
4332
|
switch (r.kind) {
|
|
4173
4333
|
case "acct": {
|
|
4174
4334
|
const a = r.acc;
|
|
4175
4335
|
const isView = viewApiKey ? a.apiKey === viewApiKey : accounts[0]?.apiKey === a.apiKey;
|
|
4176
4336
|
const isActive = isActiveAccount(a.apiKey);
|
|
4177
|
-
return /* @__PURE__ */
|
|
4337
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4178
4338
|
cursor(r),
|
|
4179
|
-
/* @__PURE__ */
|
|
4180
|
-
isView ? /* @__PURE__ */
|
|
4181
|
-
isActive ? /* @__PURE__ */
|
|
4182
|
-
/* @__PURE__ */
|
|
4339
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(acctLabel(a), 30).padEnd(31) }),
|
|
4340
|
+
isView ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
|
|
4341
|
+
isActive ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
|
|
4342
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: a.project ? truncate(a.project, 20) : "" })
|
|
4183
4343
|
] });
|
|
4184
4344
|
}
|
|
4185
4345
|
case "acct-add":
|
|
4186
|
-
return /* @__PURE__ */
|
|
4346
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4187
4347
|
cursor(r),
|
|
4188
|
-
/* @__PURE__ */
|
|
4348
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
|
|
4189
4349
|
] });
|
|
4190
4350
|
case "guard":
|
|
4191
|
-
return /* @__PURE__ */
|
|
4351
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4192
4352
|
cursor(r),
|
|
4193
|
-
/* @__PURE__ */
|
|
4194
|
-
!guardHere ? /* @__PURE__ */
|
|
4195
|
-
/* @__PURE__ */
|
|
4196
|
-
/* @__PURE__ */
|
|
4197
|
-
/* @__PURE__ */
|
|
4198
|
-
] }) : /* @__PURE__ */
|
|
4199
|
-
/* @__PURE__ */
|
|
4200
|
-
/* @__PURE__ */
|
|
4201
|
-
/* @__PURE__ */
|
|
4353
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "guard".padEnd(11) }),
|
|
4354
|
+
!guardHere ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
4355
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.bad, bold: true, children: "removed" }),
|
|
4356
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 " }),
|
|
4357
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: "enter to install" })
|
|
4358
|
+
] }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
4359
|
+
/* @__PURE__ */ jsx8(Text8, { color: guardOld ? theme.warn : theme.ok, children: `v${guardVer ?? "?"}` }),
|
|
4360
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: guardOld ? " \xB7 update available \xB7 enter update" : " (latest) \xB7 enter reinstall" }),
|
|
4361
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 d remove" })
|
|
4202
4362
|
] })
|
|
4203
4363
|
] });
|
|
4204
4364
|
case "self":
|
|
4205
|
-
return /* @__PURE__ */
|
|
4365
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4206
4366
|
cursor(r),
|
|
4207
|
-
/* @__PURE__ */
|
|
4208
|
-
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */
|
|
4209
|
-
/* @__PURE__ */
|
|
4367
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "self-prot".padEnd(11) }),
|
|
4368
|
+
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2026" }),
|
|
4369
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " blocks agents editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
|
|
4210
4370
|
] });
|
|
4211
4371
|
case "ll-enabled":
|
|
4212
|
-
return /* @__PURE__ */
|
|
4372
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4213
4373
|
cursor(r),
|
|
4214
|
-
/* @__PURE__ */
|
|
4374
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(11) }),
|
|
4215
4375
|
onOff(!!local?.enabled),
|
|
4216
|
-
/* @__PURE__ */
|
|
4376
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
|
|
4217
4377
|
] });
|
|
4218
4378
|
case "ll-path":
|
|
4219
|
-
return /* @__PURE__ */
|
|
4379
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4220
4380
|
cursor(r),
|
|
4221
|
-
/* @__PURE__ */
|
|
4222
|
-
local?.path ? /* @__PURE__ */
|
|
4381
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(11) }),
|
|
4382
|
+
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" })
|
|
4223
4383
|
] });
|
|
4224
4384
|
case "ll-server":
|
|
4225
|
-
return /* @__PURE__ */
|
|
4385
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4226
4386
|
cursor(r),
|
|
4227
|
-
/* @__PURE__ */
|
|
4228
|
-
srv.running ? /* @__PURE__ */
|
|
4229
|
-
/* @__PURE__ */
|
|
4387
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "dashboard".padEnd(11) }),
|
|
4388
|
+
srv.running ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "stopped" }),
|
|
4389
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: srv.running ? " survives closing the dataroom \xB7 enter stops" : " enter starts the dashboard local-logs link" })
|
|
4230
4390
|
] });
|
|
4231
4391
|
case "wh":
|
|
4232
|
-
return /* @__PURE__ */
|
|
4392
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4233
4393
|
cursor(r),
|
|
4234
4394
|
onOff(r.wh.enabled),
|
|
4235
|
-
/* @__PURE__ */
|
|
4236
|
-
/* @__PURE__ */
|
|
4395
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + r.wh.events).padEnd(9) }),
|
|
4396
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(r.wh.url, cols - 16) })
|
|
4237
4397
|
] });
|
|
4238
4398
|
case "wh-add":
|
|
4239
|
-
return /* @__PURE__ */
|
|
4399
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4240
4400
|
cursor(r),
|
|
4241
|
-
/* @__PURE__ */
|
|
4401
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
4242
4402
|
] });
|
|
4243
4403
|
case "alert":
|
|
4244
|
-
return /* @__PURE__ */
|
|
4404
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4245
4405
|
cursor(r),
|
|
4246
4406
|
onOff(r.rule.enabled),
|
|
4247
|
-
/* @__PURE__ */
|
|
4248
|
-
/* @__PURE__ */
|
|
4249
|
-
/* @__PURE__ */
|
|
4407
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + SIGNAL_LABEL[r.rule.signal]).padEnd(13) }),
|
|
4408
|
+
/* @__PURE__ */ jsx8(Text8, { children: `\u2265${r.rule.threshold}/${winLabel(r.rule.windowSeconds)} `.padEnd(11) }),
|
|
4409
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(chanOf(r.rule), cols - 34) })
|
|
4250
4410
|
] });
|
|
4251
4411
|
case "alert-add-email":
|
|
4252
|
-
return /* @__PURE__ */
|
|
4412
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4253
4413
|
cursor(r),
|
|
4254
|
-
/* @__PURE__ */
|
|
4414
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add email alert (enter)" })
|
|
4255
4415
|
] });
|
|
4256
4416
|
case "alert-add-tg":
|
|
4257
|
-
return /* @__PURE__ */
|
|
4417
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4258
4418
|
cursor(r),
|
|
4259
|
-
/* @__PURE__ */
|
|
4419
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
|
|
4260
4420
|
] });
|
|
4261
4421
|
}
|
|
4262
4422
|
};
|
|
@@ -4275,39 +4435,39 @@ function SettingsPanel({
|
|
|
4275
4435
|
const sec = sectionOf(r);
|
|
4276
4436
|
if (sec !== lastSec) {
|
|
4277
4437
|
if (lastSec) {
|
|
4278
|
-
lineEls.push(/* @__PURE__ */
|
|
4438
|
+
lineEls.push(/* @__PURE__ */ jsx8(Text8, { children: " " }, "sp:" + sec));
|
|
4279
4439
|
lineKey.push("");
|
|
4280
4440
|
}
|
|
4281
4441
|
lastSec = sec;
|
|
4282
4442
|
lineEls.push(
|
|
4283
|
-
/* @__PURE__ */
|
|
4284
|
-
/* @__PURE__ */
|
|
4285
|
-
/* @__PURE__ */
|
|
4443
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4444
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: sec }),
|
|
4445
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 " + SECTION_DESC[sec] })
|
|
4286
4446
|
] }, "h:" + sec)
|
|
4287
4447
|
);
|
|
4288
4448
|
lineKey.push("");
|
|
4289
4449
|
if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
|
|
4290
4450
|
lineEls.push(
|
|
4291
|
-
/* @__PURE__ */
|
|
4451
|
+
/* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
|
|
4292
4452
|
);
|
|
4293
4453
|
lineKey.push("");
|
|
4294
4454
|
}
|
|
4295
4455
|
}
|
|
4296
|
-
lineEls.push(/* @__PURE__ */
|
|
4456
|
+
lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
|
|
4297
4457
|
lineKey.push(keyOf(r));
|
|
4298
4458
|
});
|
|
4299
4459
|
if (hasEmailAlert && hasTgAlert) {
|
|
4300
4460
|
lineEls.push(
|
|
4301
|
-
/* @__PURE__ */
|
|
4461
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
|
|
4302
4462
|
);
|
|
4303
4463
|
lineKey.push("");
|
|
4304
4464
|
}
|
|
4305
|
-
lineEls.push(/* @__PURE__ */
|
|
4465
|
+
lineEls.push(/* @__PURE__ */ jsx8(Text8, { children: " " }, "ver-sp"));
|
|
4306
4466
|
lineKey.push("");
|
|
4307
4467
|
lineEls.push(
|
|
4308
|
-
/* @__PURE__ */
|
|
4468
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", color: theme.dim, children: [
|
|
4309
4469
|
`solongate v${ver}`,
|
|
4310
|
-
latest ? newerThan(latest, ver) ? /* @__PURE__ */
|
|
4470
|
+
latest ? newerThan(latest, ver) ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: ` \xB7 v${latest} on npm \u2014 auto-updating` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: " \xB7 latest" }) : null
|
|
4311
4471
|
] }, "ver")
|
|
4312
4472
|
);
|
|
4313
4473
|
lineKey.push("");
|
|
@@ -4318,50 +4478,30 @@ function SettingsPanel({
|
|
|
4318
4478
|
const win = lineEls.slice(startL, startL + budget);
|
|
4319
4479
|
const moreAbove = startL;
|
|
4320
4480
|
const moreBelow = Math.max(0, lineEls.length - (startL + budget));
|
|
4321
|
-
return /* @__PURE__ */
|
|
4322
|
-
/* @__PURE__ */
|
|
4323
|
-
editing ? /* @__PURE__ */
|
|
4324
|
-
/* @__PURE__ */
|
|
4325
|
-
/* @__PURE__ */
|
|
4326
|
-
] }) : msg ? /* @__PURE__ */
|
|
4327
|
-
/* @__PURE__ */
|
|
4481
|
+
return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
4482
|
+
/* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.dim, children: focused ? `\u2191\u2193 move \xB7 enter act \xB7 m toggle \xB7 t test \xB7 e edit \xB7 x remove \xB7 d delete \xB7 n add${moreAbove ? ` \xB7 \u25B2${moreAbove}` : ""}${moreBelow ? ` \xB7 \u25BC${moreBelow}` : ""}` : "press \u2192 to configure" }),
|
|
4483
|
+
editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
4484
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
|
|
4485
|
+
/* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
|
|
4486
|
+
] }) : msg ? /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : login?.msg ? /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: login.phase === "error" ? theme.bad : theme.ok, children: login.msg }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
4487
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", height: budget, overflow: "hidden", children: win })
|
|
4328
4488
|
] }) });
|
|
4329
4489
|
}
|
|
4330
4490
|
|
|
4331
4491
|
// src/tui/App.tsx
|
|
4332
|
-
import { jsx as
|
|
4492
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4333
4493
|
function LiveHint() {
|
|
4334
|
-
return /* @__PURE__ */
|
|
4335
|
-
/* @__PURE__ */
|
|
4336
|
-
/* @__PURE__ */
|
|
4337
|
-
/* @__PURE__ */
|
|
4338
|
-
/* @__PURE__ */
|
|
4494
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4495
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
|
|
4496
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
|
|
4497
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
|
|
4498
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
4339
4499
|
"press ",
|
|
4340
|
-
/* @__PURE__ */
|
|
4500
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
4341
4501
|
" to go live"
|
|
4342
4502
|
] }) })
|
|
4343
4503
|
] });
|
|
4344
4504
|
}
|
|
4345
|
-
function DryRunPanel({ focused }) {
|
|
4346
|
-
const [opened, setOpened] = useState8(false);
|
|
4347
|
-
useEffect7(() => {
|
|
4348
|
-
if (focused && !opened) {
|
|
4349
|
-
openBrowser("https://dashboard.solongate.com/playground");
|
|
4350
|
-
setOpened(true);
|
|
4351
|
-
}
|
|
4352
|
-
}, [focused, opened]);
|
|
4353
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
4354
|
-
/* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "Policy Dry Run" }),
|
|
4355
|
-
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Replay a rule set against your real historical traffic and see exactly" }),
|
|
4356
|
-
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "what would change before you ship it." }),
|
|
4357
|
-
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: opened ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "Opened in your browser." }) : /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
4358
|
-
"press ",
|
|
4359
|
-
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
|
|
4360
|
-
" to open the dry-run playground in your browser"
|
|
4361
|
-
] }) }),
|
|
4362
|
-
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Pick any policy and tune the sample there. From Policies, open a policy and press D to dry-run that one." }) })
|
|
4363
|
-
] });
|
|
4364
|
-
}
|
|
4365
4505
|
var SECTIONS = [
|
|
4366
4506
|
{ label: "Live", Panel: LiveHint },
|
|
4367
4507
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
@@ -4375,27 +4515,27 @@ var BANNER_HEX = ["white", "white", "white", "white", "white", "white"];
|
|
|
4375
4515
|
function Banner({ cols }) {
|
|
4376
4516
|
const wide = cols >= 82;
|
|
4377
4517
|
if (!wide) {
|
|
4378
|
-
return /* @__PURE__ */
|
|
4379
|
-
/* @__PURE__ */
|
|
4380
|
-
/* @__PURE__ */
|
|
4518
|
+
return /* @__PURE__ */ jsxs9(Box9, { children: [
|
|
4519
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
|
|
4520
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
|
|
4381
4521
|
] });
|
|
4382
4522
|
}
|
|
4383
|
-
return /* @__PURE__ */
|
|
4384
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
4385
|
-
/* @__PURE__ */
|
|
4523
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
4524
|
+
BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
|
|
4525
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
|
|
4386
4526
|
] });
|
|
4387
4527
|
}
|
|
4388
4528
|
var SETTINGS_SECTION = SECTIONS.findIndex((s) => s.label === "Settings");
|
|
4389
4529
|
function App() {
|
|
4390
4530
|
const { exit } = useApp();
|
|
4391
|
-
const [accounts, setAccounts] =
|
|
4392
|
-
const [viewKey, setViewKey] =
|
|
4393
|
-
const [viewNonce, setViewNonce] =
|
|
4394
|
-
const [section, setSection] =
|
|
4395
|
-
const [focus, setFocus] =
|
|
4396
|
-
const [help, setHelp] =
|
|
4397
|
-
const [update2, setUpdate] =
|
|
4398
|
-
|
|
4531
|
+
const [accounts, setAccounts] = useState9(() => listAccounts());
|
|
4532
|
+
const [viewKey, setViewKey] = useState9(() => accounts[0]?.apiKey);
|
|
4533
|
+
const [viewNonce, setViewNonce] = useState9(0);
|
|
4534
|
+
const [section, setSection] = useState9(accounts.length === 0 ? SETTINGS_SECTION : 0);
|
|
4535
|
+
const [focus, setFocus] = useState9("nav");
|
|
4536
|
+
const [help, setHelp] = useState9(false);
|
|
4537
|
+
const [update2, setUpdate] = useState9({ kind: "idle" });
|
|
4538
|
+
useEffect8(() => {
|
|
4399
4539
|
let alive = true;
|
|
4400
4540
|
const run = () => void tuiUpdateFlow((s) => alive && setUpdate(s));
|
|
4401
4541
|
run();
|
|
@@ -4408,7 +4548,7 @@ function App() {
|
|
|
4408
4548
|
const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
|
|
4409
4549
|
const cur = accounts[acctIdx];
|
|
4410
4550
|
const acctLabel2 = cur ? cur.email || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}` : "not logged in";
|
|
4411
|
-
|
|
4551
|
+
useEffect8(() => {
|
|
4412
4552
|
if (!cur || cur.email) return;
|
|
4413
4553
|
let alive = true;
|
|
4414
4554
|
api.auth.me().then((r) => {
|
|
@@ -4444,7 +4584,7 @@ function App() {
|
|
|
4444
4584
|
};
|
|
4445
4585
|
const locked = accounts.length === 0;
|
|
4446
4586
|
const effectiveSection = locked ? SETTINGS_SECTION : section;
|
|
4447
|
-
|
|
4587
|
+
useInput8((input, key) => {
|
|
4448
4588
|
if (help) {
|
|
4449
4589
|
setHelp(false);
|
|
4450
4590
|
return;
|
|
@@ -4472,33 +4612,33 @@ function App() {
|
|
|
4472
4612
|
});
|
|
4473
4613
|
const { cols, rows } = useTermSize();
|
|
4474
4614
|
const current = SECTIONS[effectiveSection];
|
|
4475
|
-
if (help) return /* @__PURE__ */
|
|
4615
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
4476
4616
|
if (current.label === "Live" && focus === "panel") {
|
|
4477
|
-
return /* @__PURE__ */
|
|
4617
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
|
|
4478
4618
|
}
|
|
4479
4619
|
const Panel = current.Panel;
|
|
4480
|
-
const panelBody = current.label === "Settings" ? /* @__PURE__ */
|
|
4620
|
+
const panelBody = current.label === "Settings" ? /* @__PURE__ */ jsx9(SettingsPanel, { active: true, focused: focus === "panel", viewApiKey: viewKey, onView: viewAccount, onAccountsChanged: syncAccounts }) : /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" });
|
|
4481
4621
|
return (
|
|
4482
4622
|
// height rows-1 (not rows): once total output reaches stdout.rows ink stops
|
|
4483
4623
|
// diffing and clearTerminal-repaints every frame, which slides the banner.
|
|
4484
4624
|
// overflow hidden makes it a hard guarantee even if a panel over-renders.
|
|
4485
|
-
/* @__PURE__ */
|
|
4486
|
-
/* @__PURE__ */
|
|
4487
|
-
/* @__PURE__ */
|
|
4488
|
-
/* @__PURE__ */
|
|
4489
|
-
/* @__PURE__ */
|
|
4490
|
-
locked ? /* @__PURE__ */
|
|
4491
|
-
update2.kind === "updating" ? /* @__PURE__ */
|
|
4625
|
+
/* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, paddingTop: 1, overflow: "hidden", children: [
|
|
4626
|
+
/* @__PURE__ */ jsx9(Banner, { cols }),
|
|
4627
|
+
/* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
|
|
4628
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
|
|
4629
|
+
/* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
|
|
4630
|
+
locked ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 log in from Settings to unlock the dataroom" }) : accounts.length > 1 ? /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: ` (${acctIdx + 1}/${accounts.length} \xB7 a switch \xB7 Settings to manage)` }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \xB7 Settings to add another" }),
|
|
4631
|
+
update2.kind === "updating" ? /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: ` \u2191 updating to v${update2.version}\u2026` }) : update2.kind === "updated" ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, bold: true, children: ` \u2191 v${update2.version} installed \u2014 restart (q, then solongate) to apply` }) : null
|
|
4492
4632
|
] }),
|
|
4493
|
-
/* @__PURE__ */
|
|
4494
|
-
/* @__PURE__ */
|
|
4633
|
+
/* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
|
|
4634
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
|
|
4495
4635
|
const isCur = i === effectiveSection;
|
|
4496
4636
|
const disabled = locked && s.label !== "Settings";
|
|
4497
|
-
return /* @__PURE__ */
|
|
4637
|
+
return /* @__PURE__ */ jsx9(Text9, { color: isCur ? theme.accentBright : disabled ? theme.dim : void 0, bold: isCur, dimColor: disabled, children: (isCur ? "\u25B8 " : disabled ? "\u2298 " : " ") + s.label }, s.label);
|
|
4498
4638
|
}) }),
|
|
4499
|
-
/* @__PURE__ */
|
|
4639
|
+
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1, minWidth: 0, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, overflow: "hidden", children: panelBody })
|
|
4500
4640
|
] }, viewNonce),
|
|
4501
|
-
/* @__PURE__ */
|
|
4641
|
+
/* @__PURE__ */ jsx9(Box9, { children: locked ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2192/enter", "open Settings"], ["n", "log in"], ["q", "quit"]] }) : focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ...accounts.length > 1 ? [["a", "account"]] : [], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
|
|
4502
4642
|
] })
|
|
4503
4643
|
);
|
|
4504
4644
|
}
|
|
@@ -4513,21 +4653,21 @@ var HELP = [
|
|
|
4513
4653
|
["Accounts", [["a", "switch account (view another account logged in on this device)"], ["", "the header shows which account you are viewing; guard/logging keep the active key"]]]
|
|
4514
4654
|
];
|
|
4515
4655
|
function HelpOverlay({ cols, rows }) {
|
|
4516
|
-
return /* @__PURE__ */
|
|
4517
|
-
/* @__PURE__ */
|
|
4518
|
-
/* @__PURE__ */
|
|
4519
|
-
/* @__PURE__ */
|
|
4520
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
4521
|
-
/* @__PURE__ */
|
|
4522
|
-
/* @__PURE__ */
|
|
4656
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
|
|
4657
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
|
|
4658
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
4659
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
|
|
4660
|
+
keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
4661
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
|
|
4662
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
|
|
4523
4663
|
] }, k))
|
|
4524
4664
|
] }, group)) }),
|
|
4525
|
-
/* @__PURE__ */
|
|
4665
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
4526
4666
|
] });
|
|
4527
4667
|
}
|
|
4528
4668
|
|
|
4529
4669
|
// src/tui/index.tsx
|
|
4530
|
-
import { jsx as
|
|
4670
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
4531
4671
|
async function launchTui() {
|
|
4532
4672
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
4533
4673
|
process.stderr.write(
|
|
@@ -4552,7 +4692,7 @@ async function launchTui() {
|
|
|
4552
4692
|
console.info = toFile("info");
|
|
4553
4693
|
console.debug = toFile("debug");
|
|
4554
4694
|
try {
|
|
4555
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
4695
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}), { patchConsole: false });
|
|
4556
4696
|
await waitUntilExit();
|
|
4557
4697
|
} finally {
|
|
4558
4698
|
Object.assign(console, saved);
|