@solongate/proxy 0.82.30 → 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 +598 -438
- package/dist/tui/index.js +586 -435
- 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();
|
|
@@ -2032,13 +2192,6 @@ function PoliciesPanel({ focused }) {
|
|
|
2032
2192
|
return;
|
|
2033
2193
|
}
|
|
2034
2194
|
if (view === "rules") {
|
|
2035
|
-
if (key.ctrl && input === "d") {
|
|
2036
|
-
if (selected) {
|
|
2037
|
-
openBrowser(`${DASHBOARD_URL}/playground?policy=${encodeURIComponent(selected.id)}`);
|
|
2038
|
-
setStatus("Opened the dry-run playground in your browser.");
|
|
2039
|
-
}
|
|
2040
|
-
return;
|
|
2041
|
-
}
|
|
2042
2195
|
if (key.leftArrow) return setView("list"), void setStatus(null);
|
|
2043
2196
|
if (key.upArrow) setRi((n) => Math.max(0, n - 1));
|
|
2044
2197
|
else if (key.downArrow) setRi((n) => Math.min(rules.length - 1, n + 1));
|
|
@@ -2066,13 +2219,10 @@ function PoliciesPanel({ focused }) {
|
|
|
2066
2219
|
setFi(0);
|
|
2067
2220
|
setView("rule");
|
|
2068
2221
|
} else if (input === "D") {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
newly allowed ${res.newly_allowed} newly blocked ${res.newly_blocked} unchanged ${res.unchanged}` + (res.sample_newly_blocked?.length ? `
|
|
2074
|
-
now blocked e.g.: ${res.sample_newly_blocked.slice(0, 3).map((s) => truncate(s.tool, 24)).join(", ")}` : "")
|
|
2075
|
-
)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
2222
|
+
if (selected) {
|
|
2223
|
+
requestDryRun(selected.id);
|
|
2224
|
+
setStatus('Sent to Dry Run - open the "Dry Run" section in the left nav for the full report.');
|
|
2225
|
+
}
|
|
2076
2226
|
} else if (input === "s") void save();
|
|
2077
2227
|
else if (input === "x") discard();
|
|
2078
2228
|
return;
|
|
@@ -2105,35 +2255,35 @@ function PoliciesPanel({ focused }) {
|
|
|
2105
2255
|
const lWin = policies.slice(lStart, lStart + lBudget);
|
|
2106
2256
|
const lAbove = lStart;
|
|
2107
2257
|
const lBelow = Math.max(0, policies.length - (lStart + lBudget));
|
|
2108
|
-
return /* @__PURE__ */
|
|
2109
|
-
/* @__PURE__ */
|
|
2110
|
-
/* @__PURE__ */
|
|
2111
|
-
activeQ.loading && !activeQ.data ? /* @__PURE__ */
|
|
2112
|
-
/* @__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: [
|
|
2113
2263
|
"\u25CF ",
|
|
2114
2264
|
truncate(activeName, 28)
|
|
2115
2265
|
] }),
|
|
2116
|
-
/* @__PURE__ */
|
|
2117
|
-
] }) : /* @__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" })
|
|
2118
2268
|
] }),
|
|
2119
|
-
/* @__PURE__ */
|
|
2120
|
-
/* @__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) => {
|
|
2121
2271
|
const i = lStart + wi;
|
|
2122
|
-
return /* @__PURE__ */
|
|
2272
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
|
|
2123
2273
|
(i === pi ? "\u25B8 " : " ") + truncate(p.name, 26).padEnd(27),
|
|
2124
|
-
/* @__PURE__ */
|
|
2274
|
+
/* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
|
|
2125
2275
|
p.mode.padEnd(10),
|
|
2126
2276
|
" ",
|
|
2127
2277
|
p.rules.length,
|
|
2128
2278
|
" rules"
|
|
2129
2279
|
] }),
|
|
2130
|
-
p.id === activeId ? /* @__PURE__ */
|
|
2280
|
+
p.id === activeId ? /* @__PURE__ */ jsx4(Text4, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
|
|
2131
2281
|
] }, p.id);
|
|
2132
2282
|
}) }),
|
|
2133
|
-
status ? /* @__PURE__ */
|
|
2283
|
+
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
2134
2284
|
] }) });
|
|
2135
2285
|
}
|
|
2136
|
-
const dirtyTag = dirty ? /* @__PURE__ */
|
|
2286
|
+
const dirtyTag = dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
|
|
2137
2287
|
if (view === "rules") {
|
|
2138
2288
|
const rStatusLines = status ? status.split("\n").length : 0;
|
|
2139
2289
|
const rHead = 4 + rStatusLines;
|
|
@@ -2143,16 +2293,16 @@ function PoliciesPanel({ focused }) {
|
|
|
2143
2293
|
const rWin = rules.slice(rStart, rStart + rBudget);
|
|
2144
2294
|
const rAbove = rStart;
|
|
2145
2295
|
const rBelow = Math.max(0, rules.length - (rStart + rBudget));
|
|
2146
|
-
return /* @__PURE__ */
|
|
2147
|
-
/* @__PURE__ */
|
|
2148
|
-
/* @__PURE__ */
|
|
2149
|
-
/* @__PURE__ */
|
|
2150
|
-
/* @__PURE__ */
|
|
2151
|
-
/* @__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` }),
|
|
2152
2302
|
dirtyTag
|
|
2153
2303
|
] }),
|
|
2154
|
-
/* @__PURE__ */
|
|
2155
|
-
/* @__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(
|
|
2156
2306
|
Table,
|
|
2157
2307
|
{
|
|
2158
2308
|
columns: [
|
|
@@ -2178,24 +2328,24 @@ function PoliciesPanel({ focused }) {
|
|
|
2178
2328
|
})
|
|
2179
2329
|
}
|
|
2180
2330
|
) }),
|
|
2181
|
-
rules.length === 0 ? /* @__PURE__ */
|
|
2182
|
-
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
|
|
2183
2333
|
] }) });
|
|
2184
2334
|
}
|
|
2185
2335
|
const rule = rules[ri];
|
|
2186
|
-
return /* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
/* @__PURE__ */
|
|
2189
|
-
/* @__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 ?? "" }),
|
|
2190
2340
|
dirtyTag
|
|
2191
2341
|
] }),
|
|
2192
|
-
/* @__PURE__ */
|
|
2193
|
-
/* @__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) => {
|
|
2194
2344
|
const val = rule ? f.get(rule) : "";
|
|
2195
2345
|
const active2 = i === fi;
|
|
2196
|
-
return /* @__PURE__ */
|
|
2197
|
-
/* @__PURE__ */
|
|
2198
|
-
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(
|
|
2199
2349
|
TextInput2,
|
|
2200
2350
|
{
|
|
2201
2351
|
value: editVal,
|
|
@@ -2205,8 +2355,8 @@ function PoliciesPanel({ focused }) {
|
|
|
2205
2355
|
setEditing(false);
|
|
2206
2356
|
}
|
|
2207
2357
|
}
|
|
2208
|
-
) : /* @__PURE__ */
|
|
2209
|
-
|
|
2358
|
+
) : /* @__PURE__ */ jsx4(
|
|
2359
|
+
Text4,
|
|
2210
2360
|
{
|
|
2211
2361
|
color: f.kind === "effect" ? val === "ALLOW" ? theme.ok : theme.bad : void 0,
|
|
2212
2362
|
dimColor: !val,
|
|
@@ -2215,14 +2365,14 @@ function PoliciesPanel({ focused }) {
|
|
|
2215
2365
|
)
|
|
2216
2366
|
] }, f.label);
|
|
2217
2367
|
}) }),
|
|
2218
|
-
status ? /* @__PURE__ */
|
|
2368
|
+
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
2219
2369
|
] });
|
|
2220
2370
|
}
|
|
2221
2371
|
|
|
2222
2372
|
// src/tui/panels/RateLimit.tsx
|
|
2223
|
-
import { Box as
|
|
2224
|
-
import { useEffect as
|
|
2225
|
-
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";
|
|
2226
2376
|
var MODES = ["off", "detect", "block"];
|
|
2227
2377
|
var FIELDS2 = ["mode", "perMinute", "perHour", "perDay"];
|
|
2228
2378
|
var when = (ms) => {
|
|
@@ -2234,11 +2384,11 @@ function RateLimitPanel({ focused }) {
|
|
|
2234
2384
|
const layersQ = useLoader(() => api.settings.getSecurityLayers());
|
|
2235
2385
|
const historyQ = useLoader(() => api.settings.getRateLimitHistory());
|
|
2236
2386
|
const insightsQ = useLoader(() => api.stats.securityInsights(7));
|
|
2237
|
-
const [draft, setDraft] =
|
|
2238
|
-
const [dirty, setDirty] =
|
|
2239
|
-
const [sel, setSel] =
|
|
2240
|
-
const [status, setStatus] =
|
|
2241
|
-
|
|
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(() => {
|
|
2242
2392
|
if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
|
|
2243
2393
|
}, [layersQ.data]);
|
|
2244
2394
|
usePoll(() => {
|
|
@@ -2286,7 +2436,7 @@ function RateLimitPanel({ focused }) {
|
|
|
2286
2436
|
const fi = onField ? selC : -1;
|
|
2287
2437
|
const bcur = onField ? -1 : selC - 4;
|
|
2288
2438
|
const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
|
|
2289
|
-
|
|
2439
|
+
useInput4(
|
|
2290
2440
|
(input, key) => {
|
|
2291
2441
|
const step = key.shift ? 10 : 1;
|
|
2292
2442
|
if (key.upArrow) setSel((n) => Math.max(0, n - 1));
|
|
@@ -2305,48 +2455,48 @@ function RateLimitPanel({ focused }) {
|
|
|
2305
2455
|
},
|
|
2306
2456
|
{ isActive: focused }
|
|
2307
2457
|
);
|
|
2308
|
-
return /* @__PURE__ */
|
|
2458
|
+
return /* @__PURE__ */ jsx5(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
|
|
2309
2459
|
const lim = draft.perMinute;
|
|
2310
2460
|
const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
|
|
2311
2461
|
const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
|
|
2312
2462
|
const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
|
|
2313
2463
|
const shown = anomalies2.slice(off, off + burstBudget);
|
|
2314
2464
|
const bw = Math.max(10, cols - 30);
|
|
2315
|
-
return /* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
/* @__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" })
|
|
2320
2470
|
] }),
|
|
2321
|
-
/* @__PURE__ */
|
|
2322
|
-
/* @__PURE__ */
|
|
2323
|
-
/* @__PURE__ */
|
|
2324
|
-
showExtras && /* @__PURE__ */
|
|
2325
|
-
hasLoad ? /* @__PURE__ */
|
|
2326
|
-
/* @__PURE__ */
|
|
2327
|
-
/* @__PURE__ */
|
|
2328
|
-
/* @__PURE__ */
|
|
2329
|
-
/* @__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}%)` })
|
|
2330
2480
|
] }) : null,
|
|
2331
|
-
/* @__PURE__ */
|
|
2481
|
+
/* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
2332
2482
|
"Busiest 7d",
|
|
2333
|
-
/* @__PURE__ */
|
|
2483
|
+
/* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " most calls in one window vs your limit" })
|
|
2334
2484
|
] }),
|
|
2335
|
-
/* @__PURE__ */
|
|
2336
|
-
/* @__PURE__ */
|
|
2337
|
-
/* @__PURE__ */
|
|
2338
|
-
/* @__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` })
|
|
2339
2489
|
] }),
|
|
2340
|
-
/* @__PURE__ */
|
|
2341
|
-
/* @__PURE__ */
|
|
2490
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
2491
|
+
/* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
|
|
2342
2492
|
`Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
|
|
2343
|
-
maxOff > 0 ? /* @__PURE__ */
|
|
2344
|
-
/* @__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)" })
|
|
2345
2495
|
] }),
|
|
2346
|
-
anomalies2.length === 0 ? /* @__PURE__ */
|
|
2496
|
+
anomalies2.length === 0 ? /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
|
|
2347
2497
|
" ",
|
|
2348
2498
|
insightsQ.loading ? "loading\u2026" : insightsQ.error ? `couldn't load bursts: ${String(insightsQ.error).slice(0, 60)}` : "no bursts in the last 7 days"
|
|
2349
|
-
] }) : /* @__PURE__ */
|
|
2499
|
+
] }) : /* @__PURE__ */ jsx5(
|
|
2350
2500
|
Table,
|
|
2351
2501
|
{
|
|
2352
2502
|
columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
|
|
@@ -2362,13 +2512,13 @@ function RateLimitPanel({ focused }) {
|
|
|
2362
2512
|
}
|
|
2363
2513
|
)
|
|
2364
2514
|
] }),
|
|
2365
|
-
/* @__PURE__ */
|
|
2366
|
-
/* @__PURE__ */
|
|
2367
|
-
/* @__PURE__ */
|
|
2368
|
-
/* @__PURE__ */
|
|
2369
|
-
lastChange ? /* @__PURE__ */
|
|
2370
|
-
dirty ? /* @__PURE__ */
|
|
2371
|
-
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
|
|
2372
2522
|
] })
|
|
2373
2523
|
] });
|
|
2374
2524
|
})() : null });
|
|
@@ -2376,39 +2526,39 @@ function RateLimitPanel({ focused }) {
|
|
|
2376
2526
|
function BusiestRow({ label, peak, limit }) {
|
|
2377
2527
|
const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
|
|
2378
2528
|
const hot = limit > 0 && peak > limit;
|
|
2379
|
-
return /* @__PURE__ */
|
|
2380
|
-
/* @__PURE__ */
|
|
2381
|
-
/* @__PURE__ */
|
|
2382
|
-
/* @__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 })
|
|
2383
2533
|
] });
|
|
2384
2534
|
}
|
|
2385
2535
|
function FieldRow({ label, active: active2, children }) {
|
|
2386
|
-
return /* @__PURE__ */
|
|
2387
|
-
/* @__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) }),
|
|
2388
2538
|
children
|
|
2389
2539
|
] });
|
|
2390
2540
|
}
|
|
2391
2541
|
|
|
2392
2542
|
// src/tui/panels/Dlp.tsx
|
|
2393
|
-
import { Box as
|
|
2543
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
2394
2544
|
import TextInput3 from "ink-text-input";
|
|
2395
|
-
import { useEffect as
|
|
2396
|
-
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";
|
|
2397
2547
|
var MODES2 = ["off", "detect", "block"];
|
|
2398
2548
|
function DlpPanel({ focused }) {
|
|
2399
2549
|
const { cols, rows } = usePanelSize();
|
|
2400
2550
|
const q = useLoader(() => api.settings.getSecurityLayers());
|
|
2401
|
-
const [dlp, setDlp] =
|
|
2402
|
-
const [available, setAvailable] =
|
|
2403
|
-
const [ghostOn, setGhostOn] =
|
|
2404
|
-
const [ghostPats, setGhostPats] =
|
|
2405
|
-
const [sel, setSel] =
|
|
2406
|
-
const [dirty, setDirty] =
|
|
2407
|
-
const [status, setStatus] =
|
|
2408
|
-
const [adding, setAdding] =
|
|
2409
|
-
const [newName, setNewName] =
|
|
2410
|
-
const [input, setInput] =
|
|
2411
|
-
|
|
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(() => {
|
|
2412
2562
|
if (q.data && !dirty) {
|
|
2413
2563
|
setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
|
|
2414
2564
|
setAvailable(q.data.availablePatterns);
|
|
@@ -2455,7 +2605,7 @@ function DlpPanel({ focused }) {
|
|
|
2455
2605
|
];
|
|
2456
2606
|
const selC = Math.min(sel, Math.max(0, entries.length - 1));
|
|
2457
2607
|
const cur = entries[selC];
|
|
2458
|
-
|
|
2608
|
+
useInput5(
|
|
2459
2609
|
(input2, key) => {
|
|
2460
2610
|
if (!dlp) return;
|
|
2461
2611
|
if (key.ctrl && input2 === "r") {
|
|
@@ -2500,13 +2650,13 @@ function DlpPanel({ focused }) {
|
|
|
2500
2650
|
const enabled = new Set(dlp?.patterns ?? []);
|
|
2501
2651
|
const lines = [];
|
|
2502
2652
|
const header = (key, label, extra) => lines.push({
|
|
2503
|
-
node: /* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2505
|
-
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
|
|
2506
2656
|
] }, key),
|
|
2507
2657
|
entry: -1
|
|
2508
2658
|
});
|
|
2509
|
-
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 });
|
|
2510
2660
|
let ei = 0;
|
|
2511
2661
|
header("h:builtin", "built-in secret patterns", "space toggles on/off");
|
|
2512
2662
|
available.forEach((p) => {
|
|
@@ -2514,10 +2664,10 @@ function DlpPanel({ focused }) {
|
|
|
2514
2664
|
const isCur = focused && ei === selC;
|
|
2515
2665
|
const idx = ei++;
|
|
2516
2666
|
lines.push({
|
|
2517
|
-
node: /* @__PURE__ */
|
|
2518
|
-
/* @__PURE__ */
|
|
2519
|
-
/* @__PURE__ */
|
|
2520
|
-
/* @__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 })
|
|
2521
2671
|
] }, "b:" + p),
|
|
2522
2672
|
entry: idx
|
|
2523
2673
|
});
|
|
@@ -2529,10 +2679,10 @@ function DlpPanel({ focused }) {
|
|
|
2529
2679
|
const isCur = focused && ei === selC;
|
|
2530
2680
|
const idx = ei++;
|
|
2531
2681
|
lines.push({
|
|
2532
|
-
node: /* @__PURE__ */
|
|
2533
|
-
/* @__PURE__ */
|
|
2534
|
-
/* @__PURE__ */
|
|
2535
|
-
/* @__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)) })
|
|
2536
2686
|
] }, "c:" + c2.name + i),
|
|
2537
2687
|
entry: idx
|
|
2538
2688
|
});
|
|
@@ -2544,10 +2694,10 @@ function DlpPanel({ focused }) {
|
|
|
2544
2694
|
const isCur = focused && ei === selC;
|
|
2545
2695
|
const idx = ei++;
|
|
2546
2696
|
lines.push({
|
|
2547
|
-
node: /* @__PURE__ */
|
|
2548
|
-
/* @__PURE__ */
|
|
2549
|
-
/* @__PURE__ */
|
|
2550
|
-
!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
|
|
2551
2701
|
] }, "g:" + p + i),
|
|
2552
2702
|
entry: idx
|
|
2553
2703
|
});
|
|
@@ -2561,17 +2711,17 @@ function DlpPanel({ focused }) {
|
|
|
2561
2711
|
const win = lines.slice(start, start + budget);
|
|
2562
2712
|
const moreAbove = start;
|
|
2563
2713
|
const moreBelow = Math.max(0, lines.length - (start + budget));
|
|
2564
|
-
return /* @__PURE__ */
|
|
2565
|
-
/* @__PURE__ */
|
|
2566
|
-
/* @__PURE__ */
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
/* @__PURE__ */
|
|
2569
|
-
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
|
|
2570
2720
|
] }),
|
|
2571
|
-
/* @__PURE__ */
|
|
2572
|
-
adding ? /* @__PURE__ */
|
|
2573
|
-
/* @__PURE__ */
|
|
2574
|
-
/* @__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(
|
|
2575
2725
|
TextInput3,
|
|
2576
2726
|
{
|
|
2577
2727
|
value: input,
|
|
@@ -2598,19 +2748,19 @@ function DlpPanel({ focused }) {
|
|
|
2598
2748
|
}
|
|
2599
2749
|
)
|
|
2600
2750
|
] }) }) : null,
|
|
2601
|
-
/* @__PURE__ */
|
|
2602
|
-
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
|
|
2603
2753
|
] }) : null });
|
|
2604
2754
|
}
|
|
2605
2755
|
|
|
2606
2756
|
// src/tui/panels/Audit.tsx
|
|
2607
|
-
import { Box as
|
|
2757
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
2608
2758
|
import TextInput4 from "ink-text-input";
|
|
2609
2759
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2610
2760
|
import { homedir as homedir5 } from "os";
|
|
2611
2761
|
import { join as join5 } from "path";
|
|
2612
|
-
import { useState as
|
|
2613
|
-
import { jsx as
|
|
2762
|
+
import { useState as useState7 } from "react";
|
|
2763
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2614
2764
|
var DECISIONS = [void 0, "DENY", "ALLOW"];
|
|
2615
2765
|
var SIGNALS = [void 0, "dlp", "ratelimit"];
|
|
2616
2766
|
var SESS_STATUS = [void 0, "active", "idle", "ended"];
|
|
@@ -2718,25 +2868,25 @@ var STATUS_DOT = {
|
|
|
2718
2868
|
};
|
|
2719
2869
|
function AuditPanel({ active: active2, focused }) {
|
|
2720
2870
|
const { cols, rows } = usePanelSize();
|
|
2721
|
-
const [source, setSource] =
|
|
2722
|
-
const [view, setView] =
|
|
2723
|
-
const [di, setDi] =
|
|
2724
|
-
const [gi, setGi] =
|
|
2725
|
-
const [tool, setTool] =
|
|
2726
|
-
const [agent, setAgent] =
|
|
2727
|
-
const [search, setSearch] =
|
|
2728
|
-
const [sessFilter, setSessFilter] =
|
|
2729
|
-
const [page, setPage] =
|
|
2730
|
-
const [sel, setSel] =
|
|
2731
|
-
const [detailScroll, setDetailScroll] =
|
|
2732
|
-
const [editing, setEditing] =
|
|
2733
|
-
const [si, setSi] =
|
|
2734
|
-
const [sessSearch, setSessSearch] =
|
|
2735
|
-
const [sessSel, setSessSel] =
|
|
2736
|
-
const [confirm, setConfirm] =
|
|
2737
|
-
const [msg, setMsg] =
|
|
2738
|
-
const [showHelp, setShowHelp] =
|
|
2739
|
-
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);
|
|
2740
2890
|
const toTop = () => setSel(0);
|
|
2741
2891
|
const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
|
|
2742
2892
|
usePoll(statsQ.reloadQuiet, 15e3, active2 && source === "cloud" && !frozen);
|
|
@@ -2855,7 +3005,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2855
3005
|
};
|
|
2856
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" }));
|
|
2857
3007
|
};
|
|
2858
|
-
|
|
3008
|
+
useInput6(
|
|
2859
3009
|
(input, key) => {
|
|
2860
3010
|
if (input === " ") {
|
|
2861
3011
|
setFrozen((f) => !f);
|
|
@@ -2989,58 +3139,58 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
2989
3139
|
{ isActive: focused && !editing }
|
|
2990
3140
|
);
|
|
2991
3141
|
const localAll = localQ.data ?? [];
|
|
2992
|
-
const strip = source === "cloud" ? /* @__PURE__ */
|
|
2993
|
-
/* @__PURE__ */
|
|
2994
|
-
/* @__PURE__ */
|
|
2995
|
-
/* @__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: [
|
|
2996
3146
|
statsQ.data?.allowed ?? "\xB7\xB7\xB7",
|
|
2997
3147
|
" allow"
|
|
2998
3148
|
] }),
|
|
2999
|
-
/* @__PURE__ */
|
|
3000
|
-
/* @__PURE__ */
|
|
3149
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
|
|
3150
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
|
|
3001
3151
|
statsQ.data?.denied ?? "\xB7\xB7",
|
|
3002
3152
|
" deny"
|
|
3003
3153
|
] }),
|
|
3004
|
-
/* @__PURE__ */
|
|
3005
|
-
] }) : /* @__PURE__ */
|
|
3006
|
-
/* @__PURE__ */
|
|
3007
|
-
/* @__PURE__ */
|
|
3008
|
-
/* @__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: [
|
|
3009
3159
|
localAll.filter((r) => r.decision === "ALLOW").length,
|
|
3010
3160
|
" allow"
|
|
3011
3161
|
] }),
|
|
3012
|
-
/* @__PURE__ */
|
|
3013
|
-
/* @__PURE__ */
|
|
3162
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
|
|
3163
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
|
|
3014
3164
|
localAll.filter((r) => r.decision !== "ALLOW").length,
|
|
3015
3165
|
" deny"
|
|
3016
3166
|
] }),
|
|
3017
|
-
/* @__PURE__ */
|
|
3167
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3018
3168
|
" \xB7 ",
|
|
3019
3169
|
LOCAL_LOG
|
|
3020
3170
|
] })
|
|
3021
3171
|
] });
|
|
3022
|
-
const copyBanner = frozen ? /* @__PURE__ */
|
|
3023
|
-
const srcChip = /* @__PURE__ */
|
|
3024
|
-
/* @__PURE__ */
|
|
3025
|
-
/* @__PURE__ */
|
|
3026
|
-
/* @__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) " })
|
|
3027
3177
|
] });
|
|
3028
3178
|
if (showHelp) {
|
|
3029
|
-
const colOf = (groups) => /* @__PURE__ */
|
|
3030
|
-
/* @__PURE__ */
|
|
3031
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
3032
|
-
/* @__PURE__ */
|
|
3033
|
-
/* @__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 })
|
|
3034
3184
|
] }, k))
|
|
3035
3185
|
] }, group)) });
|
|
3036
|
-
return /* @__PURE__ */
|
|
3186
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3037
3187
|
copyBanner,
|
|
3038
|
-
/* @__PURE__ */
|
|
3188
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
|
|
3039
3189
|
"AUDIT \u2014 all keys",
|
|
3040
3190
|
" ",
|
|
3041
|
-
/* @__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)" })
|
|
3042
3192
|
] }),
|
|
3043
|
-
/* @__PURE__ */
|
|
3193
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
|
|
3044
3194
|
colOf(AUDIT_HELP.slice(0, 1)),
|
|
3045
3195
|
colOf(AUDIT_HELP.slice(1))
|
|
3046
3196
|
] })
|
|
@@ -3059,53 +3209,53 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3059
3209
|
const maxScroll = Math.max(0, contentLines.length - bodyRows);
|
|
3060
3210
|
const off = Math.min(detailScroll, maxScroll);
|
|
3061
3211
|
const win = contentLines.slice(off, off + bodyRows);
|
|
3062
|
-
return /* @__PURE__ */
|
|
3212
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3063
3213
|
copyBanner,
|
|
3064
|
-
/* @__PURE__ */
|
|
3065
|
-
/* @__PURE__ */
|
|
3066
|
-
/* @__PURE__ */
|
|
3067
|
-
/* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3069
|
-
e.dlp.length ? /* @__PURE__ */
|
|
3070
|
-
e.burst ? /* @__PURE__ */
|
|
3071
|
-
/* @__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" })
|
|
3072
3222
|
] }),
|
|
3073
|
-
/* @__PURE__ */
|
|
3074
|
-
/* @__PURE__ */
|
|
3075
|
-
/* @__PURE__ */
|
|
3076
|
-
/* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3080
|
-
/* @__PURE__ */
|
|
3081
|
-
/* @__PURE__ */
|
|
3082
|
-
/* @__PURE__ */
|
|
3083
|
-
/* @__PURE__ */
|
|
3084
|
-
/* @__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" })
|
|
3085
3235
|
] }),
|
|
3086
|
-
/* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3088
|
-
/* @__PURE__ */
|
|
3089
|
-
/* @__PURE__ */
|
|
3090
|
-
/* @__PURE__ */
|
|
3091
|
-
/* @__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" })
|
|
3092
3242
|
] }),
|
|
3093
|
-
/* @__PURE__ */
|
|
3094
|
-
/* @__PURE__ */
|
|
3095
|
-
/* @__PURE__ */
|
|
3096
|
-
/* @__PURE__ */
|
|
3097
|
-
/* @__PURE__ */
|
|
3098
|
-
/* @__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 " })
|
|
3099
3249
|
] })
|
|
3100
3250
|
] });
|
|
3101
3251
|
}
|
|
3102
|
-
const chip = (label, val, on) => /* @__PURE__ */
|
|
3103
|
-
/* @__PURE__ */
|
|
3252
|
+
const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
3253
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3104
3254
|
label,
|
|
3105
3255
|
":"
|
|
3106
3256
|
] }),
|
|
3107
|
-
/* @__PURE__ */
|
|
3108
|
-
/* @__PURE__ */
|
|
3257
|
+
/* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
|
|
3258
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " })
|
|
3109
3259
|
] });
|
|
3110
3260
|
if (view === "sessions") {
|
|
3111
3261
|
const headerRows2 = 5 + (editing ? 1 : 0);
|
|
@@ -3113,20 +3263,20 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3113
3263
|
const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
|
|
3114
3264
|
const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
|
|
3115
3265
|
const win = sessionsFiltered.slice(start2, start2 + listRows2);
|
|
3116
|
-
return /* @__PURE__ */
|
|
3266
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3117
3267
|
strip,
|
|
3118
3268
|
copyBanner,
|
|
3119
|
-
/* @__PURE__ */
|
|
3269
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3120
3270
|
srcChip,
|
|
3121
|
-
/* @__PURE__ */
|
|
3122
|
-
/* @__PURE__ */
|
|
3271
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
|
|
3272
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 logs (v) " }),
|
|
3123
3273
|
chip("status", SESS_STATUS[si] ?? "all", si !== 0),
|
|
3124
3274
|
chip("search", sessSearch || "\xB7", !!sessSearch)
|
|
3125
3275
|
] }),
|
|
3126
|
-
/* @__PURE__ */
|
|
3127
|
-
editing === "sess-search" ? /* @__PURE__ */
|
|
3128
|
-
/* @__PURE__ */
|
|
3129
|
-
/* @__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(
|
|
3130
3280
|
TextInput4,
|
|
3131
3281
|
{
|
|
3132
3282
|
value: sessSearch,
|
|
@@ -3138,8 +3288,8 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3138
3288
|
}
|
|
3139
3289
|
)
|
|
3140
3290
|
] }) : null,
|
|
3141
|
-
/* @__PURE__ */
|
|
3142
|
-
/* @__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(
|
|
3143
3293
|
Table,
|
|
3144
3294
|
{
|
|
3145
3295
|
columns: [
|
|
@@ -3170,7 +3320,7 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3170
3320
|
})
|
|
3171
3321
|
}
|
|
3172
3322
|
),
|
|
3173
|
-
sessionsFiltered.length === 0 ? /* @__PURE__ */
|
|
3323
|
+
sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
|
|
3174
3324
|
"(no sessions",
|
|
3175
3325
|
source === "local" ? " in the local file" : "",
|
|
3176
3326
|
")"
|
|
@@ -3183,13 +3333,13 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3183
3333
|
const maxStart = Math.max(0, pageRows.length - listRows);
|
|
3184
3334
|
const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
|
|
3185
3335
|
const windowed = pageRows.slice(start, start + listRows);
|
|
3186
|
-
return /* @__PURE__ */
|
|
3336
|
+
return /* @__PURE__ */ jsx7(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3187
3337
|
strip,
|
|
3188
3338
|
copyBanner,
|
|
3189
|
-
/* @__PURE__ */
|
|
3339
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3190
3340
|
srcChip,
|
|
3191
|
-
/* @__PURE__ */
|
|
3192
|
-
/* @__PURE__ */
|
|
3341
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "LOGS" }),
|
|
3342
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 sessions (v) " }),
|
|
3193
3343
|
chip("dec", DECISIONS[di] ?? "all", di !== 0),
|
|
3194
3344
|
chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
|
|
3195
3345
|
chip("tool", tool || "\xB7", !!tool),
|
|
@@ -3197,14 +3347,14 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3197
3347
|
chip("search", search || "\xB7", !!search),
|
|
3198
3348
|
sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
|
|
3199
3349
|
] }),
|
|
3200
|
-
/* @__PURE__ */
|
|
3201
|
-
msg ? /* @__PURE__ */
|
|
3202
|
-
editing && editing !== "sess-search" ? /* @__PURE__ */
|
|
3203
|
-
/* @__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: [
|
|
3204
3354
|
editing,
|
|
3205
3355
|
": "
|
|
3206
3356
|
] }),
|
|
3207
|
-
/* @__PURE__ */
|
|
3357
|
+
/* @__PURE__ */ jsx7(
|
|
3208
3358
|
TextInput4,
|
|
3209
3359
|
{
|
|
3210
3360
|
value: editing === "tool" ? tool : editing === "agent" ? agent : search,
|
|
@@ -3217,9 +3367,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3217
3367
|
}
|
|
3218
3368
|
)
|
|
3219
3369
|
] }) : null,
|
|
3220
|
-
/* @__PURE__ */
|
|
3221
|
-
/* @__PURE__ */
|
|
3222
|
-
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: [
|
|
3223
3373
|
"(no entries",
|
|
3224
3374
|
source === "local" ? " in the local file" : "",
|
|
3225
3375
|
" \u2014 c clears filters)"
|
|
@@ -3228,9 +3378,9 @@ function AuditPanel({ active: active2, focused }) {
|
|
|
3228
3378
|
}
|
|
3229
3379
|
|
|
3230
3380
|
// src/tui/panels/Settings.tsx
|
|
3231
|
-
import { Box as
|
|
3381
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
3232
3382
|
import TextInput5 from "ink-text-input";
|
|
3233
|
-
import { useEffect as
|
|
3383
|
+
import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
|
|
3234
3384
|
|
|
3235
3385
|
// src/global-install.ts
|
|
3236
3386
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync2, mkdirSync as mkdirSync4, rmSync } from "fs";
|
|
@@ -3714,7 +3864,7 @@ async function tuiUpdateFlow(onStatus) {
|
|
|
3714
3864
|
}
|
|
3715
3865
|
|
|
3716
3866
|
// src/tui/panels/Settings.tsx
|
|
3717
|
-
import { Fragment as Fragment5, jsx as
|
|
3867
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3718
3868
|
var EVENTS = ["denials", "allowed", "all"];
|
|
3719
3869
|
var SPIN2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3720
3870
|
var SIGNALS2 = ["any", "deny", "dlp", "ratelimit"];
|
|
@@ -3740,16 +3890,16 @@ function SettingsPanel({
|
|
|
3740
3890
|
}) {
|
|
3741
3891
|
void active2;
|
|
3742
3892
|
const { cols, rows } = usePanelSize();
|
|
3743
|
-
const [sel, setSel] =
|
|
3744
|
-
const [editing, setEditing] =
|
|
3745
|
-
const [input, setInput] =
|
|
3746
|
-
const [confirmDel, setConfirmDel] =
|
|
3747
|
-
const [msg, setMsg] =
|
|
3748
|
-
const [busy, setBusy] =
|
|
3749
|
-
const [editor, setEditor] =
|
|
3750
|
-
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);
|
|
3751
3901
|
const ver = currentVersion();
|
|
3752
|
-
|
|
3902
|
+
useEffect7(() => {
|
|
3753
3903
|
let live2 = true;
|
|
3754
3904
|
latestVersion().then((v) => {
|
|
3755
3905
|
if (live2) setLatest(v);
|
|
@@ -3759,26 +3909,26 @@ function SettingsPanel({
|
|
|
3759
3909
|
live2 = false;
|
|
3760
3910
|
};
|
|
3761
3911
|
}, []);
|
|
3762
|
-
const [accounts, setAccounts] =
|
|
3763
|
-
const [login, setLogin] =
|
|
3764
|
-
const [tick, setTick] =
|
|
3912
|
+
const [accounts, setAccounts] = useState8(() => listAccounts());
|
|
3913
|
+
const [login, setLogin] = useState8(null);
|
|
3914
|
+
const [tick, setTick] = useState8(0);
|
|
3765
3915
|
const abort = useRef3(false);
|
|
3766
3916
|
const refreshAccounts = () => setAccounts(listAccounts());
|
|
3767
|
-
const [guardHere, setGuardHere] =
|
|
3768
|
-
const [guardVer, setGuardVer] =
|
|
3769
|
-
const [guardOld, setGuardOld] =
|
|
3917
|
+
const [guardHere, setGuardHere] = useState8(() => isGuardInstalled());
|
|
3918
|
+
const [guardVer, setGuardVer] = useState8(() => installedGuardVersion());
|
|
3919
|
+
const [guardOld, setGuardOld] = useState8(() => guardHookOutdated());
|
|
3770
3920
|
const refreshGuard = () => {
|
|
3771
3921
|
setGuardHere(isGuardInstalled());
|
|
3772
3922
|
setGuardVer(installedGuardVersion());
|
|
3773
3923
|
setGuardOld(guardHookOutdated());
|
|
3774
3924
|
};
|
|
3775
3925
|
const locked = accounts.length === 0;
|
|
3776
|
-
const [srv, setSrv] =
|
|
3777
|
-
|
|
3926
|
+
const [srv, setSrv] = useState8(() => logsServerStatus());
|
|
3927
|
+
useEffect7(() => {
|
|
3778
3928
|
const t = setInterval(() => setSrv(logsServerStatus()), 3e3);
|
|
3779
3929
|
return () => clearInterval(t);
|
|
3780
3930
|
}, []);
|
|
3781
|
-
|
|
3931
|
+
useEffect7(() => {
|
|
3782
3932
|
if (!login || login.phase === "done" || login.phase === "error") return;
|
|
3783
3933
|
const t = setInterval(() => setTick((n) => n + 1), 120);
|
|
3784
3934
|
return () => clearInterval(t);
|
|
@@ -3791,7 +3941,7 @@ function SettingsPanel({
|
|
|
3791
3941
|
const local = localQ.data;
|
|
3792
3942
|
const selfProt = selfQ.data;
|
|
3793
3943
|
const autoInstalledRef = useRef3(false);
|
|
3794
|
-
|
|
3944
|
+
useEffect7(() => {
|
|
3795
3945
|
if (autoInstalledRef.current) return;
|
|
3796
3946
|
if (guardHere && guardOld) {
|
|
3797
3947
|
autoInstalledRef.current = true;
|
|
@@ -3803,7 +3953,7 @@ function SettingsPanel({
|
|
|
3803
3953
|
}
|
|
3804
3954
|
}
|
|
3805
3955
|
}, [guardHere, guardOld]);
|
|
3806
|
-
const [hidden, setHidden] =
|
|
3956
|
+
const [hidden, setHidden] = useState8(/* @__PURE__ */ new Set());
|
|
3807
3957
|
const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
|
|
3808
3958
|
const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
|
|
3809
3959
|
const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
|
|
@@ -3984,7 +4134,7 @@ function SettingsPanel({
|
|
|
3984
4134
|
setEditor({ ...editor, target: v, field: 1 });
|
|
3985
4135
|
}
|
|
3986
4136
|
};
|
|
3987
|
-
|
|
4137
|
+
useInput7(
|
|
3988
4138
|
(inp, key) => {
|
|
3989
4139
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
3990
4140
|
if (key.escape) {
|
|
@@ -4117,156 +4267,156 @@ function SettingsPanel({
|
|
|
4117
4267
|
);
|
|
4118
4268
|
const spin = SPIN2[tick % SPIN2.length];
|
|
4119
4269
|
if (login && (login.phase === "starting" || login.phase === "waiting")) {
|
|
4120
|
-
return /* @__PURE__ */
|
|
4121
|
-
/* @__PURE__ */
|
|
4122
|
-
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: [
|
|
4123
4273
|
spin,
|
|
4124
4274
|
" starting device pairing\u2026"
|
|
4125
|
-
] }) : /* @__PURE__ */
|
|
4126
|
-
/* @__PURE__ */
|
|
4127
|
-
/* @__PURE__ */
|
|
4128
|
-
/* @__PURE__ */
|
|
4129
|
-
/* @__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: [
|
|
4130
4280
|
spin,
|
|
4131
4281
|
" waiting for authorization\u2026 "
|
|
4132
4282
|
] }),
|
|
4133
|
-
/* @__PURE__ */
|
|
4283
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "esc to cancel" })
|
|
4134
4284
|
] })
|
|
4135
4285
|
] })
|
|
4136
4286
|
] });
|
|
4137
4287
|
}
|
|
4138
4288
|
if (editor) {
|
|
4139
|
-
const fieldRow = (idx, label, value, hint) => /* @__PURE__ */
|
|
4140
|
-
/* @__PURE__ */
|
|
4141
|
-
/* @__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) }),
|
|
4142
4292
|
value,
|
|
4143
|
-
editor.field === idx && hint ? /* @__PURE__ */
|
|
4293
|
+
editor.field === idx && hint ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + hint }) : null
|
|
4144
4294
|
] });
|
|
4145
|
-
return /* @__PURE__ */
|
|
4146
|
-
/* @__PURE__ */
|
|
4295
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
4296
|
+
/* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
|
|
4147
4297
|
editor.id ? "Edit" : "New",
|
|
4148
4298
|
" ",
|
|
4149
4299
|
editor.channel,
|
|
4150
4300
|
" alert"
|
|
4151
4301
|
] }),
|
|
4152
|
-
/* @__PURE__ */
|
|
4153
|
-
editing === "alert-target" ? /* @__PURE__ */
|
|
4154
|
-
/* @__PURE__ */
|
|
4155
|
-
/* @__PURE__ */
|
|
4156
|
-
] }) : msg ? /* @__PURE__ */
|
|
4157
|
-
/* @__PURE__ */
|
|
4158
|
-
fieldRow(0, "target", editor.target ? /* @__PURE__ */
|
|
4159
|
-
fieldRow(1, "signal", /* @__PURE__ */
|
|
4160
|
-
fieldRow(2, "threshold", /* @__PURE__ */
|
|
4161
|
-
fieldRow(3, "window", /* @__PURE__ */
|
|
4162
|
-
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
|
|
4163
4313
|
] }),
|
|
4164
|
-
/* @__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: [
|
|
4165
4315
|
"fires when ",
|
|
4166
|
-
/* @__PURE__ */
|
|
4316
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
|
|
4167
4317
|
" reach ",
|
|
4168
|
-
/* @__PURE__ */
|
|
4318
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: editor.threshold }),
|
|
4169
4319
|
" within",
|
|
4170
4320
|
" ",
|
|
4171
|
-
/* @__PURE__ */
|
|
4321
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) })
|
|
4172
4322
|
] }) })
|
|
4173
4323
|
] });
|
|
4174
4324
|
}
|
|
4175
4325
|
const loading = !locked && (localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data || guardQ.loading && !guardQ.data || selfQ.loading && !selfQ.data);
|
|
4176
4326
|
const error = locked ? null : localQ.error ?? whQ.error ?? alertQ.error ?? guardQ.error ?? selfQ.error;
|
|
4177
|
-
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" });
|
|
4178
4328
|
const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
|
|
4179
4329
|
const isCur = (r) => keyOf(r) === keyOf(cur) && focused;
|
|
4180
|
-
const cursor = (r) => /* @__PURE__ */
|
|
4330
|
+
const cursor = (r) => /* @__PURE__ */ jsx8(Text8, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
|
|
4181
4331
|
const rowLine = (r) => {
|
|
4182
4332
|
switch (r.kind) {
|
|
4183
4333
|
case "acct": {
|
|
4184
4334
|
const a = r.acc;
|
|
4185
4335
|
const isView = viewApiKey ? a.apiKey === viewApiKey : accounts[0]?.apiKey === a.apiKey;
|
|
4186
4336
|
const isActive = isActiveAccount(a.apiKey);
|
|
4187
|
-
return /* @__PURE__ */
|
|
4337
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4188
4338
|
cursor(r),
|
|
4189
|
-
/* @__PURE__ */
|
|
4190
|
-
isView ? /* @__PURE__ */
|
|
4191
|
-
isActive ? /* @__PURE__ */
|
|
4192
|
-
/* @__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) : "" })
|
|
4193
4343
|
] });
|
|
4194
4344
|
}
|
|
4195
4345
|
case "acct-add":
|
|
4196
|
-
return /* @__PURE__ */
|
|
4346
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4197
4347
|
cursor(r),
|
|
4198
|
-
/* @__PURE__ */
|
|
4348
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
|
|
4199
4349
|
] });
|
|
4200
4350
|
case "guard":
|
|
4201
|
-
return /* @__PURE__ */
|
|
4351
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4202
4352
|
cursor(r),
|
|
4203
|
-
/* @__PURE__ */
|
|
4204
|
-
!guardHere ? /* @__PURE__ */
|
|
4205
|
-
/* @__PURE__ */
|
|
4206
|
-
/* @__PURE__ */
|
|
4207
|
-
/* @__PURE__ */
|
|
4208
|
-
] }) : /* @__PURE__ */
|
|
4209
|
-
/* @__PURE__ */
|
|
4210
|
-
/* @__PURE__ */
|
|
4211
|
-
/* @__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" })
|
|
4212
4362
|
] })
|
|
4213
4363
|
] });
|
|
4214
4364
|
case "self":
|
|
4215
|
-
return /* @__PURE__ */
|
|
4365
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4216
4366
|
cursor(r),
|
|
4217
|
-
/* @__PURE__ */
|
|
4218
|
-
selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */
|
|
4219
|
-
/* @__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" })
|
|
4220
4370
|
] });
|
|
4221
4371
|
case "ll-enabled":
|
|
4222
|
-
return /* @__PURE__ */
|
|
4372
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4223
4373
|
cursor(r),
|
|
4224
|
-
/* @__PURE__ */
|
|
4374
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(11) }),
|
|
4225
4375
|
onOff(!!local?.enabled),
|
|
4226
|
-
/* @__PURE__ */
|
|
4376
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
|
|
4227
4377
|
] });
|
|
4228
4378
|
case "ll-path":
|
|
4229
|
-
return /* @__PURE__ */
|
|
4379
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4230
4380
|
cursor(r),
|
|
4231
|
-
/* @__PURE__ */
|
|
4232
|
-
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" })
|
|
4233
4383
|
] });
|
|
4234
4384
|
case "ll-server":
|
|
4235
|
-
return /* @__PURE__ */
|
|
4385
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4236
4386
|
cursor(r),
|
|
4237
|
-
/* @__PURE__ */
|
|
4238
|
-
srv.running ? /* @__PURE__ */
|
|
4239
|
-
/* @__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" })
|
|
4240
4390
|
] });
|
|
4241
4391
|
case "wh":
|
|
4242
|
-
return /* @__PURE__ */
|
|
4392
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4243
4393
|
cursor(r),
|
|
4244
4394
|
onOff(r.wh.enabled),
|
|
4245
|
-
/* @__PURE__ */
|
|
4246
|
-
/* @__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) })
|
|
4247
4397
|
] });
|
|
4248
4398
|
case "wh-add":
|
|
4249
|
-
return /* @__PURE__ */
|
|
4399
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4250
4400
|
cursor(r),
|
|
4251
|
-
/* @__PURE__ */
|
|
4401
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
|
|
4252
4402
|
] });
|
|
4253
4403
|
case "alert":
|
|
4254
|
-
return /* @__PURE__ */
|
|
4404
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4255
4405
|
cursor(r),
|
|
4256
4406
|
onOff(r.rule.enabled),
|
|
4257
|
-
/* @__PURE__ */
|
|
4258
|
-
/* @__PURE__ */
|
|
4259
|
-
/* @__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) })
|
|
4260
4410
|
] });
|
|
4261
4411
|
case "alert-add-email":
|
|
4262
|
-
return /* @__PURE__ */
|
|
4412
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4263
4413
|
cursor(r),
|
|
4264
|
-
/* @__PURE__ */
|
|
4414
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add email alert (enter)" })
|
|
4265
4415
|
] });
|
|
4266
4416
|
case "alert-add-tg":
|
|
4267
|
-
return /* @__PURE__ */
|
|
4417
|
+
return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
|
|
4268
4418
|
cursor(r),
|
|
4269
|
-
/* @__PURE__ */
|
|
4419
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
|
|
4270
4420
|
] });
|
|
4271
4421
|
}
|
|
4272
4422
|
};
|
|
@@ -4285,39 +4435,39 @@ function SettingsPanel({
|
|
|
4285
4435
|
const sec = sectionOf(r);
|
|
4286
4436
|
if (sec !== lastSec) {
|
|
4287
4437
|
if (lastSec) {
|
|
4288
|
-
lineEls.push(/* @__PURE__ */
|
|
4438
|
+
lineEls.push(/* @__PURE__ */ jsx8(Text8, { children: " " }, "sp:" + sec));
|
|
4289
4439
|
lineKey.push("");
|
|
4290
4440
|
}
|
|
4291
4441
|
lastSec = sec;
|
|
4292
4442
|
lineEls.push(
|
|
4293
|
-
/* @__PURE__ */
|
|
4294
|
-
/* @__PURE__ */
|
|
4295
|
-
/* @__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] })
|
|
4296
4446
|
] }, "h:" + sec)
|
|
4297
4447
|
);
|
|
4298
4448
|
lineKey.push("");
|
|
4299
4449
|
if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
|
|
4300
4450
|
lineEls.push(
|
|
4301
|
-
/* @__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)
|
|
4302
4452
|
);
|
|
4303
4453
|
lineKey.push("");
|
|
4304
4454
|
}
|
|
4305
4455
|
}
|
|
4306
|
-
lineEls.push(/* @__PURE__ */
|
|
4456
|
+
lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
|
|
4307
4457
|
lineKey.push(keyOf(r));
|
|
4308
4458
|
});
|
|
4309
4459
|
if (hasEmailAlert && hasTgAlert) {
|
|
4310
4460
|
lineEls.push(
|
|
4311
|
-
/* @__PURE__ */
|
|
4461
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
|
|
4312
4462
|
);
|
|
4313
4463
|
lineKey.push("");
|
|
4314
4464
|
}
|
|
4315
|
-
lineEls.push(/* @__PURE__ */
|
|
4465
|
+
lineEls.push(/* @__PURE__ */ jsx8(Text8, { children: " " }, "ver-sp"));
|
|
4316
4466
|
lineKey.push("");
|
|
4317
4467
|
lineEls.push(
|
|
4318
|
-
/* @__PURE__ */
|
|
4468
|
+
/* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", color: theme.dim, children: [
|
|
4319
4469
|
`solongate v${ver}`,
|
|
4320
|
-
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
|
|
4321
4471
|
] }, "ver")
|
|
4322
4472
|
);
|
|
4323
4473
|
lineKey.push("");
|
|
@@ -4328,26 +4478,26 @@ function SettingsPanel({
|
|
|
4328
4478
|
const win = lineEls.slice(startL, startL + budget);
|
|
4329
4479
|
const moreAbove = startL;
|
|
4330
4480
|
const moreBelow = Math.max(0, lineEls.length - (startL + budget));
|
|
4331
|
-
return /* @__PURE__ */
|
|
4332
|
-
/* @__PURE__ */
|
|
4333
|
-
editing ? /* @__PURE__ */
|
|
4334
|
-
/* @__PURE__ */
|
|
4335
|
-
/* @__PURE__ */
|
|
4336
|
-
] }) : msg ? /* @__PURE__ */
|
|
4337
|
-
/* @__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 })
|
|
4338
4488
|
] }) });
|
|
4339
4489
|
}
|
|
4340
4490
|
|
|
4341
4491
|
// src/tui/App.tsx
|
|
4342
|
-
import { jsx as
|
|
4492
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4343
4493
|
function LiveHint() {
|
|
4344
|
-
return /* @__PURE__ */
|
|
4345
|
-
/* @__PURE__ */
|
|
4346
|
-
/* @__PURE__ */
|
|
4347
|
-
/* @__PURE__ */
|
|
4348
|
-
/* @__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: [
|
|
4349
4499
|
"press ",
|
|
4350
|
-
/* @__PURE__ */
|
|
4500
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
|
|
4351
4501
|
" to go live"
|
|
4352
4502
|
] }) })
|
|
4353
4503
|
] });
|
|
@@ -4357,6 +4507,7 @@ var SECTIONS = [
|
|
|
4357
4507
|
{ label: "Policies", Panel: PoliciesPanel },
|
|
4358
4508
|
{ label: "Rate Limit", Panel: RateLimitPanel },
|
|
4359
4509
|
{ label: "DLP", Panel: DlpPanel },
|
|
4510
|
+
{ label: "Dry Run", Panel: DryRunPanel },
|
|
4360
4511
|
{ label: "Audit", Panel: AuditPanel },
|
|
4361
4512
|
{ label: "Settings", Panel: SettingsPanel }
|
|
4362
4513
|
];
|
|
@@ -4364,27 +4515,27 @@ var BANNER_HEX = ["white", "white", "white", "white", "white", "white"];
|
|
|
4364
4515
|
function Banner({ cols }) {
|
|
4365
4516
|
const wide = cols >= 82;
|
|
4366
4517
|
if (!wide) {
|
|
4367
|
-
return /* @__PURE__ */
|
|
4368
|
-
/* @__PURE__ */
|
|
4369
|
-
/* @__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" })
|
|
4370
4521
|
] });
|
|
4371
4522
|
}
|
|
4372
|
-
return /* @__PURE__ */
|
|
4373
|
-
BANNER_FULL.map((line, i) => /* @__PURE__ */
|
|
4374
|
-
/* @__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" })
|
|
4375
4526
|
] });
|
|
4376
4527
|
}
|
|
4377
4528
|
var SETTINGS_SECTION = SECTIONS.findIndex((s) => s.label === "Settings");
|
|
4378
4529
|
function App() {
|
|
4379
4530
|
const { exit } = useApp();
|
|
4380
|
-
const [accounts, setAccounts] =
|
|
4381
|
-
const [viewKey, setViewKey] =
|
|
4382
|
-
const [viewNonce, setViewNonce] =
|
|
4383
|
-
const [section, setSection] =
|
|
4384
|
-
const [focus, setFocus] =
|
|
4385
|
-
const [help, setHelp] =
|
|
4386
|
-
const [update2, setUpdate] =
|
|
4387
|
-
|
|
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(() => {
|
|
4388
4539
|
let alive = true;
|
|
4389
4540
|
const run = () => void tuiUpdateFlow((s) => alive && setUpdate(s));
|
|
4390
4541
|
run();
|
|
@@ -4397,7 +4548,7 @@ function App() {
|
|
|
4397
4548
|
const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
|
|
4398
4549
|
const cur = accounts[acctIdx];
|
|
4399
4550
|
const acctLabel2 = cur ? cur.email || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}` : "not logged in";
|
|
4400
|
-
|
|
4551
|
+
useEffect8(() => {
|
|
4401
4552
|
if (!cur || cur.email) return;
|
|
4402
4553
|
let alive = true;
|
|
4403
4554
|
api.auth.me().then((r) => {
|
|
@@ -4433,7 +4584,7 @@ function App() {
|
|
|
4433
4584
|
};
|
|
4434
4585
|
const locked = accounts.length === 0;
|
|
4435
4586
|
const effectiveSection = locked ? SETTINGS_SECTION : section;
|
|
4436
|
-
|
|
4587
|
+
useInput8((input, key) => {
|
|
4437
4588
|
if (help) {
|
|
4438
4589
|
setHelp(false);
|
|
4439
4590
|
return;
|
|
@@ -4461,33 +4612,33 @@ function App() {
|
|
|
4461
4612
|
});
|
|
4462
4613
|
const { cols, rows } = useTermSize();
|
|
4463
4614
|
const current = SECTIONS[effectiveSection];
|
|
4464
|
-
if (help) return /* @__PURE__ */
|
|
4615
|
+
if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
|
|
4465
4616
|
if (current.label === "Live" && focus === "panel") {
|
|
4466
|
-
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) });
|
|
4467
4618
|
}
|
|
4468
4619
|
const Panel = current.Panel;
|
|
4469
|
-
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" });
|
|
4470
4621
|
return (
|
|
4471
4622
|
// height rows-1 (not rows): once total output reaches stdout.rows ink stops
|
|
4472
4623
|
// diffing and clearTerminal-repaints every frame, which slides the banner.
|
|
4473
4624
|
// overflow hidden makes it a hard guarantee even if a panel over-renders.
|
|
4474
|
-
/* @__PURE__ */
|
|
4475
|
-
/* @__PURE__ */
|
|
4476
|
-
/* @__PURE__ */
|
|
4477
|
-
/* @__PURE__ */
|
|
4478
|
-
/* @__PURE__ */
|
|
4479
|
-
locked ? /* @__PURE__ */
|
|
4480
|
-
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
|
|
4481
4632
|
] }),
|
|
4482
|
-
/* @__PURE__ */
|
|
4483
|
-
/* @__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) => {
|
|
4484
4635
|
const isCur = i === effectiveSection;
|
|
4485
4636
|
const disabled = locked && s.label !== "Settings";
|
|
4486
|
-
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);
|
|
4487
4638
|
}) }),
|
|
4488
|
-
/* @__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 })
|
|
4489
4640
|
] }, viewNonce),
|
|
4490
|
-
/* @__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"]] }) })
|
|
4491
4642
|
] })
|
|
4492
4643
|
);
|
|
4493
4644
|
}
|
|
@@ -4502,21 +4653,21 @@ var HELP = [
|
|
|
4502
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"]]]
|
|
4503
4654
|
];
|
|
4504
4655
|
function HelpOverlay({ cols, rows }) {
|
|
4505
|
-
return /* @__PURE__ */
|
|
4506
|
-
/* @__PURE__ */
|
|
4507
|
-
/* @__PURE__ */
|
|
4508
|
-
/* @__PURE__ */
|
|
4509
|
-
keys.map(([k, desc]) => /* @__PURE__ */
|
|
4510
|
-
/* @__PURE__ */
|
|
4511
|
-
/* @__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 })
|
|
4512
4663
|
] }, k))
|
|
4513
4664
|
] }, group)) }),
|
|
4514
|
-
/* @__PURE__ */
|
|
4665
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
|
|
4515
4666
|
] });
|
|
4516
4667
|
}
|
|
4517
4668
|
|
|
4518
4669
|
// src/tui/index.tsx
|
|
4519
|
-
import { jsx as
|
|
4670
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
4520
4671
|
async function launchTui() {
|
|
4521
4672
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
4522
4673
|
process.stderr.write(
|
|
@@ -4541,7 +4692,7 @@ async function launchTui() {
|
|
|
4541
4692
|
console.info = toFile("info");
|
|
4542
4693
|
console.debug = toFile("debug");
|
|
4543
4694
|
try {
|
|
4544
|
-
const { waitUntilExit } = render(/* @__PURE__ */
|
|
4695
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}), { patchConsole: false });
|
|
4545
4696
|
await waitUntilExit();
|
|
4546
4697
|
} finally {
|
|
4547
4698
|
Object.assign(console, saved);
|