@solongate/proxy 0.82.31 → 0.82.32

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