@solongate/proxy 0.82.30 → 0.82.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7374,6 +7374,7 @@ var policies_exports = {};
7374
7374
  __export(policies_exports, {
7375
7375
  active: () => active,
7376
7376
  addRule: () => addRule,
7377
+ backtest: () => backtest,
7377
7378
  create: () => create,
7378
7379
  dryRun: () => dryRun,
7379
7380
  get: () => get,
@@ -7423,6 +7424,9 @@ function setActive(policyId) {
7423
7424
  function dryRun(body) {
7424
7425
  return request("POST", "/policies/dry-run", { body });
7425
7426
  }
7427
+ function backtest(body) {
7428
+ return request("POST", "/policies/backtest", { body });
7429
+ }
7426
7430
  var init_policies = __esm({
7427
7431
  "src/api-client/policies.ts"() {
7428
7432
  "use strict";
@@ -8794,11 +8798,176 @@ var init_Live = __esm({
8794
8798
  }
8795
8799
  });
8796
8800
 
8797
- // src/tui/panels/Policies.tsx
8801
+ // src/tui/panels/DryRun.tsx
8798
8802
  import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
8799
- import TextInput2 from "ink-text-input";
8800
8803
  import { useEffect as useEffect3, useState as useState3 } from "react";
8801
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
8804
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
8805
+ function requestDryRun(policyId) {
8806
+ pendingPolicyId = policyId;
8807
+ }
8808
+ function DryRunPanel({ focused }) {
8809
+ const list6 = useLoader(() => api.policies.list());
8810
+ const policies = list6.data?.policies ?? [];
8811
+ const [pi, setPi] = useState3(0);
8812
+ const [si, setSi] = useState3(3);
8813
+ const [res, setRes] = useState3(null);
8814
+ const [running, setRunning] = useState3(false);
8815
+ const [err2, setErr] = useState3(null);
8816
+ const [off, setOff] = useState3(0);
8817
+ const { cols, rows } = usePanelSize();
8818
+ const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
8819
+ function run10(policyIdx = pi, sampleIdx = si) {
8820
+ const p = policies[policyIdx];
8821
+ if (!p) return;
8822
+ setRunning(true);
8823
+ setErr(null);
8824
+ setRes(null);
8825
+ setOff(0);
8826
+ 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));
8827
+ }
8828
+ useEffect3(() => {
8829
+ if (policies.length === 0 || res || running || err2) return;
8830
+ let idx = 0;
8831
+ if (pendingPolicyId) {
8832
+ const found = policies.findIndex((p) => p.id === pendingPolicyId);
8833
+ if (found >= 0) idx = found;
8834
+ pendingPolicyId = null;
8835
+ }
8836
+ setPi(idx);
8837
+ run10(idx, si);
8838
+ }, [policies.length]);
8839
+ useInput2((input, key) => {
8840
+ if (!focused) return;
8841
+ if (key.upArrow) setOff((n) => Math.max(0, n - 1));
8842
+ else if (key.downArrow) setOff((n) => n + 1);
8843
+ else if (input === "p") {
8844
+ const n = policies.length ? (pi + 1) % policies.length : 0;
8845
+ setPi(n);
8846
+ run10(n, si);
8847
+ } else if (input === "n") {
8848
+ const n = (si + 1) % SAMPLES.length;
8849
+ setSi(n);
8850
+ run10(pi, n);
8851
+ } else if (input === "r") run10(pi, si);
8852
+ });
8853
+ const w = Math.max(40, cols);
8854
+ const lines = [];
8855
+ const head = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: t }, `h${lines.length}`));
8856
+ const dim3 = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { color: theme.dim, wrap: "truncate", children: t }, `d${lines.length}`));
8857
+ const row = (t) => lines.push(/* @__PURE__ */ jsx3(Text3, { wrap: "truncate", children: t }, `r${lines.length}`));
8858
+ const blank = () => lines.push(/* @__PURE__ */ jsx3(Text3, { children: " " }, `b${lines.length}`));
8859
+ if (res) {
8860
+ const s = res.summary;
8861
+ const denyAfter = s.would_deny;
8862
+ const denyBefore = s.would_deny - s.newly_blocked + s.newly_allowed;
8863
+ const before = pct(denyBefore, s.evaluated);
8864
+ const after = pct(denyAfter, s.evaluated);
8865
+ const ppShift = s.evaluated ? ((denyAfter - denyBefore) / s.evaluated * 100).toFixed(1) : "0.0";
8866
+ head("Summary");
8867
+ row(` evaluated ${s.evaluated} would allow ${s.would_allow} would deny ${s.would_deny}`);
8868
+ lines.push(
8869
+ /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
8870
+ " newly blocked ",
8871
+ /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: String(s.newly_blocked) }),
8872
+ " newly allowed ",
8873
+ /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: String(s.newly_allowed) }),
8874
+ " unchanged ",
8875
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: String(s.unchanged) })
8876
+ ] }, `sum${lines.length}`)
8877
+ );
8878
+ row(` deny rate ${before} -> ${after} (${Number(ppShift) > 0 ? "+" : ""}${ppShift}pp)`);
8879
+ blank();
8880
+ if (res.per_rule.length) {
8881
+ head("Per-rule impact");
8882
+ dim3(` ${"RULE".padEnd(38)}${"MATCHED".padEnd(9)}${"%".padEnd(8)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
8883
+ for (const r of res.per_rule.slice(0, 30)) {
8884
+ row(` ${truncate2(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}`);
8885
+ }
8886
+ blank();
8887
+ }
8888
+ if (res.per_agent.length) {
8889
+ head("Per-agent impact");
8890
+ dim3(` ${"AGENT".padEnd(24)}${"EVALUATED".padEnd(11)}${"CHANGED".padEnd(9)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
8891
+ for (const a of res.per_agent) {
8892
+ row(` ${truncate2(a.agent || "-", 22).padEnd(24)}${String(a.evaluated).padEnd(11)}${String(a.changed).padEnd(9)}${String(a.newly_blocked).padEnd(9)}${a.newly_allowed}`);
8893
+ }
8894
+ blank();
8895
+ }
8896
+ if (res.per_tool.length) {
8897
+ head("Per-tool breakdown");
8898
+ dim3(` ${"TOOL".padEnd(24)}${"EVALUATED".padEnd(11)}${"CHANGED".padEnd(9)}${"NEW-BLK".padEnd(9)}NEW-ALW`);
8899
+ for (const t of res.per_tool) {
8900
+ row(` ${truncate2(t.tool || "-", 22).padEnd(24)}${String(t.evaluated).padEnd(11)}${String(t.changed).padEnd(9)}${String(t.newly_blocked).padEnd(9)}${t.newly_allowed}`);
8901
+ }
8902
+ blank();
8903
+ }
8904
+ if (res.samples.length) {
8905
+ head(`Changed calls (${res.samples.length})`);
8906
+ for (const c2 of res.samples) {
8907
+ const flip = `${c2.original}->${c2.predicted}`;
8908
+ const newlyAllowed = c2.predicted === "ALLOW";
8909
+ lines.push(
8910
+ /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
8911
+ " ",
8912
+ /* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: truncate2(c2.tool, 16).padEnd(17) }),
8913
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate2(c2.agent || "-", 14).padEnd(15) }),
8914
+ /* @__PURE__ */ jsx3(Text3, { color: newlyAllowed ? theme.ok : theme.bad, children: flip.padEnd(14) }),
8915
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: truncate2(c2.preview.replace(/\s+/g, " "), Math.max(10, w - 50)) })
8916
+ ] }, `c${lines.length}`)
8917
+ );
8918
+ }
8919
+ }
8920
+ }
8921
+ const headerRows = 3;
8922
+ const budget = Math.max(3, rows - headerRows);
8923
+ const maxOff = Math.max(0, lines.length - budget);
8924
+ const start = Math.min(off, maxOff);
8925
+ const win = lines.slice(start, start + budget);
8926
+ const above = start;
8927
+ const below = Math.max(0, lines.length - (start + budget));
8928
+ const sampleLabel = `${SAMPLES[si]}${res?.sampled != null ? ` (ran ${res.sampled})` : ""}`;
8929
+ return /* @__PURE__ */ jsx3(DataView, { loading: list6.loading && !list6.data, error: list6.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8930
+ /* @__PURE__ */ jsxs3(Box3, { children: [
8931
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "Policy Dry Run" }),
8932
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
8933
+ " ",
8934
+ truncate2(selected?.name ?? "no policy", 26)
8935
+ ] }),
8936
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
8937
+ " mode: ",
8938
+ selected?.mode ?? "-"
8939
+ ] }),
8940
+ /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
8941
+ " sample: ",
8942
+ sampleLabel
8943
+ ] })
8944
+ ] }),
8945
+ /* @__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" }),
8946
+ running ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: "replaying recent traffic\u2026" }) : null,
8947
+ err2 ? /* @__PURE__ */ jsx3(Text3, { color: theme.bad, wrap: "truncate", children: "\u2717 " + err2 }) : null,
8948
+ !running && !err2 && !res ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "no result yet \u2014 press r to run" }) : null,
8949
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: win })
8950
+ ] }) });
8951
+ }
8952
+ var SAMPLES, pendingPolicyId, pct;
8953
+ var init_DryRun = __esm({
8954
+ "src/tui/panels/DryRun.tsx"() {
8955
+ "use strict";
8956
+ init_api_client();
8957
+ init_components();
8958
+ init_hooks();
8959
+ init_theme();
8960
+ SAMPLES = [100, 250, 500, 1e3, 2500, 5e3];
8961
+ pendingPolicyId = null;
8962
+ pct = (n, d) => d ? (n / d * 100).toFixed(1) + "%" : "0%";
8963
+ }
8964
+ });
8965
+
8966
+ // src/tui/panels/Policies.tsx
8967
+ import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
8968
+ import TextInput2 from "ink-text-input";
8969
+ import { useEffect as useEffect4, useState as useState4 } from "react";
8970
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
8802
8971
  function ruleSummary(r) {
8803
8972
  const bits = [];
8804
8973
  for (const [g, tag] of [["commandConstraints", "cmd"], ["pathConstraints", "path"], ["filenameConstraints", "file"], ["urlConstraints", "url"]]) {
@@ -8815,19 +8984,19 @@ function PoliciesPanel({ focused }) {
8815
8984
  const activeId = activeQ.data?.policy?.id ?? null;
8816
8985
  const activeBy = activeQ.data?.matched_by;
8817
8986
  const { rows: panelRows } = usePanelSize();
8818
- const [view, setView] = useState3("list");
8819
- const [pi, setPi] = useState3(0);
8820
- const [ri, setRi] = useState3(0);
8821
- const [fi, setFi] = useState3(0);
8822
- const [rules, setRules] = useState3([]);
8823
- const [mode, setMode] = useState3("denylist");
8824
- const [dirty, setDirty] = useState3(false);
8825
- const [editing, setEditing] = useState3(false);
8826
- const [editVal, setEditVal] = useState3("");
8827
- const [status, setStatus] = useState3(null);
8987
+ const [view, setView] = useState4("list");
8988
+ const [pi, setPi] = useState4(0);
8989
+ const [ri, setRi] = useState4(0);
8990
+ const [fi, setFi] = useState4(0);
8991
+ const [rules, setRules] = useState4([]);
8992
+ const [mode, setMode] = useState4("denylist");
8993
+ const [dirty, setDirty] = useState4(false);
8994
+ const [editing, setEditing] = useState4(false);
8995
+ const [editVal, setEditVal] = useState4("");
8996
+ const [status, setStatus] = useState4(null);
8828
8997
  const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
8829
8998
  const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
8830
- useEffect3(() => {
8999
+ useEffect4(() => {
8831
9000
  if (detail.data) {
8832
9001
  setRules(detail.data.rules);
8833
9002
  setMode(detail.data.mode ?? "denylist");
@@ -8873,7 +9042,7 @@ function PoliciesPanel({ focused }) {
8873
9042
  setDirty(false);
8874
9043
  setStatus("discarded");
8875
9044
  };
8876
- useInput2(
9045
+ useInput3(
8877
9046
  (input, key) => {
8878
9047
  if (key.ctrl && input === "r" && !editing) {
8879
9048
  list6.reload();
@@ -8906,13 +9075,6 @@ function PoliciesPanel({ focused }) {
8906
9075
  return;
8907
9076
  }
8908
9077
  if (view === "rules") {
8909
- if (key.ctrl && input === "d") {
8910
- if (selected) {
8911
- openBrowser(`${DASHBOARD_URL}/playground?policy=${encodeURIComponent(selected.id)}`);
8912
- setStatus("Opened the dry-run playground in your browser.");
8913
- }
8914
- return;
8915
- }
8916
9078
  if (key.leftArrow) return setView("list"), void setStatus(null);
8917
9079
  if (key.upArrow) setRi((n) => Math.max(0, n - 1));
8918
9080
  else if (key.downArrow) setRi((n) => Math.min(rules.length - 1, n + 1));
@@ -8940,13 +9102,10 @@ function PoliciesPanel({ focused }) {
8940
9102
  setFi(0);
8941
9103
  setView("rule");
8942
9104
  } else if (input === "D") {
8943
- setStatus("Dry-running\u2026");
8944
- void api.policies.dryRun({ rules, mode }).then((res) => setStatus(
8945
- `dry-run: replayed ${res.evaluated} recent calls against ${rules.length} rules
8946
- would allow ${res.would_allow} would deny ${res.would_deny}
8947
- newly allowed ${res.newly_allowed} newly blocked ${res.newly_blocked} unchanged ${res.unchanged}` + (res.sample_newly_blocked?.length ? `
8948
- now blocked e.g.: ${res.sample_newly_blocked.slice(0, 3).map((s) => truncate2(s.tool, 24)).join(", ")}` : "")
8949
- )).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
9105
+ if (selected) {
9106
+ requestDryRun(selected.id);
9107
+ setStatus('Sent to Dry Run - open the "Dry Run" section in the left nav for the full report.');
9108
+ }
8950
9109
  } else if (input === "s") void save();
8951
9110
  else if (input === "x") discard();
8952
9111
  return;
@@ -8979,35 +9138,35 @@ function PoliciesPanel({ focused }) {
8979
9138
  const lWin = policies.slice(lStart, lStart + lBudget);
8980
9139
  const lAbove = lStart;
8981
9140
  const lBelow = Math.max(0, policies.length - (lStart + lBudget));
8982
- return /* @__PURE__ */ jsx3(DataView, { loading: list6.loading && !list6.data, error: list6.error, empty: !!list6.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8983
- /* @__PURE__ */ jsxs3(Box3, { children: [
8984
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "active: " }),
8985
- activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
8986
- /* @__PURE__ */ jsxs3(Text3, { color: theme.ok, bold: true, children: [
9141
+ return /* @__PURE__ */ jsx4(DataView, { loading: list6.loading && !list6.data, error: list6.error, empty: !!list6.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
9142
+ /* @__PURE__ */ jsxs4(Box4, { children: [
9143
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "active: " }),
9144
+ activeQ.loading && !activeQ.data ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2026" }) : activeName ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
9145
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.ok, bold: true, children: [
8987
9146
  "\u25CF ",
8988
9147
  truncate2(activeName, 28)
8989
9148
  ] }),
8990
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
8991
- ] }) : /* @__PURE__ */ jsx3(Text3, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
9149
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
9150
+ ] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
8992
9151
  ] }),
8993
- /* @__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" }),
8994
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
9152
+ /* @__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" }),
9153
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: lWin.map((p, wi) => {
8995
9154
  const i = lStart + wi;
8996
- return /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
9155
+ return /* @__PURE__ */ jsxs4(Text4, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
8997
9156
  (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
8998
- /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
9157
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
8999
9158
  p.mode.padEnd(10),
9000
9159
  " ",
9001
9160
  p.rules.length,
9002
9161
  " rules"
9003
9162
  ] }),
9004
- p.id === activeId ? /* @__PURE__ */ jsx3(Text3, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
9163
+ p.id === activeId ? /* @__PURE__ */ jsx4(Text4, { color: theme.ok, bold: true, children: " \u25CF ACTIVE" }) : null
9005
9164
  ] }, p.id);
9006
9165
  }) }),
9007
- status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9166
+ status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9008
9167
  ] }) });
9009
9168
  }
9010
- const dirtyTag = dirty ? /* @__PURE__ */ jsx3(Text3, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
9169
+ const dirtyTag = dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null;
9011
9170
  if (view === "rules") {
9012
9171
  const rStatusLines = status ? status.split("\n").length : 0;
9013
9172
  const rHead = 4 + rStatusLines;
@@ -9017,16 +9176,16 @@ function PoliciesPanel({ focused }) {
9017
9176
  const rWin = rules.slice(rStart, rStart + rBudget);
9018
9177
  const rAbove = rStart;
9019
9178
  const rBelow = Math.max(0, rules.length - (rStart + rBudget));
9020
- return /* @__PURE__ */ jsx3(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
9021
- /* @__PURE__ */ jsxs3(Box3, { children: [
9022
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
9023
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " mode: " }),
9024
- /* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
9025
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: ` ${rules.length} rules` }),
9179
+ return /* @__PURE__ */ jsx4(DataView, { loading: detail.loading && !detail.data, error: detail.error, children: /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
9180
+ /* @__PURE__ */ jsxs4(Box4, { children: [
9181
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
9182
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " mode: " }),
9183
+ /* @__PURE__ */ jsx4(Text4, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
9184
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${rules.length} rules` }),
9026
9185
  dirtyTag
9027
9186
  ] }),
9028
- /* @__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 ^D playground \xB7 s save \xB7 \u2190 back${rAbove ? ` \xB7 \u25B2${rAbove}` : ""}${rBelow ? ` \xB7 \u25BC${rBelow}` : ""}` }),
9029
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
9187
+ /* @__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}` : ""}` }),
9188
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(
9030
9189
  Table,
9031
9190
  {
9032
9191
  columns: [
@@ -9052,24 +9211,24 @@ function PoliciesPanel({ focused }) {
9052
9211
  })
9053
9212
  }
9054
9213
  ) }),
9055
- rules.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "(no rules)" }) : null,
9056
- status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9214
+ rules.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "(no rules)" }) : null,
9215
+ status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9057
9216
  ] }) });
9058
9217
  }
9059
9218
  const rule = rules[ri];
9060
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
9061
- /* @__PURE__ */ jsxs3(Box3, { children: [
9062
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: "Rule " }),
9063
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: rule?.id ?? "" }),
9219
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
9220
+ /* @__PURE__ */ jsxs4(Box4, { children: [
9221
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.accentBright, children: "Rule " }),
9222
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
9064
9223
  dirtyTag
9065
9224
  ] }),
9066
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
9067
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
9225
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
9226
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
9068
9227
  const val = rule ? f.get(rule) : "";
9069
9228
  const active2 = i === fi;
9070
- return /* @__PURE__ */ jsxs3(Box3, { children: [
9071
- /* @__PURE__ */ jsx3(Text3, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) }),
9072
- active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx3(
9229
+ return /* @__PURE__ */ jsxs4(Box4, { children: [
9230
+ /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) }),
9231
+ active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx4(
9073
9232
  TextInput2,
9074
9233
  {
9075
9234
  value: editVal,
@@ -9079,8 +9238,8 @@ function PoliciesPanel({ focused }) {
9079
9238
  setEditing(false);
9080
9239
  }
9081
9240
  }
9082
- ) : /* @__PURE__ */ jsx3(
9083
- Text3,
9241
+ ) : /* @__PURE__ */ jsx4(
9242
+ Text4,
9084
9243
  {
9085
9244
  color: f.kind === "effect" ? val === "ALLOW" ? theme.ok : theme.bad : void 0,
9086
9245
  dimColor: !val,
@@ -9089,19 +9248,18 @@ function PoliciesPanel({ focused }) {
9089
9248
  )
9090
9249
  ] }, f.label);
9091
9250
  }) }),
9092
- status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9251
+ status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
9093
9252
  ] });
9094
9253
  }
9095
- var DASHBOARD_URL, constr, setConstr, FIELDS;
9254
+ var constr, setConstr, FIELDS;
9096
9255
  var init_Policies = __esm({
9097
9256
  "src/tui/panels/Policies.tsx"() {
9098
9257
  "use strict";
9099
9258
  init_api_client();
9100
- init_device_login();
9259
+ init_DryRun();
9101
9260
  init_components();
9102
9261
  init_hooks();
9103
9262
  init_theme();
9104
- DASHBOARD_URL = "https://dashboard.solongate.com";
9105
9263
  constr = (r, g, side) => (r[g]?.[side] ?? []).join(", ");
9106
9264
  setConstr = (r, g, side, val) => {
9107
9265
  const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
@@ -9127,19 +9285,19 @@ var init_Policies = __esm({
9127
9285
  });
9128
9286
 
9129
9287
  // src/tui/panels/RateLimit.tsx
9130
- import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
9131
- import { useEffect as useEffect4, useState as useState4 } from "react";
9132
- import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
9288
+ import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
9289
+ import { useEffect as useEffect5, useState as useState5 } from "react";
9290
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
9133
9291
  function RateLimitPanel({ focused }) {
9134
9292
  const { cols, rows } = usePanelSize();
9135
9293
  const layersQ = useLoader(() => api.settings.getSecurityLayers());
9136
9294
  const historyQ = useLoader(() => api.settings.getRateLimitHistory());
9137
9295
  const insightsQ = useLoader(() => api.stats.securityInsights(7));
9138
- const [draft, setDraft] = useState4(null);
9139
- const [dirty, setDirty] = useState4(false);
9140
- const [sel, setSel] = useState4(0);
9141
- const [status, setStatus] = useState4(null);
9142
- useEffect4(() => {
9296
+ const [draft, setDraft] = useState5(null);
9297
+ const [dirty, setDirty] = useState5(false);
9298
+ const [sel, setSel] = useState5(0);
9299
+ const [status, setStatus] = useState5(null);
9300
+ useEffect5(() => {
9143
9301
  if (layersQ.data && !dirty) setDraft({ ...layersQ.data.layers.rateLimit });
9144
9302
  }, [layersQ.data]);
9145
9303
  usePoll(() => {
@@ -9187,7 +9345,7 @@ function RateLimitPanel({ focused }) {
9187
9345
  const fi = onField ? selC : -1;
9188
9346
  const bcur = onField ? -1 : selC - 4;
9189
9347
  const off = bcur < 0 ? 0 : Math.min(Math.max(0, bcur - Math.floor(burstBudget / 2)), maxOff);
9190
- useInput3(
9348
+ useInput4(
9191
9349
  (input, key) => {
9192
9350
  const step = key.shift ? 10 : 1;
9193
9351
  if (key.upArrow) setSel((n) => Math.max(0, n - 1));
@@ -9206,48 +9364,48 @@ function RateLimitPanel({ focused }) {
9206
9364
  },
9207
9365
  { isActive: focused }
9208
9366
  );
9209
- return /* @__PURE__ */ jsx4(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
9367
+ return /* @__PURE__ */ jsx5(DataView, { loading: layersQ.loading && !draft, error: layersQ.error, children: draft ? (() => {
9210
9368
  const lim = draft.perMinute;
9211
9369
  const now = insightsQ.data ? insightsQ.data["activity"]?.minute?.slice(-1)[0]?.count ?? 0 : 0;
9212
9370
  const loadPct = lim > 0 ? Math.min(100, Math.round(now / lim * 100)) : 0;
9213
9371
  const last24h = (insightsQ.data?.["activity"]?.hour ?? []).reduce((s, b) => s + b.count, 0);
9214
9372
  const shown = anomalies2.slice(off, off + burstBudget);
9215
9373
  const bw = Math.max(10, cols - 30);
9216
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", height: rows, overflow: "hidden", children: [
9217
- /* @__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" }),
9218
- /* @__PURE__ */ jsxs4(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
9219
- /* @__PURE__ */ jsx4(Text4, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
9220
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
9374
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", height: rows, overflow: "hidden", children: [
9375
+ /* @__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" }),
9376
+ /* @__PURE__ */ jsxs5(FieldRow, { label: "Mode", active: focused && fi === 0, children: [
9377
+ /* @__PURE__ */ jsx5(Text5, { color: modeColor(draft.mode), bold: true, children: draft.mode }),
9378
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: draft.mode === "off" ? " no limit" : draft.mode === "detect" ? " flag bursts, never block" : " DENY calls over the limit" })
9221
9379
  ] }),
9222
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perMinute || "off" }) }),
9223
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour || "off" }) }),
9224
- /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay || "off" }) }),
9225
- showExtras && /* @__PURE__ */ jsxs4(Fragment4, { children: [
9226
- hasLoad ? /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9227
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load now".padEnd(13) }),
9228
- /* @__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))) }),
9229
- /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
9230
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
9380
+ /* @__PURE__ */ jsx5(FieldRow, { label: "Per minute", active: focused && fi === 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perMinute || "off" }) }),
9381
+ /* @__PURE__ */ jsx5(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perHour || "off" }) }),
9382
+ /* @__PURE__ */ jsx5(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: draft.perDay || "off" }) }),
9383
+ showExtras && /* @__PURE__ */ jsxs5(Fragment4, { children: [
9384
+ hasLoad ? /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9385
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "load now".padEnd(13) }),
9386
+ /* @__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))) }),
9387
+ /* @__PURE__ */ jsx5(Text5, { color: "#233457", children: "\u2591".repeat(Math.max(0, 18 - Math.round(now / lim * 18))) }),
9388
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` ${now}/${lim} this minute (${loadPct}%)` })
9231
9389
  ] }) : null,
9232
- /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9390
+ /* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9233
9391
  "Busiest 7d",
9234
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " most calls in one window vs your limit" })
9392
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " most calls in one window vs your limit" })
9235
9393
  ] }),
9236
- /* @__PURE__ */ jsx4(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
9237
- /* @__PURE__ */ jsx4(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
9238
- /* @__PURE__ */ jsx4(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
9239
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` })
9394
+ /* @__PURE__ */ jsx5(BusiestRow, { label: "minute", peak: peaks.minute, limit: draft.perMinute }),
9395
+ /* @__PURE__ */ jsx5(BusiestRow, { label: "hour", peak: peaks.hour, limit: draft.perHour }),
9396
+ /* @__PURE__ */ jsx5(BusiestRow, { label: "day", peak: peaks.day, limit: draft.perDay }),
9397
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: `activity`.padEnd(11) + ` last 24h \xB7 ${last24h} calls \xB7 busiest hour ${peaks.hour}/hr` })
9240
9398
  ] }),
9241
- /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
9242
- /* @__PURE__ */ jsxs4(Text4, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9399
+ /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
9400
+ /* @__PURE__ */ jsxs5(Text5, { color: theme.accentBright, bold: true, wrap: "truncate", children: [
9243
9401
  `Recent bursts 7d${anomalies2.length ? ` (${anomalies2.length})` : ""}`,
9244
- maxOff > 0 ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
9245
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
9402
+ maxOff > 0 ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: ` ${off + 1}-${Math.min(off + burstBudget, anomalies2.length)}/${anomalies2.length}` }) : null,
9403
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " over the per-minute limit \xB7 Bypassed = detect (not blocked)" })
9246
9404
  ] }),
9247
- anomalies2.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { color: theme.dim, children: [
9405
+ anomalies2.length === 0 ? /* @__PURE__ */ jsxs5(Text5, { color: theme.dim, children: [
9248
9406
  " ",
9249
9407
  insightsQ.loading ? "loading\u2026" : insightsQ.error ? `couldn't load bursts: ${String(insightsQ.error).slice(0, 60)}` : "no bursts in the last 7 days"
9250
- ] }) : /* @__PURE__ */ jsx4(
9408
+ ] }) : /* @__PURE__ */ jsx5(
9251
9409
  Table,
9252
9410
  {
9253
9411
  columns: [{ header: "WHEN", width: 15 }, { header: "RESULT", width: 9 }, { header: "CALLS", width: 7 }, { header: "AGENT", width: Math.max(8, bw - 31) }],
@@ -9263,13 +9421,13 @@ function RateLimitPanel({ focused }) {
9263
9421
  }
9264
9422
  )
9265
9423
  ] }),
9266
- /* @__PURE__ */ jsx4(Text4, { children: " " }),
9267
- /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9268
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "limit now " }),
9269
- /* @__PURE__ */ jsx4(Text4, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
9270
- lastChange ? /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
9271
- dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
9272
- status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
9424
+ /* @__PURE__ */ jsx5(Text5, { children: " " }),
9425
+ /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9426
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "limit now " }),
9427
+ /* @__PURE__ */ jsx5(Text5, { children: lim > 0 ? `${lim}/min` : "no per-minute limit" }),
9428
+ lastChange ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: ` \xB7 changed ${when(lastChange.ts)}` }) : null,
9429
+ dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s)" }) : null,
9430
+ status ? /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: " " + status }) : null
9273
9431
  ] })
9274
9432
  ] });
9275
9433
  })() : null });
@@ -9277,15 +9435,15 @@ function RateLimitPanel({ focused }) {
9277
9435
  function BusiestRow({ label, peak, limit }) {
9278
9436
  const ctx = limit > 0 ? peak > limit ? `over ${limit}` : peak === limit ? `at limit ${limit}` : `limit ${limit}` : "no limit set";
9279
9437
  const hot = limit > 0 && peak > limit;
9280
- return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9281
- /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: (" " + label).padEnd(11) }),
9282
- /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
9283
- /* @__PURE__ */ jsx4(Text4, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
9438
+ return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9439
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: (" " + label).padEnd(11) }),
9440
+ /* @__PURE__ */ jsx5(Text5, { color: hot ? theme.bad : void 0, bold: hot, children: String(peak).padStart(4) + " calls" }),
9441
+ /* @__PURE__ */ jsx5(Text5, { color: hot ? theme.bad : theme.dim, children: " \xB7 " + ctx })
9284
9442
  ] });
9285
9443
  }
9286
9444
  function FieldRow({ label, active: active2, children }) {
9287
- return /* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
9288
- /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
9445
+ return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9446
+ /* @__PURE__ */ jsx5(Text5, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + label.padEnd(11) }),
9289
9447
  children
9290
9448
  ] });
9291
9449
  }
@@ -9307,24 +9465,24 @@ var init_RateLimit = __esm({
9307
9465
  });
9308
9466
 
9309
9467
  // src/tui/panels/Dlp.tsx
9310
- import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
9468
+ import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
9311
9469
  import TextInput3 from "ink-text-input";
9312
- import { useEffect as useEffect5, useState as useState5 } from "react";
9313
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
9470
+ import { useEffect as useEffect6, useState as useState6 } from "react";
9471
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
9314
9472
  function DlpPanel({ focused }) {
9315
9473
  const { cols, rows } = usePanelSize();
9316
9474
  const q = useLoader(() => api.settings.getSecurityLayers());
9317
- const [dlp, setDlp] = useState5(null);
9318
- const [available, setAvailable] = useState5([]);
9319
- const [ghostOn, setGhostOn] = useState5(false);
9320
- const [ghostPats, setGhostPats] = useState5([]);
9321
- const [sel, setSel] = useState5(0);
9322
- const [dirty, setDirty] = useState5(false);
9323
- const [status, setStatus] = useState5(null);
9324
- const [adding, setAdding] = useState5(null);
9325
- const [newName, setNewName] = useState5("");
9326
- const [input, setInput] = useState5("");
9327
- useEffect5(() => {
9475
+ const [dlp, setDlp] = useState6(null);
9476
+ const [available, setAvailable] = useState6([]);
9477
+ const [ghostOn, setGhostOn] = useState6(false);
9478
+ const [ghostPats, setGhostPats] = useState6([]);
9479
+ const [sel, setSel] = useState6(0);
9480
+ const [dirty, setDirty] = useState6(false);
9481
+ const [status, setStatus] = useState6(null);
9482
+ const [adding, setAdding] = useState6(null);
9483
+ const [newName, setNewName] = useState6("");
9484
+ const [input, setInput] = useState6("");
9485
+ useEffect6(() => {
9328
9486
  if (q.data && !dirty) {
9329
9487
  setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
9330
9488
  setAvailable(q.data.availablePatterns);
@@ -9371,7 +9529,7 @@ function DlpPanel({ focused }) {
9371
9529
  ];
9372
9530
  const selC = Math.min(sel, Math.max(0, entries.length - 1));
9373
9531
  const cur = entries[selC];
9374
- useInput4(
9532
+ useInput5(
9375
9533
  (input2, key) => {
9376
9534
  if (!dlp) return;
9377
9535
  if (key.ctrl && input2 === "r") {
@@ -9416,13 +9574,13 @@ function DlpPanel({ focused }) {
9416
9574
  const enabled = new Set(dlp?.patterns ?? []);
9417
9575
  const lines = [];
9418
9576
  const header = (key, label, extra) => lines.push({
9419
- node: /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9420
- /* @__PURE__ */ jsx5(Text5, { color: theme.accentBright, bold: true, children: label }),
9421
- extra ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " " + extra }) : null
9577
+ node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9578
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: label }),
9579
+ extra ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + extra }) : null
9422
9580
  ] }, key),
9423
9581
  entry: -1
9424
9582
  });
9425
- const note = (key, text) => lines.push({ node: /* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: text }, key), entry: -1 });
9583
+ const note = (key, text) => lines.push({ node: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: text }, key), entry: -1 });
9426
9584
  let ei = 0;
9427
9585
  header("h:builtin", "built-in secret patterns", "space toggles on/off");
9428
9586
  available.forEach((p) => {
@@ -9430,10 +9588,10 @@ function DlpPanel({ focused }) {
9430
9588
  const isCur = focused && ei === selC;
9431
9589
  const idx = ei++;
9432
9590
  lines.push({
9433
- node: /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
9434
- /* @__PURE__ */ jsx5(Text5, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
9435
- /* @__PURE__ */ jsx5(Text5, { color: on ? theme.ok : theme.dim, children: on ? "\u25CF " : "\u25CB " }),
9436
- /* @__PURE__ */ jsx5(Text5, { color: on ? void 0 : theme.dim, children: p })
9591
+ node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
9592
+ /* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
9593
+ /* @__PURE__ */ jsx6(Text6, { color: on ? theme.ok : theme.dim, children: on ? "\u25CF " : "\u25CB " }),
9594
+ /* @__PURE__ */ jsx6(Text6, { color: on ? void 0 : theme.dim, children: p })
9437
9595
  ] }, "b:" + p),
9438
9596
  entry: idx
9439
9597
  });
@@ -9445,10 +9603,10 @@ function DlpPanel({ focused }) {
9445
9603
  const isCur = focused && ei === selC;
9446
9604
  const idx = ei++;
9447
9605
  lines.push({
9448
- node: /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
9449
- /* @__PURE__ */ jsx5(Text5, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
9450
- /* @__PURE__ */ jsx5(Text5, { color: theme.accent, children: c2.name }),
9451
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " " + truncate2(c2.re, Math.max(10, cols - c2.name.length - 8)) })
9606
+ node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
9607
+ /* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
9608
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: c2.name }),
9609
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " " + truncate2(c2.re, Math.max(10, cols - c2.name.length - 8)) })
9452
9610
  ] }, "c:" + c2.name + i),
9453
9611
  entry: idx
9454
9612
  });
@@ -9460,10 +9618,10 @@ function DlpPanel({ focused }) {
9460
9618
  const isCur = focused && ei === selC;
9461
9619
  const idx = ei++;
9462
9620
  lines.push({
9463
- node: /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
9464
- /* @__PURE__ */ jsx5(Text5, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
9465
- /* @__PURE__ */ jsx5(Text5, { color: ghostOn ? theme.accent : theme.dim, children: truncate2(p, Math.max(10, cols - 6)) }),
9466
- !ghostOn ? /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " (ghost off)" }) : null
9621
+ node: /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", backgroundColor: isCur ? "#1c2f63" : void 0, bold: isCur, children: [
9622
+ /* @__PURE__ */ jsx6(Text6, { color: isCur ? theme.accentBright : theme.dim, children: isCur ? "\u25B8 " : " " }),
9623
+ /* @__PURE__ */ jsx6(Text6, { color: ghostOn ? theme.accent : theme.dim, children: truncate2(p, Math.max(10, cols - 6)) }),
9624
+ !ghostOn ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (ghost off)" }) : null
9467
9625
  ] }, "g:" + p + i),
9468
9626
  entry: idx
9469
9627
  });
@@ -9477,17 +9635,17 @@ function DlpPanel({ focused }) {
9477
9635
  const win = lines.slice(start, start + budget);
9478
9636
  const moreAbove = start;
9479
9637
  const moreBelow = Math.max(0, lines.length - (start + budget));
9480
- return /* @__PURE__ */ jsx5(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
9481
- /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9482
- /* @__PURE__ */ jsx5(Text5, { children: "mode: " }),
9483
- /* @__PURE__ */ jsx5(Text5, { color: modeColor(dlp.mode), bold: true, children: dlp.mode }),
9484
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: dlp.mode === "off" ? " (scanning disabled)" : dlp.mode === "detect" ? " (flag dlp:yes, redact output)" : " (deny + redact secrets)" }),
9485
- dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
9638
+ return /* @__PURE__ */ jsx6(DataView, { loading: q.loading && !dlp, error: q.error, children: dlp ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9639
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9640
+ /* @__PURE__ */ jsx6(Text6, { children: "mode: " }),
9641
+ /* @__PURE__ */ jsx6(Text6, { color: modeColor(dlp.mode), bold: true, children: dlp.mode }),
9642
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: dlp.mode === "off" ? " (scanning disabled)" : dlp.mode === "detect" ? " (flag dlp:yes, redact output)" : " (deny + redact secrets)" }),
9643
+ dirty ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
9486
9644
  ] }),
9487
- /* @__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" }),
9488
- adding ? /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs5(Box5, { children: [
9489
- /* @__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): " }),
9490
- /* @__PURE__ */ jsx5(
9645
+ /* @__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" }),
9646
+ adding ? /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: /* @__PURE__ */ jsxs6(Box6, { children: [
9647
+ /* @__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): " }),
9648
+ /* @__PURE__ */ jsx6(
9491
9649
  TextInput3,
9492
9650
  {
9493
9651
  value: input,
@@ -9514,8 +9672,8 @@ function DlpPanel({ focused }) {
9514
9672
  }
9515
9673
  )
9516
9674
  ] }) }) : null,
9517
- /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
9518
- status ? /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
9675
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
9676
+ status ? /* @__PURE__ */ jsx6(Text6, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
9519
9677
  ] }) : null });
9520
9678
  }
9521
9679
  var MODES2;
@@ -9531,13 +9689,13 @@ var init_Dlp = __esm({
9531
9689
  });
9532
9690
 
9533
9691
  // src/tui/panels/Audit.tsx
9534
- import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
9692
+ import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9535
9693
  import TextInput4 from "ink-text-input";
9536
9694
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
9537
9695
  import { homedir as homedir8 } from "os";
9538
9696
  import { join as join10 } from "path";
9539
- import { useState as useState6 } from "react";
9540
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
9697
+ import { useState as useState7 } from "react";
9698
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9541
9699
  function loadLocalRows() {
9542
9700
  return parseLocalLines(tailLines(LOCAL_LOG, LOCAL_MAX_BYTES)).map((j, i) => {
9543
9701
  const rs = reasonSignals(j.reason);
@@ -9562,25 +9720,25 @@ function loadLocalRows() {
9562
9720
  }
9563
9721
  function AuditPanel({ active: active2, focused }) {
9564
9722
  const { cols, rows } = usePanelSize();
9565
- const [source, setSource] = useState6("cloud");
9566
- const [view, setView] = useState6("logs");
9567
- const [di, setDi] = useState6(0);
9568
- const [gi, setGi] = useState6(0);
9569
- const [tool, setTool] = useState6("");
9570
- const [agent, setAgent] = useState6("");
9571
- const [search, setSearch] = useState6("");
9572
- const [sessFilter, setSessFilter] = useState6("");
9573
- const [page, setPage] = useState6(0);
9574
- const [sel, setSel] = useState6(0);
9575
- const [detailScroll, setDetailScroll] = useState6(0);
9576
- const [editing, setEditing] = useState6(null);
9577
- const [si, setSi] = useState6(0);
9578
- const [sessSearch, setSessSearch] = useState6("");
9579
- const [sessSel, setSessSel] = useState6(0);
9580
- const [confirm, setConfirm] = useState6(null);
9581
- const [msg, setMsg] = useState6(null);
9582
- const [showHelp, setShowHelp] = useState6(false);
9583
- const [frozen, setFrozen] = useState6(false);
9723
+ const [source, setSource] = useState7("cloud");
9724
+ const [view, setView] = useState7("logs");
9725
+ const [di, setDi] = useState7(0);
9726
+ const [gi, setGi] = useState7(0);
9727
+ const [tool, setTool] = useState7("");
9728
+ const [agent, setAgent] = useState7("");
9729
+ const [search, setSearch] = useState7("");
9730
+ const [sessFilter, setSessFilter] = useState7("");
9731
+ const [page, setPage] = useState7(0);
9732
+ const [sel, setSel] = useState7(0);
9733
+ const [detailScroll, setDetailScroll] = useState7(0);
9734
+ const [editing, setEditing] = useState7(null);
9735
+ const [si, setSi] = useState7(0);
9736
+ const [sessSearch, setSessSearch] = useState7("");
9737
+ const [sessSel, setSessSel] = useState7(0);
9738
+ const [confirm, setConfirm] = useState7(null);
9739
+ const [msg, setMsg] = useState7(null);
9740
+ const [showHelp, setShowHelp] = useState7(false);
9741
+ const [frozen, setFrozen] = useState7(false);
9584
9742
  const toTop = () => setSel(0);
9585
9743
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
9586
9744
  usePoll(statsQ.reloadQuiet, 15e3, active2 && source === "cloud" && !frozen);
@@ -9699,7 +9857,7 @@ function AuditPanel({ active: active2, focused }) {
9699
9857
  };
9700
9858
  run10().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" }));
9701
9859
  };
9702
- useInput5(
9860
+ useInput6(
9703
9861
  (input, key) => {
9704
9862
  if (input === " ") {
9705
9863
  setFrozen((f) => !f);
@@ -9833,58 +9991,58 @@ function AuditPanel({ active: active2, focused }) {
9833
9991
  { isActive: focused && !editing }
9834
9992
  );
9835
9993
  const localAll = localQ.data ?? [];
9836
- const strip = source === "cloud" ? /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9837
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
9838
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls \xB7 " }),
9839
- /* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
9994
+ const strip = source === "cloud" ? /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9995
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: statsQ.data ? statsQ.data.total_calls : "\xB7\xB7\xB7\xB7" }),
9996
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " calls \xB7 " }),
9997
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
9840
9998
  statsQ.data?.allowed ?? "\xB7\xB7\xB7",
9841
9999
  " allow"
9842
10000
  ] }),
9843
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
9844
- /* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
10001
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
10002
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
9845
10003
  statsQ.data?.denied ?? "\xB7\xB7",
9846
10004
  " deny"
9847
10005
  ] }),
9848
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies` })
9849
- ] }) : /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9850
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: localAll.length }),
9851
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " calls in local file \xB7 " }),
9852
- /* @__PURE__ */ jsxs6(Text6, { color: theme.ok, children: [
10006
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` \xB7 ${statsQ.data?.active_policies ?? "\xB7"} policies` })
10007
+ ] }) : /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10008
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: localAll.length }),
10009
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " calls in local file \xB7 " }),
10010
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.ok, children: [
9853
10011
  localAll.filter((r) => r.decision === "ALLOW").length,
9854
10012
  " allow"
9855
10013
  ] }),
9856
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 " }),
9857
- /* @__PURE__ */ jsxs6(Text6, { color: theme.bad, children: [
10014
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
10015
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.bad, children: [
9858
10016
  localAll.filter((r) => r.decision !== "ALLOW").length,
9859
10017
  " deny"
9860
10018
  ] }),
9861
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
10019
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9862
10020
  " \xB7 ",
9863
10021
  LOCAL_LOG
9864
10022
  ] })
9865
10023
  ] });
9866
- 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;
9867
- const srcChip = /* @__PURE__ */ jsxs6(Text6, { children: [
9868
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "src:" }),
9869
- /* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
9870
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
10024
+ 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;
10025
+ const srcChip = /* @__PURE__ */ jsxs7(Text7, { children: [
10026
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "src:" }),
10027
+ /* @__PURE__ */ jsx7(Text7, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
10028
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " (s) " })
9871
10029
  ] });
9872
10030
  if (showHelp) {
9873
- 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: [
9874
- /* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
9875
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9876
- /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(19) }),
9877
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
10031
+ 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: [
10032
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accent, children: group }),
10033
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10034
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, children: (" " + k).padEnd(19) }),
10035
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: desc })
9878
10036
  ] }, k))
9879
10037
  ] }, group)) });
9880
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
10038
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9881
10039
  copyBanner,
9882
- /* @__PURE__ */ jsxs6(Text6, { bold: true, color: theme.accentBright, children: [
10040
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
9883
10041
  "AUDIT \u2014 all keys",
9884
10042
  " ",
9885
- /* @__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)" })
10043
+ /* @__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)" })
9886
10044
  ] }),
9887
- /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
10045
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
9888
10046
  colOf(AUDIT_HELP.slice(0, 1)),
9889
10047
  colOf(AUDIT_HELP.slice(1))
9890
10048
  ] })
@@ -9903,53 +10061,53 @@ function AuditPanel({ active: active2, focused }) {
9903
10061
  const maxScroll = Math.max(0, contentLines.length - bodyRows);
9904
10062
  const off = Math.min(detailScroll, maxScroll);
9905
10063
  const win = contentLines.slice(off, off + bodyRows);
9906
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
10064
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9907
10065
  copyBanner,
9908
- /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9909
- /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
9910
- /* @__PURE__ */ jsx6(Text6, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
9911
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: " " + e.tool }),
9912
- /* @__PURE__ */ jsx6(Text6, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
9913
- e.dlp.length ? /* @__PURE__ */ jsx6(Text6, { color: theme.bad, children: " DLP!" }) : null,
9914
- e.burst ? /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: " BURST" }) : null,
9915
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2190 back" })
10066
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10067
+ /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
10068
+ /* @__PURE__ */ jsx7(Text7, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
10069
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: " " + e.tool }),
10070
+ /* @__PURE__ */ jsx7(Text7, { color: loc ? theme.ok : "white", children: " " + (loc ? "LOC" : "CLD") }),
10071
+ e.dlp.length ? /* @__PURE__ */ jsx7(Text7, { color: theme.bad, children: " DLP!" }) : null,
10072
+ e.burst ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: " BURST" }) : null,
10073
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2190 back" })
9916
10074
  ] }),
9917
- /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9918
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2502 when " }),
9919
- /* @__PURE__ */ jsx6(Text6, { children: new Date(e.at).toLocaleString() }),
9920
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 perm " }),
9921
- /* @__PURE__ */ jsx6(Text6, { children: e.permission || "\u2014" }),
9922
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 trust " }),
9923
- /* @__PURE__ */ jsx6(Text6, { children: e.trust || "\u2014" }),
9924
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 eval " }),
9925
- /* @__PURE__ */ jsx6(Text6, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
9926
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 agent " }),
9927
- /* @__PURE__ */ jsx6(Text6, { children: e.agent ?? "\u2014" }),
9928
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502" })
10075
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10076
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2502 when " }),
10077
+ /* @__PURE__ */ jsx7(Text7, { children: new Date(e.at).toLocaleString() }),
10078
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 perm " }),
10079
+ /* @__PURE__ */ jsx7(Text7, { children: e.permission || "\u2014" }),
10080
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 trust " }),
10081
+ /* @__PURE__ */ jsx7(Text7, { children: e.trust || "\u2014" }),
10082
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 eval " }),
10083
+ /* @__PURE__ */ jsx7(Text7, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
10084
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 agent " }),
10085
+ /* @__PURE__ */ jsx7(Text7, { children: e.agent ?? "\u2014" }),
10086
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502" })
9929
10087
  ] }),
9930
- /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9931
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "\u2502 session " }),
9932
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: e.session ?? "\u2014" }),
9933
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502 rule " }),
9934
- /* @__PURE__ */ jsx6(Text6, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
9935
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \u2502" })
10088
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10089
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2502 session " }),
10090
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: e.session ?? "\u2014" }),
10091
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502 rule " }),
10092
+ /* @__PURE__ */ jsx7(Text7, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
10093
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2502" })
9936
10094
  ] }),
9937
- /* @__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 }),
9938
- /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx6(Text6, { wrap: "truncate", children: l || " " }, off + i)) }),
9939
- /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9940
- /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
9941
- /* @__PURE__ */ jsx6(Text6, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
9942
- /* @__PURE__ */ jsx6(Text6, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
10095
+ /* @__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 }),
10096
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: l || " " }, off + i)) }),
10097
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10098
+ /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", bold: true, children: " ENTRY " }),
10099
+ /* @__PURE__ */ jsx7(Text7, { backgroundColor: "#0b1530", color: "white", children: ` ${e.id} ` }),
10100
+ /* @__PURE__ */ jsx7(Text7, { backgroundColor: BG2, color: "white", children: " \u2191\u2193 scroll \xB7 space copy \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
9943
10101
  ] })
9944
10102
  ] });
9945
10103
  }
9946
- const chip = (label, val, on) => /* @__PURE__ */ jsxs6(Text6, { children: [
9947
- /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
10104
+ const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
10105
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9948
10106
  label,
9949
10107
  ":"
9950
10108
  ] }),
9951
- /* @__PURE__ */ jsx6(Text6, { color: on ? theme.accentBright : theme.dim, children: val }),
9952
- /* @__PURE__ */ jsx6(Text6, { children: " " })
10109
+ /* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
10110
+ /* @__PURE__ */ jsx7(Text7, { children: " " })
9953
10111
  ] });
9954
10112
  if (view === "sessions") {
9955
10113
  const headerRows2 = 5 + (editing ? 1 : 0);
@@ -9957,20 +10115,20 @@ function AuditPanel({ active: active2, focused }) {
9957
10115
  const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
9958
10116
  const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
9959
10117
  const win = sessionsFiltered.slice(start2, start2 + listRows2);
9960
- return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
10118
+ return /* @__PURE__ */ jsx7(DataView, { loading: sessLoading && !frozen, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9961
10119
  strip,
9962
10120
  copyBanner,
9963
- /* @__PURE__ */ jsxs6(Box6, { children: [
10121
+ /* @__PURE__ */ jsxs7(Box7, { children: [
9964
10122
  srcChip,
9965
- /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
9966
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 logs (v) " }),
10123
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "SESSIONS" }),
10124
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 logs (v) " }),
9967
10125
  chip("status", SESS_STATUS[si] ?? "all", si !== 0),
9968
10126
  chip("search", sessSearch || "\xB7", !!sessSearch)
9969
10127
  ] }),
9970
- /* @__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" }),
9971
- editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9972
- /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
9973
- /* @__PURE__ */ jsx6(
10128
+ /* @__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" }),
10129
+ editing === "sess-search" ? /* @__PURE__ */ jsxs7(Box7, { children: [
10130
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "search: " }),
10131
+ /* @__PURE__ */ jsx7(
9974
10132
  TextInput4,
9975
10133
  {
9976
10134
  value: sessSearch,
@@ -9982,8 +10140,8 @@ function AuditPanel({ active: active2, focused }) {
9982
10140
  }
9983
10141
  )
9984
10142
  ] }) : null,
9985
- /* @__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}` : ""}` }),
9986
- /* @__PURE__ */ jsx6(
10143
+ /* @__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}` : ""}` }),
10144
+ /* @__PURE__ */ jsx7(
9987
10145
  Table,
9988
10146
  {
9989
10147
  columns: [
@@ -10014,7 +10172,7 @@ function AuditPanel({ active: active2, focused }) {
10014
10172
  })
10015
10173
  }
10016
10174
  ),
10017
- sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
10175
+ sessionsFiltered.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10018
10176
  "(no sessions",
10019
10177
  source === "local" ? " in the local file" : "",
10020
10178
  ")"
@@ -10027,13 +10185,13 @@ function AuditPanel({ active: active2, focused }) {
10027
10185
  const maxStart = Math.max(0, pageRows.length - listRows);
10028
10186
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
10029
10187
  const windowed = pageRows.slice(start, start + listRows);
10030
- return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
10188
+ return /* @__PURE__ */ jsx7(DataView, { loading: logsLoading && !frozen, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
10031
10189
  strip,
10032
10190
  copyBanner,
10033
- /* @__PURE__ */ jsxs6(Box6, { children: [
10191
+ /* @__PURE__ */ jsxs7(Box7, { children: [
10034
10192
  srcChip,
10035
- /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, bold: true, children: "LOGS" }),
10036
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " \xB7 sessions (v) " }),
10193
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, children: "LOGS" }),
10194
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 sessions (v) " }),
10037
10195
  chip("dec", DECISIONS[di] ?? "all", di !== 0),
10038
10196
  chip("sig", SIGNALS[gi] ?? "all", gi !== 0),
10039
10197
  chip("tool", tool || "\xB7", !!tool),
@@ -10041,14 +10199,14 @@ function AuditPanel({ active: active2, focused }) {
10041
10199
  chip("search", search || "\xB7", !!search),
10042
10200
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
10043
10201
  ] }),
10044
- /* @__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" }),
10045
- msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
10046
- editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
10047
- /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
10202
+ /* @__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" }),
10203
+ msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
10204
+ editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs7(Box7, { children: [
10205
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
10048
10206
  editing,
10049
10207
  ": "
10050
10208
  ] }),
10051
- /* @__PURE__ */ jsx6(
10209
+ /* @__PURE__ */ jsx7(
10052
10210
  TextInput4,
10053
10211
  {
10054
10212
  value: editing === "tool" ? tool : editing === "agent" ? agent : search,
@@ -10061,9 +10219,9 @@ function AuditPanel({ active: active2, focused }) {
10061
10219
  }
10062
10220
  )
10063
10221
  ] }) : null,
10064
- /* @__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` : ""}` }),
10065
- /* @__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)) }),
10066
- pageRows.length === 0 ? /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
10222
+ /* @__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` : ""}` }),
10223
+ /* @__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)) }),
10224
+ pageRows.length === 0 ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
10067
10225
  "(no entries",
10068
10226
  source === "local" ? " in the local file" : "",
10069
10227
  " \u2014 c clears filters)"
@@ -10472,10 +10630,10 @@ var init_global_install = __esm({
10472
10630
  });
10473
10631
 
10474
10632
  // src/tui/panels/Settings.tsx
10475
- import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
10633
+ import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
10476
10634
  import TextInput5 from "ink-text-input";
10477
- import { useEffect as useEffect6, useRef as useRef3, useState as useState7 } from "react";
10478
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
10635
+ import { useEffect as useEffect7, useRef as useRef3, useState as useState8 } from "react";
10636
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
10479
10637
  function SettingsPanel({
10480
10638
  active: active2,
10481
10639
  focused,
@@ -10485,16 +10643,16 @@ function SettingsPanel({
10485
10643
  }) {
10486
10644
  void active2;
10487
10645
  const { cols, rows } = usePanelSize();
10488
- const [sel, setSel] = useState7(0);
10489
- const [editing, setEditing] = useState7(null);
10490
- const [input, setInput] = useState7("");
10491
- const [confirmDel, setConfirmDel] = useState7(null);
10492
- const [msg, setMsg] = useState7(null);
10493
- const [busy, setBusy] = useState7(false);
10494
- const [editor, setEditor] = useState7(null);
10495
- const [latest, setLatest] = useState7(null);
10646
+ const [sel, setSel] = useState8(0);
10647
+ const [editing, setEditing] = useState8(null);
10648
+ const [input, setInput] = useState8("");
10649
+ const [confirmDel, setConfirmDel] = useState8(null);
10650
+ const [msg, setMsg] = useState8(null);
10651
+ const [busy, setBusy] = useState8(false);
10652
+ const [editor, setEditor] = useState8(null);
10653
+ const [latest, setLatest] = useState8(null);
10496
10654
  const ver = currentVersion();
10497
- useEffect6(() => {
10655
+ useEffect7(() => {
10498
10656
  let live2 = true;
10499
10657
  latestVersion().then((v) => {
10500
10658
  if (live2) setLatest(v);
@@ -10504,26 +10662,26 @@ function SettingsPanel({
10504
10662
  live2 = false;
10505
10663
  };
10506
10664
  }, []);
10507
- const [accounts, setAccounts] = useState7(() => listAccounts());
10508
- const [login, setLogin] = useState7(null);
10509
- const [tick, setTick] = useState7(0);
10665
+ const [accounts, setAccounts] = useState8(() => listAccounts());
10666
+ const [login, setLogin] = useState8(null);
10667
+ const [tick, setTick] = useState8(0);
10510
10668
  const abort = useRef3(false);
10511
10669
  const refreshAccounts = () => setAccounts(listAccounts());
10512
- const [guardHere, setGuardHere] = useState7(() => isGuardInstalled());
10513
- const [guardVer, setGuardVer] = useState7(() => installedGuardVersion());
10514
- const [guardOld, setGuardOld] = useState7(() => guardHookOutdated());
10670
+ const [guardHere, setGuardHere] = useState8(() => isGuardInstalled());
10671
+ const [guardVer, setGuardVer] = useState8(() => installedGuardVersion());
10672
+ const [guardOld, setGuardOld] = useState8(() => guardHookOutdated());
10515
10673
  const refreshGuard = () => {
10516
10674
  setGuardHere(isGuardInstalled());
10517
10675
  setGuardVer(installedGuardVersion());
10518
10676
  setGuardOld(guardHookOutdated());
10519
10677
  };
10520
10678
  const locked = accounts.length === 0;
10521
- const [srv, setSrv] = useState7(() => logsServerStatus());
10522
- useEffect6(() => {
10679
+ const [srv, setSrv] = useState8(() => logsServerStatus());
10680
+ useEffect7(() => {
10523
10681
  const t = setInterval(() => setSrv(logsServerStatus()), 3e3);
10524
10682
  return () => clearInterval(t);
10525
10683
  }, []);
10526
- useEffect6(() => {
10684
+ useEffect7(() => {
10527
10685
  if (!login || login.phase === "done" || login.phase === "error") return;
10528
10686
  const t = setInterval(() => setTick((n) => n + 1), 120);
10529
10687
  return () => clearInterval(t);
@@ -10536,7 +10694,7 @@ function SettingsPanel({
10536
10694
  const local = localQ.data;
10537
10695
  const selfProt = selfQ.data;
10538
10696
  const autoInstalledRef = useRef3(false);
10539
- useEffect6(() => {
10697
+ useEffect7(() => {
10540
10698
  if (autoInstalledRef.current) return;
10541
10699
  if (guardHere && guardOld) {
10542
10700
  autoInstalledRef.current = true;
@@ -10548,7 +10706,7 @@ function SettingsPanel({
10548
10706
  }
10549
10707
  }
10550
10708
  }, [guardHere, guardOld]);
10551
- const [hidden, setHidden] = useState7(/* @__PURE__ */ new Set());
10709
+ const [hidden, setHidden] = useState8(/* @__PURE__ */ new Set());
10552
10710
  const webhooks = (whQ.data?.webhooks ?? []).filter((w) => !hidden.has("wh:" + w.id));
10553
10711
  const alerts = (alertQ.data?.rules ?? []).filter((r) => !hidden.has("alert:" + r.id));
10554
10712
  const hasEmailAlert = alerts.some((r) => alertChannel(r) === "email");
@@ -10729,7 +10887,7 @@ function SettingsPanel({
10729
10887
  setEditor({ ...editor, target: v, field: 1 });
10730
10888
  }
10731
10889
  };
10732
- useInput6(
10890
+ useInput7(
10733
10891
  (inp, key) => {
10734
10892
  if (login && (login.phase === "starting" || login.phase === "waiting")) {
10735
10893
  if (key.escape) {
@@ -10862,156 +11020,156 @@ function SettingsPanel({
10862
11020
  );
10863
11021
  const spin = SPIN2[tick % SPIN2.length];
10864
11022
  if (login && (login.phase === "starting" || login.phase === "waiting")) {
10865
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
10866
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: "Add an account" }),
10867
- login.phase === "starting" ? /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
11023
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
11024
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "Add an account" }),
11025
+ login.phase === "starting" ? /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
10868
11026
  spin,
10869
11027
  " starting device pairing\u2026"
10870
- ] }) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, children: [
10871
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
10872
- /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
10873
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
10874
- /* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
11028
+ ] }) : /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
11029
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Opening your browser to authorize. If it didn't open, visit:" }),
11030
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, wrap: "truncate", children: login.url }),
11031
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
11032
+ /* @__PURE__ */ jsxs8(Text8, { color: theme.warn, children: [
10875
11033
  spin,
10876
11034
  " waiting for authorization\u2026 "
10877
11035
  ] }),
10878
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "esc to cancel" })
11036
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "esc to cancel" })
10879
11037
  ] })
10880
11038
  ] })
10881
11039
  ] });
10882
11040
  }
10883
11041
  if (editor) {
10884
- const fieldRow = (idx, label, value, hint) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
10885
- /* @__PURE__ */ jsx7(Text7, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
10886
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(11) }),
11042
+ const fieldRow = (idx, label, value, hint) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
11043
+ /* @__PURE__ */ jsx8(Text8, { color: editor.field === idx ? theme.accentBright : theme.dim, children: editor.field === idx ? "\u25B8 " : " " }),
11044
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(11) }),
10887
11045
  value,
10888
- editor.field === idx && hint ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + hint }) : null
11046
+ editor.field === idx && hint ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + hint }) : null
10889
11047
  ] });
10890
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
10891
- /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
11048
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
11049
+ /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
10892
11050
  editor.id ? "Edit" : "New",
10893
11051
  " ",
10894
11052
  editor.channel,
10895
11053
  " alert"
10896
11054
  ] }),
10897
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
10898
- editing === "alert-target" ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
10899
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
10900
- /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
10901
- ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
10902
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
10903
- fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(editor.target, cols - 16) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
10904
- fieldRow(1, "signal", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
10905
- fieldRow(2, "threshold", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
10906
- fieldRow(3, "window", /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change"),
10907
- 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
11055
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2191\u2193 field \xB7 \u2190\u2192 change \xB7 e edit target \xB7 enter save \xB7 esc cancel" }),
11056
+ editing === "alert-target" ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
11057
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editor.channel === "email" ? "e-mail address: " : "telegram chat id (from @userinfobot): " }),
11058
+ /* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
11059
+ ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
11060
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
11061
+ fieldRow(0, "target", editor.target ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(editor.target, cols - 16) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "empty \u2014 press e" }), "press e to edit"),
11062
+ fieldRow(1, "signal", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }), "\u2190\u2192 change"),
11063
+ fieldRow(2, "threshold", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: `\u2265 ${editor.threshold}` }), "\u2190\u2192 \xB15"),
11064
+ fieldRow(3, "window", /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) }), "\u2190\u2192 change"),
11065
+ 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
10908
11066
  ] }),
10909
- /* @__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: [
11067
+ /* @__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: [
10910
11068
  "fires when ",
10911
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
11069
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: SIGNAL_LABEL[editor.signal] }),
10912
11070
  " reach ",
10913
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: editor.threshold }),
11071
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: editor.threshold }),
10914
11072
  " within",
10915
11073
  " ",
10916
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: winLabel(editor.windowSeconds) })
11074
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: winLabel(editor.windowSeconds) })
10917
11075
  ] }) })
10918
11076
  ] });
10919
11077
  }
10920
11078
  const loading = !locked && (localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data || guardQ.loading && !guardQ.data || selfQ.loading && !selfQ.data);
10921
11079
  const error = locked ? null : localQ.error ?? whQ.error ?? alertQ.error ?? guardQ.error ?? selfQ.error;
10922
- const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
11080
+ const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
10923
11081
  const chanOf = (rule) => [...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
10924
11082
  const isCur = (r) => keyOf(r) === keyOf(cur) && focused;
10925
- const cursor = (r) => /* @__PURE__ */ jsx7(Text7, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
11083
+ const cursor = (r) => /* @__PURE__ */ jsx8(Text8, { color: isCur(r) ? theme.accentBright : theme.dim, children: isCur(r) ? "\u25B8 " : " " });
10926
11084
  const rowLine = (r) => {
10927
11085
  switch (r.kind) {
10928
11086
  case "acct": {
10929
11087
  const a = r.acc;
10930
11088
  const isView = viewApiKey ? a.apiKey === viewApiKey : accounts[0]?.apiKey === a.apiKey;
10931
11089
  const isActive = isActiveAccount(a.apiKey);
10932
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11090
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10933
11091
  cursor(r),
10934
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(acctLabel(a), 30).padEnd(31) }),
10935
- isView ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
10936
- isActive ? /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " }),
10937
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: a.project ? truncate2(a.project, 20) : "" })
11092
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(acctLabel(a), 30).padEnd(31) }),
11093
+ isView ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "\u25CF viewing " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
11094
+ isActive ? /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: "ACTIVE " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " }),
11095
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: a.project ? truncate2(a.project, 20) : "" })
10938
11096
  ] });
10939
11097
  }
10940
11098
  case "acct-add":
10941
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11099
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10942
11100
  cursor(r),
10943
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
11101
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add account (enter \u2014 device login in the browser)" })
10944
11102
  ] });
10945
11103
  case "guard":
10946
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11104
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10947
11105
  cursor(r),
10948
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "guard".padEnd(11) }),
10949
- !guardHere ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
10950
- /* @__PURE__ */ jsx7(Text7, { color: theme.bad, bold: true, children: "removed" }),
10951
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 " }),
10952
- /* @__PURE__ */ jsx7(Text7, { color: theme.accentBright, children: "enter to install" })
10953
- ] }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
10954
- /* @__PURE__ */ jsx7(Text7, { color: guardOld ? theme.warn : theme.ok, children: `v${guardVer ?? "?"}` }),
10955
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: guardOld ? " \xB7 update available \xB7 enter update" : " (latest) \xB7 enter reinstall" }),
10956
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \xB7 d remove" })
11106
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "guard".padEnd(11) }),
11107
+ !guardHere ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
11108
+ /* @__PURE__ */ jsx8(Text8, { color: theme.bad, bold: true, children: "removed" }),
11109
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 " }),
11110
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: "enter to install" })
11111
+ ] }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
11112
+ /* @__PURE__ */ jsx8(Text8, { color: guardOld ? theme.warn : theme.ok, children: `v${guardVer ?? "?"}` }),
11113
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: guardOld ? " \xB7 update available \xB7 enter update" : " (latest) \xB7 enter reinstall" }),
11114
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \xB7 d remove" })
10957
11115
  ] })
10958
11116
  ] });
10959
11117
  case "self":
10960
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11118
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10961
11119
  cursor(r),
10962
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "self-prot".padEnd(11) }),
10963
- selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2026" }),
10964
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " blocks agents editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
11120
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "self-prot".padEnd(11) }),
11121
+ selfProt ? onOff(selfProt.enabled) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2026" }),
11122
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " blocks agents editing SolonGate\u2019s own hooks/config \xB7 enter toggles" })
10965
11123
  ] });
10966
11124
  case "ll-enabled":
10967
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11125
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10968
11126
  cursor(r),
10969
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(11) }),
11127
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(11) }),
10970
11128
  onOff(!!local?.enabled),
10971
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
11129
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
10972
11130
  ] });
10973
11131
  case "ll-path":
10974
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11132
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10975
11133
  cursor(r),
10976
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(11) }),
10977
- local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
11134
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(11) }),
11135
+ local?.path ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "not set \u2014 enter to edit" })
10978
11136
  ] });
10979
11137
  case "ll-server":
10980
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11138
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10981
11139
  cursor(r),
10982
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "dashboard".padEnd(11) }),
10983
- srv.running ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "stopped" }),
10984
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: srv.running ? " survives closing the dataroom \xB7 enter stops" : " enter starts the dashboard local-logs link" })
11140
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "dashboard".padEnd(11) }),
11141
+ srv.running ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: `running 127.0.0.1:${srv.port}` }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "stopped" }),
11142
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: srv.running ? " survives closing the dataroom \xB7 enter stops" : " enter starts the dashboard local-logs link" })
10985
11143
  ] });
10986
11144
  case "wh":
10987
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11145
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10988
11146
  cursor(r),
10989
11147
  onOff(r.wh.enabled),
10990
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + r.wh.events).padEnd(9) }),
10991
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(r.wh.url, cols - 16) })
11148
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + r.wh.events).padEnd(9) }),
11149
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(r.wh.url, cols - 16) })
10992
11150
  ] });
10993
11151
  case "wh-add":
10994
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11152
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
10995
11153
  cursor(r),
10996
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
11154
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
10997
11155
  ] });
10998
11156
  case "alert":
10999
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11157
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
11000
11158
  cursor(r),
11001
11159
  onOff(r.rule.enabled),
11002
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + SIGNAL_LABEL[r.rule.signal]).padEnd(13) }),
11003
- /* @__PURE__ */ jsx7(Text7, { children: `\u2265${r.rule.threshold}/${winLabel(r.rule.windowSeconds)} `.padEnd(11) }),
11004
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(r.rule), cols - 34) })
11160
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + SIGNAL_LABEL[r.rule.signal]).padEnd(13) }),
11161
+ /* @__PURE__ */ jsx8(Text8, { children: `\u2265${r.rule.threshold}/${winLabel(r.rule.windowSeconds)} `.padEnd(11) }),
11162
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(chanOf(r.rule), cols - 34) })
11005
11163
  ] });
11006
11164
  case "alert-add-email":
11007
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11165
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
11008
11166
  cursor(r),
11009
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add email alert (enter)" })
11167
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add email alert (enter)" })
11010
11168
  ] });
11011
11169
  case "alert-add-tg":
11012
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11170
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
11013
11171
  cursor(r),
11014
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
11172
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add telegram alert (enter \u2014 numeric chat id from @userinfobot)" })
11015
11173
  ] });
11016
11174
  }
11017
11175
  };
@@ -11030,39 +11188,39 @@ function SettingsPanel({
11030
11188
  const sec = sectionOf(r);
11031
11189
  if (sec !== lastSec) {
11032
11190
  if (lastSec) {
11033
- lineEls.push(/* @__PURE__ */ jsx7(Text7, { children: " " }, "sp:" + sec));
11191
+ lineEls.push(/* @__PURE__ */ jsx8(Text8, { children: " " }, "sp:" + sec));
11034
11192
  lineKey.push("");
11035
11193
  }
11036
11194
  lastSec = sec;
11037
11195
  lineEls.push(
11038
- /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
11039
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: sec }),
11040
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " \u2014 " + SECTION_DESC[sec] })
11196
+ /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
11197
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: sec }),
11198
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 " + SECTION_DESC[sec] })
11041
11199
  ] }, "h:" + sec)
11042
11200
  );
11043
11201
  lineKey.push("");
11044
11202
  if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
11045
11203
  lineEls.push(
11046
- /* @__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)
11204
+ /* @__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)
11047
11205
  );
11048
11206
  lineKey.push("");
11049
11207
  }
11050
11208
  }
11051
- lineEls.push(/* @__PURE__ */ jsx7(Box7, { children: rowLine(r) }, keyOf(r)));
11209
+ lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
11052
11210
  lineKey.push(keyOf(r));
11053
11211
  });
11054
11212
  if (hasEmailAlert && hasTgAlert) {
11055
11213
  lineEls.push(
11056
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
11214
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " both channels in use \u2014 remove one (d) to change it" }, "both-note")
11057
11215
  );
11058
11216
  lineKey.push("");
11059
11217
  }
11060
- lineEls.push(/* @__PURE__ */ jsx7(Text7, { children: " " }, "ver-sp"));
11218
+ lineEls.push(/* @__PURE__ */ jsx8(Text8, { children: " " }, "ver-sp"));
11061
11219
  lineKey.push("");
11062
11220
  lineEls.push(
11063
- /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", color: theme.dim, children: [
11221
+ /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", color: theme.dim, children: [
11064
11222
  `solongate v${ver}`,
11065
- 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
11223
+ 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
11066
11224
  ] }, "ver")
11067
11225
  );
11068
11226
  lineKey.push("");
@@ -11073,13 +11231,13 @@ function SettingsPanel({
11073
11231
  const win = lineEls.slice(startL, startL + budget);
11074
11232
  const moreAbove = startL;
11075
11233
  const moreBelow = Math.max(0, lineEls.length - (startL + budget));
11076
- return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
11077
- /* @__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" }),
11078
- editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
11079
- /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
11080
- /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
11081
- ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(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: " " }),
11082
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: budget, overflow: "hidden", children: win })
11234
+ return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
11235
+ /* @__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" }),
11236
+ editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
11237
+ /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : "webhook url: " }),
11238
+ /* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
11239
+ ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(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: " " }),
11240
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", height: budget, overflow: "hidden", children: win })
11083
11241
  ] }) });
11084
11242
  }
11085
11243
  var EVENTS, SPIN2, SIGNALS2, SIGNAL_LABEL, WINDOWS, winLabel, THRESH_MIN, THRESH_MAX, nearestWindowIdx, editorFieldCount, acctLabel, alertChannel;
@@ -11115,17 +11273,17 @@ var init_Settings = __esm({
11115
11273
  });
11116
11274
 
11117
11275
  // src/tui/App.tsx
11118
- import { Box as Box8, Text as Text8, useApp, useInput as useInput7 } from "ink";
11119
- import { useEffect as useEffect7, useState as useState8 } from "react";
11120
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
11276
+ import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
11277
+ import { useEffect as useEffect8, useState as useState9 } from "react";
11278
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
11121
11279
  function LiveHint() {
11122
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
11123
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
11124
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
11125
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
11126
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
11280
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
11281
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
11282
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
11283
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
11284
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
11127
11285
  "press ",
11128
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
11286
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
11129
11287
  " to go live"
11130
11288
  ] }) })
11131
11289
  ] });
@@ -11133,26 +11291,26 @@ function LiveHint() {
11133
11291
  function Banner({ cols }) {
11134
11292
  const wide = cols >= 82;
11135
11293
  if (!wide) {
11136
- return /* @__PURE__ */ jsxs8(Box8, { children: [
11137
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
11138
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
11294
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
11295
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
11296
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
11139
11297
  ] });
11140
11298
  }
11141
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
11142
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
11143
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
11299
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
11300
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
11301
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
11144
11302
  ] });
11145
11303
  }
11146
11304
  function App() {
11147
11305
  const { exit } = useApp();
11148
- const [accounts, setAccounts] = useState8(() => listAccounts());
11149
- const [viewKey, setViewKey] = useState8(() => accounts[0]?.apiKey);
11150
- const [viewNonce, setViewNonce] = useState8(0);
11151
- const [section, setSection] = useState8(accounts.length === 0 ? SETTINGS_SECTION : 0);
11152
- const [focus, setFocus] = useState8("nav");
11153
- const [help, setHelp] = useState8(false);
11154
- const [update2, setUpdate] = useState8({ kind: "idle" });
11155
- useEffect7(() => {
11306
+ const [accounts, setAccounts] = useState9(() => listAccounts());
11307
+ const [viewKey, setViewKey] = useState9(() => accounts[0]?.apiKey);
11308
+ const [viewNonce, setViewNonce] = useState9(0);
11309
+ const [section, setSection] = useState9(accounts.length === 0 ? SETTINGS_SECTION : 0);
11310
+ const [focus, setFocus] = useState9("nav");
11311
+ const [help, setHelp] = useState9(false);
11312
+ const [update2, setUpdate] = useState9({ kind: "idle" });
11313
+ useEffect8(() => {
11156
11314
  let alive = true;
11157
11315
  const run10 = () => void tuiUpdateFlow((s) => alive && setUpdate(s));
11158
11316
  run10();
@@ -11165,7 +11323,7 @@ function App() {
11165
11323
  const acctIdx = Math.max(0, accounts.findIndex((a) => a.apiKey === viewKey));
11166
11324
  const cur = accounts[acctIdx];
11167
11325
  const acctLabel2 = cur ? cur.email || cur.user || cur.project || `account \u2026${cur.apiKey.slice(-4)}` : "not logged in";
11168
- useEffect7(() => {
11326
+ useEffect8(() => {
11169
11327
  if (!cur || cur.email) return;
11170
11328
  let alive = true;
11171
11329
  api.auth.me().then((r) => {
@@ -11201,7 +11359,7 @@ function App() {
11201
11359
  };
11202
11360
  const locked = accounts.length === 0;
11203
11361
  const effectiveSection = locked ? SETTINGS_SECTION : section;
11204
- useInput7((input, key) => {
11362
+ useInput8((input, key) => {
11205
11363
  if (help) {
11206
11364
  setHelp(false);
11207
11365
  return;
@@ -11229,47 +11387,47 @@ function App() {
11229
11387
  });
11230
11388
  const { cols, rows } = useTermSize();
11231
11389
  const current = SECTIONS[effectiveSection];
11232
- if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
11390
+ if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
11233
11391
  if (current.label === "Live" && focus === "panel") {
11234
- return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }, viewNonce) });
11392
+ return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows - 1, overflow: "hidden", children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }, viewNonce) });
11235
11393
  }
11236
11394
  const Panel = current.Panel;
11237
- 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" });
11395
+ 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" });
11238
11396
  return (
11239
11397
  // height rows-1 (not rows): once total output reaches stdout.rows ink stops
11240
11398
  // diffing and clearTerminal-repaints every frame, which slides the banner.
11241
11399
  // overflow hidden makes it a hard guarantee even if a panel over-renders.
11242
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, paddingTop: 1, overflow: "hidden", children: [
11243
- /* @__PURE__ */ jsx8(Banner, { cols }),
11244
- /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
11245
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "account: " }),
11246
- /* @__PURE__ */ jsx8(Text8, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
11247
- 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" }),
11248
- 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
11400
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows - 1, paddingX: 1, paddingTop: 1, overflow: "hidden", children: [
11401
+ /* @__PURE__ */ jsx9(Banner, { cols }),
11402
+ /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
11403
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "account: " }),
11404
+ /* @__PURE__ */ jsx9(Text9, { color: locked ? theme.warn : theme.accentBright, bold: true, children: acctLabel2 }),
11405
+ 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" }),
11406
+ 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
11249
11407
  ] }),
11250
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
11251
- /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
11408
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
11409
+ /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => {
11252
11410
  const isCur = i === effectiveSection;
11253
11411
  const disabled = locked && s.label !== "Settings";
11254
- 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);
11412
+ 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);
11255
11413
  }) }),
11256
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, minWidth: 0, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, overflow: "hidden", children: panelBody })
11414
+ /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, minWidth: 0, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, overflow: "hidden", children: panelBody })
11257
11415
  ] }, viewNonce),
11258
- /* @__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"]] }) })
11416
+ /* @__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"]] }) })
11259
11417
  ] })
11260
11418
  );
11261
11419
  }
11262
11420
  function HelpOverlay({ cols, rows }) {
11263
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
11264
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
11265
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
11266
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
11267
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
11268
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
11269
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
11421
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
11422
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
11423
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
11424
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
11425
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
11426
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
11427
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
11270
11428
  ] }, k))
11271
11429
  ] }, group)) }),
11272
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
11430
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
11273
11431
  ] });
11274
11432
  }
11275
11433
  var SECTIONS, BANNER_HEX, SETTINGS_SECTION, HELP;
@@ -11284,6 +11442,7 @@ var init_App = __esm({
11284
11442
  init_RateLimit();
11285
11443
  init_Dlp();
11286
11444
  init_Audit();
11445
+ init_DryRun();
11287
11446
  init_Settings();
11288
11447
  init_hooks();
11289
11448
  init_client();
@@ -11294,6 +11453,7 @@ var init_App = __esm({
11294
11453
  { label: "Policies", Panel: PoliciesPanel },
11295
11454
  { label: "Rate Limit", Panel: RateLimitPanel },
11296
11455
  { label: "DLP", Panel: DlpPanel },
11456
+ { label: "Dry Run", Panel: DryRunPanel },
11297
11457
  { label: "Audit", Panel: AuditPanel },
11298
11458
  { label: "Settings", Panel: SettingsPanel }
11299
11459
  ];
@@ -11321,7 +11481,7 @@ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9 } from "fs";
11321
11481
  import { homedir as homedir10 } from "os";
11322
11482
  import { join as join12 } from "path";
11323
11483
  import { render } from "ink";
11324
- import { jsx as jsx9 } from "react/jsx-runtime";
11484
+ import { jsx as jsx10 } from "react/jsx-runtime";
11325
11485
  async function launchTui() {
11326
11486
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
11327
11487
  process.stderr.write(
@@ -11346,7 +11506,7 @@ async function launchTui() {
11346
11506
  console.info = toFile("info");
11347
11507
  console.debug = toFile("debug");
11348
11508
  try {
11349
- const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}), { patchConsole: false });
11509
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}), { patchConsole: false });
11350
11510
  await waitUntilExit();
11351
11511
  } finally {
11352
11512
  Object.assign(console, saved);