@solongate/proxy 0.81.24 → 0.81.25

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
@@ -6814,7 +6814,7 @@ function DataView({
6814
6814
  "\u2717 ",
6815
6815
  error
6816
6816
  ] });
6817
- if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "Loading\u2026" });
6817
+ if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u27F3 loading\u2026" });
6818
6818
  if (empty) return /* @__PURE__ */ jsx(Text, { color: theme.dim, children: emptyText ?? "No data." });
6819
6819
  return /* @__PURE__ */ jsx(Fragment, { children });
6820
6820
  }
@@ -7203,6 +7203,7 @@ function useLoader(fn, deps = []) {
7203
7203
  const [loading, setLoading] = useState(true);
7204
7204
  const [nonce, setNonce] = useState(0);
7205
7205
  const alive = useRef(true);
7206
+ const quiet = useRef(false);
7206
7207
  const fnRef = useRef(fn);
7207
7208
  fnRef.current = fn;
7208
7209
  useEffect(() => {
@@ -7212,7 +7213,8 @@ function useLoader(fn, deps = []) {
7212
7213
  };
7213
7214
  }, []);
7214
7215
  useEffect(() => {
7215
- setLoading(true);
7216
+ if (!quiet.current) setLoading(true);
7217
+ quiet.current = false;
7216
7218
  fnRef.current().then((d) => {
7217
7219
  if (!alive.current) return;
7218
7220
  setData(d);
@@ -7225,7 +7227,11 @@ function useLoader(fn, deps = []) {
7225
7227
  });
7226
7228
  }, [nonce, ...deps]);
7227
7229
  const reload = useCallback(() => setNonce((n) => n + 1), []);
7228
- return { data, error, loading, reload };
7230
+ const reloadQuiet = useCallback(() => {
7231
+ quiet.current = true;
7232
+ setNonce((n) => n + 1);
7233
+ }, []);
7234
+ return { data, error, loading, reload, reloadQuiet };
7229
7235
  }
7230
7236
  function usePoll(reload, intervalMs, enabled = true) {
7231
7237
  useEffect(() => {
@@ -7529,13 +7535,13 @@ function LivePanel({ active: active2 }) {
7529
7535
  const insights = useLoader(() => api.stats.securityInsights(7));
7530
7536
  const guard = useLoader(() => api.settings.getGuardStatus());
7531
7537
  usePoll(() => {
7532
- if (!paused()) sessions.reload();
7538
+ if (!paused()) sessions.reloadQuiet();
7533
7539
  }, 8e3, active2);
7534
7540
  usePoll(() => {
7535
- if (!paused()) insights.reload();
7541
+ if (!paused()) insights.reloadQuiet();
7536
7542
  }, 2e4, active2);
7537
7543
  usePoll(() => {
7538
- if (!paused()) guard.reload();
7544
+ if (!paused()) guard.reloadQuiet();
7539
7545
  }, 6e4, active2);
7540
7546
  useEffect2(() => {
7541
7547
  if (!detail) return;
@@ -8871,8 +8877,8 @@ function AuditPanel({ active: active2, focused }) {
8871
8877
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
8872
8878
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
8873
8879
  usePoll(() => {
8874
- statsQ.reload();
8875
- tsQ.reload();
8880
+ statsQ.reloadQuiet();
8881
+ tsQ.reloadQuiet();
8876
8882
  }, 15e3, active2 && source === "cloud");
8877
8883
  const query = {
8878
8884
  filter: DECISIONS[di],
@@ -8888,9 +8894,9 @@ function AuditPanel({ active: active2, focused }) {
8888
8894
  () => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
8889
8895
  [source, di, gi, tool, agent, search, sessFilter, page]
8890
8896
  );
8891
- usePoll(cloudQ.reload, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
8897
+ usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
8892
8898
  const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
8893
- usePoll(localQ.reload, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
8899
+ usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
8894
8900
  let pageRows = [];
8895
8901
  let total = 0;
8896
8902
  if (source === "cloud") {
@@ -8918,7 +8924,7 @@ function AuditPanel({ active: active2, focused }) {
8918
8924
  () => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
8919
8925
  [source]
8920
8926
  );
8921
- usePoll(agentsQ.reload, 8e3, active2 && source === "cloud" && view === "sessions");
8927
+ usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
8922
8928
  let sessions = [];
8923
8929
  if (source === "cloud") {
8924
8930
  sessions = (agentsQ.data?.agents ?? []).map((a) => ({
@@ -8978,7 +8984,7 @@ function AuditPanel({ active: active2, focused }) {
8978
8984
  else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
8979
8985
  else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
8980
8986
  else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
8981
- else if (key.return || key.rightArrow) {
8987
+ else if (key.return) {
8982
8988
  if (currentSess) {
8983
8989
  setSessFilter(currentSess.id);
8984
8990
  setPage(0);
@@ -9000,17 +9006,17 @@ function AuditPanel({ active: active2, focused }) {
9000
9006
  else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
9001
9007
  else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
9002
9008
  else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
9003
- else if (key.return || key.rightArrow) {
9009
+ else if (key.return) {
9004
9010
  if (current) {
9005
9011
  setDetailScroll(0);
9006
9012
  setView("detail");
9007
9013
  }
9008
- } else if (input === "]") {
9014
+ } else if (key.rightArrow) {
9009
9015
  if (page < pages - 1) {
9010
9016
  setPage(page + 1);
9011
9017
  toTop();
9012
9018
  }
9013
- } else if (input === "[") {
9019
+ } else if (key.leftArrow) {
9014
9020
  if (page > 0) {
9015
9021
  setPage(page - 1);
9016
9022
  toTop();
@@ -9149,7 +9155,7 @@ function AuditPanel({ active: active2, focused }) {
9149
9155
  const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
9150
9156
  const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
9151
9157
  const win = sessionsFiltered.slice(start2, start2 + listRows2);
9152
- const loading2 = source === "cloud" ? agentsQ.loading && !agentsQ.data : localQ.loading && !localQ.data;
9158
+ const loading2 = source === "cloud" ? agentsQ.loading : localQ.loading;
9153
9159
  return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9154
9160
  strip,
9155
9161
  /* @__PURE__ */ jsxs6(Box6, { children: [
@@ -9220,7 +9226,7 @@ function AuditPanel({ active: active2, focused }) {
9220
9226
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
9221
9227
  const windowed = pageRows.slice(start, start + listRows);
9222
9228
  const reasonW = Math.min(60, Math.max(12, cols - 67));
9223
- const loading = source === "cloud" ? cloudQ.loading && !cloudQ.data : localQ.loading && !localQ.data;
9229
+ const loading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
9224
9230
  return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9225
9231
  strip,
9226
9232
  /* @__PURE__ */ jsxs6(Box6, { children: [
@@ -9234,7 +9240,7 @@ function AuditPanel({ active: active2, focused }) {
9234
9240
  chip("search", search || "\xB7", !!search),
9235
9241
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
9236
9242
  ] }),
9237
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 [ ] page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
9243
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
9238
9244
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9239
9245
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
9240
9246
  editing,
@@ -9332,107 +9338,11 @@ var init_Audit = __esm({
9332
9338
  }
9333
9339
  });
9334
9340
 
9335
- // src/tui/panels/Agents.tsx
9336
- import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9337
- import { useState as useState7 } from "react";
9338
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9339
- function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
9340
- const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
9341
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9342
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
9343
- /* @__PURE__ */ jsx7(Text7, { color, children: "\u2588".repeat(filled) }),
9344
- /* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
9345
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + value })
9346
- ] });
9347
- }
9348
- function AgentsPanel({ focused }) {
9349
- const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
9350
- usePoll(live2.reload, 6e3, focused ? false : true);
9351
- const [sel, setSel] = useState7(0);
9352
- const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
9353
- const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
9354
- const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
9355
- useInput6(
9356
- (_input, key) => {
9357
- if (key.upArrow) setSel((n) => Math.max(0, n - 1));
9358
- else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
9359
- },
9360
- { isActive: focused }
9361
- );
9362
- const d = detail.data;
9363
- const base = d?.baseline;
9364
- const radar = base?.radar;
9365
- const anomalies2 = d?.anomalies ?? [];
9366
- const tmap = d?.trust_map;
9367
- const tools = tmap?.tools ?? [];
9368
- const resources = tmap?.resources ?? [];
9369
- const listW = 30;
9370
- return /* @__PURE__ */ jsx7(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs7(Box7, { children: [
9371
- /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
9372
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
9373
- agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
9374
- /* @__PURE__ */ jsxs7(Text7, { color: statusColor(a.status), children: [
9375
- a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
9376
- " "
9377
- ] }),
9378
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
9379
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `t${a.trust_score}` })
9380
- ] }, a.session_id))
9381
- ] }),
9382
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
9383
- /* @__PURE__ */ jsxs7(Box7, { children: [
9384
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
9385
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
9386
- ] }),
9387
- base?.characterBlurb ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate2(String(base.characterBlurb), 70) }) : null,
9388
- radar ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9389
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
9390
- /* @__PURE__ */ jsx7(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
9391
- /* @__PURE__ */ jsx7(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
9392
- /* @__PURE__ */ jsx7(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
9393
- /* @__PURE__ */ jsx7(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
9394
- /* @__PURE__ */ jsx7(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
9395
- ] }) : null,
9396
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9397
- /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9398
- "Anomalies ",
9399
- anomalies2.length ? `(${anomalies2.length})` : ""
9400
- ] }),
9401
- anomalies2.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9402
- /* @__PURE__ */ jsx7(Text7, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
9403
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: String(an.kind).padEnd(12) }),
9404
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate2(String(an.description ?? ""), 44) })
9405
- ] }, i))
9406
- ] }),
9407
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9408
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Trust map \xB7 top tools" }),
9409
- tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9410
- /* @__PURE__ */ jsx7(Text7, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate2(String(t.name), 16).padEnd(17)}` }),
9411
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
9412
- ] }, i)),
9413
- resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate2(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
9414
- ] })
9415
- ] }) })
9416
- ] }) });
9417
- }
9418
- var statusColor, sevColor;
9419
- var init_Agents = __esm({
9420
- "src/tui/panels/Agents.tsx"() {
9421
- "use strict";
9422
- init_api_client();
9423
- init_components();
9424
- init_hooks();
9425
- init_theme();
9426
- statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
9427
- sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
9428
- }
9429
- });
9430
-
9431
9341
  // src/tui/panels/Settings.tsx
9432
- import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
9342
+ import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
9433
9343
  import TextInput5 from "ink-text-input";
9434
- import { useState as useState8 } from "react";
9435
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
9344
+ import { useState as useState7 } from "react";
9345
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
9436
9346
  function alertBodyFor(channel) {
9437
9347
  const c2 = channel.trim();
9438
9348
  const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
@@ -9443,12 +9353,12 @@ function alertBodyFor(channel) {
9443
9353
  function SettingsPanel({ active: active2, focused }) {
9444
9354
  void active2;
9445
9355
  const { cols, rows } = usePanelSize();
9446
- const [sel, setSel] = useState8(0);
9447
- const [editing, setEditing] = useState8(null);
9448
- const [input, setInput] = useState8("");
9449
- const [confirmDel, setConfirmDel] = useState8(null);
9450
- const [msg, setMsg] = useState8(null);
9451
- const [busy, setBusy] = useState8(false);
9356
+ const [sel, setSel] = useState7(0);
9357
+ const [editing, setEditing] = useState7(null);
9358
+ const [input, setInput] = useState7("");
9359
+ const [confirmDel, setConfirmDel] = useState7(null);
9360
+ const [msg, setMsg] = useState7(null);
9361
+ const [busy, setBusy] = useState7(false);
9452
9362
  const localQ = useLoader(() => api.settings.getLocalLogs());
9453
9363
  const whQ = useLoader(() => api.settings.getWebhooks());
9454
9364
  const alertQ = useLoader(() => api.settings.getAlerts());
@@ -9518,7 +9428,7 @@ function SettingsPanel({ active: active2, focused }) {
9518
9428
  run12("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
9519
9429
  }
9520
9430
  };
9521
- useInput7(
9431
+ useInput6(
9522
9432
  (inp, key) => {
9523
9433
  setMsg(null);
9524
9434
  if (key.upArrow) {
@@ -9557,7 +9467,7 @@ function SettingsPanel({ active: active2, focused }) {
9557
9467
  const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
9558
9468
  const error = localQ.error ?? whQ.error ?? alertQ.error;
9559
9469
  const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
9560
- const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
9470
+ const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
9561
9471
  const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
9562
9472
  const listBudget = Math.max(4, rows - 8);
9563
9473
  const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
@@ -9565,68 +9475,68 @@ function SettingsPanel({ active: active2, focused }) {
9565
9475
  const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
9566
9476
  return i >= start && i < start + listBudget;
9567
9477
  };
9568
- return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
9569
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
9570
- editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
9571
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
9572
- /* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
9573
- ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
9574
- inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9575
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
9478
+ return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
9479
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
9480
+ editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
9481
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
9482
+ /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
9483
+ ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
9484
+ inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9485
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
9576
9486
  "LOCAL LOGS ",
9577
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
9487
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
9578
9488
  ] }),
9579
- /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9580
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
9581
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(10) }),
9489
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9490
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
9491
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
9582
9492
  onOff(!!local?.enabled),
9583
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
9493
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
9584
9494
  ] }),
9585
- /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9586
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
9587
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(10) }),
9588
- 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" })
9495
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9496
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
9497
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
9498
+ 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" })
9589
9499
  ] })
9590
9500
  ] }) : null,
9591
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9592
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
9501
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9502
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
9593
9503
  "WEBHOOKS ",
9594
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
9504
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9595
9505
  "\u2014 POST every matching event to your endpoint (",
9596
9506
  webhooks.length,
9597
9507
  ")"
9598
9508
  ] })
9599
9509
  ] }),
9600
- webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9601
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
9510
+ webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9511
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
9602
9512
  onOff(wh.enabled),
9603
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
9604
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
9513
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
9514
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
9605
9515
  ] }, wh.id)),
9606
- inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs8(Text8, { children: [
9607
- /* @__PURE__ */ jsx8(Text8, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
9608
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
9516
+ inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs7(Text7, { children: [
9517
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
9518
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
9609
9519
  ] }) : null
9610
9520
  ] }),
9611
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9612
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
9521
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9522
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
9613
9523
  "ALERTS ",
9614
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
9524
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9615
9525
  "\u2014 notify on a signal spike (",
9616
9526
  alerts.length,
9617
9527
  ")"
9618
9528
  ] })
9619
9529
  ] }),
9620
- alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9621
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
9530
+ alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
9531
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
9622
9532
  onOff(rule.enabled),
9623
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
9624
- /* @__PURE__ */ jsx8(Text8, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
9625
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
9533
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
9534
+ /* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
9535
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
9626
9536
  ] }, rule.id)),
9627
- inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs8(Text8, { children: [
9628
- /* @__PURE__ */ jsx8(Text8, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
9629
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
9537
+ inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs7(Text7, { children: [
9538
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
9539
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
9630
9540
  ] }) : null
9631
9541
  ] })
9632
9542
  ] }) });
@@ -9644,17 +9554,17 @@ var init_Settings = __esm({
9644
9554
  });
9645
9555
 
9646
9556
  // src/tui/App.tsx
9647
- import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
9648
- import { useState as useState9 } from "react";
9649
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
9557
+ import { Box as Box8, Text as Text8, useApp, useInput as useInput7 } from "ink";
9558
+ import { useState as useState8 } from "react";
9559
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
9650
9560
  function LiveHint() {
9651
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9652
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9653
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9654
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9655
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
9561
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
9562
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9563
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9564
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9565
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
9656
9566
  "press ",
9657
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
9567
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
9658
9568
  " to go live"
9659
9569
  ] }) })
9660
9570
  ] });
@@ -9662,22 +9572,22 @@ function LiveHint() {
9662
9572
  function Banner({ cols }) {
9663
9573
  const wide = cols >= 82;
9664
9574
  if (!wide) {
9665
- return /* @__PURE__ */ jsxs9(Box9, { children: [
9666
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9667
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
9575
+ return /* @__PURE__ */ jsxs8(Box8, { children: [
9576
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9577
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
9668
9578
  ] });
9669
9579
  }
9670
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9671
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9672
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9580
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
9581
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9582
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9673
9583
  ] });
9674
9584
  }
9675
9585
  function App() {
9676
9586
  const { exit } = useApp();
9677
- const [section, setSection] = useState9(0);
9678
- const [focus, setFocus] = useState9("nav");
9679
- const [help, setHelp] = useState9(false);
9680
- useInput8((input, key) => {
9587
+ const [section, setSection] = useState8(0);
9588
+ const [focus, setFocus] = useState8("nav");
9589
+ const [help, setHelp] = useState8(false);
9590
+ useInput7((input, key) => {
9681
9591
  if (help) {
9682
9592
  setHelp(false);
9683
9593
  return;
@@ -9698,31 +9608,31 @@ function App() {
9698
9608
  });
9699
9609
  const { cols, rows } = useTermSize();
9700
9610
  const current = SECTIONS[section];
9701
- if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
9611
+ if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
9702
9612
  if (current.label === "Live" && focus === "panel") {
9703
- return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
9613
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
9704
9614
  }
9705
9615
  const Panel = current.Panel;
9706
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9707
- /* @__PURE__ */ jsx9(Banner, { cols }),
9708
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
9709
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
9710
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
9616
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9617
+ /* @__PURE__ */ jsx8(Banner, { cols }),
9618
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
9619
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
9620
+ /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
9711
9621
  ] }),
9712
- /* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9622
+ /* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9713
9623
  ] });
9714
9624
  }
9715
9625
  function HelpOverlay({ cols, rows }) {
9716
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9717
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9718
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
9719
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
9720
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
9721
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9722
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
9626
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9627
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9628
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
9629
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
9630
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
9631
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9632
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
9723
9633
  ] }, k))
9724
9634
  ] }, group)) }),
9725
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
9635
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
9726
9636
  ] });
9727
9637
  }
9728
9638
  var SECTIONS, BANNER_HEX, HELP;
@@ -9737,12 +9647,10 @@ var init_App = __esm({
9737
9647
  init_RateLimit();
9738
9648
  init_Dlp();
9739
9649
  init_Audit();
9740
- init_Agents();
9741
9650
  init_Settings();
9742
9651
  init_hooks();
9743
9652
  SECTIONS = [
9744
9653
  { label: "Live", Panel: LiveHint },
9745
- { label: "Agents", Panel: AgentsPanel },
9746
9654
  { label: "Policies", Panel: PoliciesPanel },
9747
9655
  { label: "Rate Limit", Panel: RateLimitPanel },
9748
9656
  { label: "DLP", Panel: DlpPanel },
@@ -9755,7 +9663,7 @@ var init_App = __esm({
9755
9663
  ["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
9756
9664
  ["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
9757
9665
  ["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]],
9758
- ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["[ / ]", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
9666
+ ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
9759
9667
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
9760
9668
  ];
9761
9669
  }
@@ -9767,7 +9675,7 @@ __export(tui_exports, {
9767
9675
  launchTui: () => launchTui
9768
9676
  });
9769
9677
  import { render } from "ink";
9770
- import { jsx as jsx10 } from "react/jsx-runtime";
9678
+ import { jsx as jsx9 } from "react/jsx-runtime";
9771
9679
  async function launchTui() {
9772
9680
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
9773
9681
  process.stderr.write(
@@ -9781,7 +9689,7 @@ async function launchTui() {
9781
9689
  }
9782
9690
  process.stdout.write("\x1B[?1049h\x1B[H");
9783
9691
  try {
9784
- const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
9692
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
9785
9693
  await waitUntilExit();
9786
9694
  } finally {
9787
9695
  process.stdout.write("\x1B[?1049l");
@@ -10432,7 +10340,7 @@ async function runAgents(argv) {
10432
10340
  table(
10433
10341
  ["STATUS", "AGENT", "CALLS", "DENY", "DLP", "TRUST", "CHARACTER"],
10434
10342
  res.agents.map((a) => [
10435
- statusColor2(a.status),
10343
+ statusColor(a.status),
10436
10344
  cyan(truncate3(a.agent_name ?? a.agent_id ?? a.session_id, 20)),
10437
10345
  String(a.total_calls),
10438
10346
  a.denied_calls ? red(String(a.denied_calls)) : dim("0"),
@@ -10451,7 +10359,7 @@ async function runAgent(argv) {
10451
10359
  const a = await api.agents.get(id);
10452
10360
  if (json) return printJson(a), 0;
10453
10361
  err("");
10454
- err(` ${bold(a.agent_id)} status: ${statusColor2(String(a.status))}`);
10362
+ err(` ${bold(a.agent_id)} status: ${statusColor(String(a.status))}`);
10455
10363
  const base = a.baseline;
10456
10364
  if (base) {
10457
10365
  err(` ${dim(base.character ?? "")} trust ${bold(String(base.trustScore ?? "\u2014"))}/100 deny-rate ${(Number(base.denyRate ?? 0) * 100).toFixed(0)}%`);
@@ -10471,14 +10379,14 @@ async function runAgent(argv) {
10471
10379
  }
10472
10380
  return 0;
10473
10381
  }
10474
- var statusColor2;
10382
+ var statusColor;
10475
10383
  var init_agents2 = __esm({
10476
10384
  "src/commands/agents.ts"() {
10477
10385
  "use strict";
10478
10386
  init_api_client();
10479
10387
  init_args();
10480
10388
  init_format();
10481
- statusColor2 = (s) => s === "active" ? green(s) : s === "idle" ? yellow(s) : dim(s);
10389
+ statusColor = (s) => s === "active" ? green(s) : s === "idle" ? yellow(s) : dim(s);
10482
10390
  }
10483
10391
  });
10484
10392
 
@@ -3,6 +3,9 @@ export interface LoaderState<T> {
3
3
  error: string | null;
4
4
  loading: boolean;
5
5
  reload: () => void;
6
+ /** Refetch WITHOUT flipping `loading` — for background polls, so the
7
+ * visible "loading…" is reserved for user actions (filters, paging). */
8
+ reloadQuiet: () => void;
6
9
  }
7
10
  /**
8
11
  * Load async data once (and on demand via reload()). Safe against unmount —
package/dist/tui/index.js CHANGED
@@ -8,8 +8,8 @@ var __export = (target, all) => {
8
8
  import { render } from "ink";
9
9
 
10
10
  // src/tui/App.tsx
11
- import { Box as Box9, Text as Text9, useApp, useInput as useInput8 } from "ink";
12
- import { useState as useState9 } from "react";
11
+ import { Box as Box8, Text as Text8, useApp, useInput as useInput7 } from "ink";
12
+ import { useState as useState8 } from "react";
13
13
 
14
14
  // src/cli-utils.ts
15
15
  var c = {
@@ -142,7 +142,7 @@ function DataView({
142
142
  "\u2717 ",
143
143
  error
144
144
  ] });
145
- if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.dim, children: "Loading\u2026" });
145
+ if (loading) return /* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u27F3 loading\u2026" });
146
146
  if (empty) return /* @__PURE__ */ jsx(Text, { color: theme.dim, children: emptyText ?? "No data." });
147
147
  return /* @__PURE__ */ jsx(Fragment, { children });
148
148
  }
@@ -593,6 +593,7 @@ function useLoader(fn, deps = []) {
593
593
  const [loading, setLoading] = useState(true);
594
594
  const [nonce, setNonce] = useState(0);
595
595
  const alive = useRef(true);
596
+ const quiet = useRef(false);
596
597
  const fnRef = useRef(fn);
597
598
  fnRef.current = fn;
598
599
  useEffect(() => {
@@ -602,7 +603,8 @@ function useLoader(fn, deps = []) {
602
603
  };
603
604
  }, []);
604
605
  useEffect(() => {
605
- setLoading(true);
606
+ if (!quiet.current) setLoading(true);
607
+ quiet.current = false;
606
608
  fnRef.current().then((d) => {
607
609
  if (!alive.current) return;
608
610
  setData(d);
@@ -615,7 +617,11 @@ function useLoader(fn, deps = []) {
615
617
  });
616
618
  }, [nonce, ...deps]);
617
619
  const reload = useCallback(() => setNonce((n) => n + 1), []);
618
- return { data, error, loading, reload };
620
+ const reloadQuiet = useCallback(() => {
621
+ quiet.current = true;
622
+ setNonce((n) => n + 1);
623
+ }, []);
624
+ return { data, error, loading, reload, reloadQuiet };
619
625
  }
620
626
  function usePoll(reload, intervalMs, enabled = true) {
621
627
  useEffect(() => {
@@ -968,13 +974,13 @@ function LivePanel({ active: active2 }) {
968
974
  const insights = useLoader(() => api.stats.securityInsights(7));
969
975
  const guard = useLoader(() => api.settings.getGuardStatus());
970
976
  usePoll(() => {
971
- if (!paused()) sessions.reload();
977
+ if (!paused()) sessions.reloadQuiet();
972
978
  }, 8e3, active2);
973
979
  usePoll(() => {
974
- if (!paused()) insights.reload();
980
+ if (!paused()) insights.reloadQuiet();
975
981
  }, 2e4, active2);
976
982
  usePoll(() => {
977
- if (!paused()) guard.reload();
983
+ if (!paused()) guard.reloadQuiet();
978
984
  }, 6e4, active2);
979
985
  useEffect2(() => {
980
986
  if (!detail) return;
@@ -2235,8 +2241,8 @@ function AuditPanel({ active: active2, focused }) {
2235
2241
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
2236
2242
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
2237
2243
  usePoll(() => {
2238
- statsQ.reload();
2239
- tsQ.reload();
2244
+ statsQ.reloadQuiet();
2245
+ tsQ.reloadQuiet();
2240
2246
  }, 15e3, active2 && source === "cloud");
2241
2247
  const query = {
2242
2248
  filter: DECISIONS[di],
@@ -2252,9 +2258,9 @@ function AuditPanel({ active: active2, focused }) {
2252
2258
  () => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
2253
2259
  [source, di, gi, tool, agent, search, sessFilter, page]
2254
2260
  );
2255
- usePoll(cloudQ.reload, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
2261
+ usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
2256
2262
  const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
2257
- usePoll(localQ.reload, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
2263
+ usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
2258
2264
  let pageRows = [];
2259
2265
  let total = 0;
2260
2266
  if (source === "cloud") {
@@ -2282,7 +2288,7 @@ function AuditPanel({ active: active2, focused }) {
2282
2288
  () => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
2283
2289
  [source]
2284
2290
  );
2285
- usePoll(agentsQ.reload, 8e3, active2 && source === "cloud" && view === "sessions");
2291
+ usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
2286
2292
  let sessions = [];
2287
2293
  if (source === "cloud") {
2288
2294
  sessions = (agentsQ.data?.agents ?? []).map((a) => ({
@@ -2342,7 +2348,7 @@ function AuditPanel({ active: active2, focused }) {
2342
2348
  else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
2343
2349
  else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
2344
2350
  else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
2345
- else if (key.return || key.rightArrow) {
2351
+ else if (key.return) {
2346
2352
  if (currentSess) {
2347
2353
  setSessFilter(currentSess.id);
2348
2354
  setPage(0);
@@ -2364,17 +2370,17 @@ function AuditPanel({ active: active2, focused }) {
2364
2370
  else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
2365
2371
  else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
2366
2372
  else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
2367
- else if (key.return || key.rightArrow) {
2373
+ else if (key.return) {
2368
2374
  if (current) {
2369
2375
  setDetailScroll(0);
2370
2376
  setView("detail");
2371
2377
  }
2372
- } else if (input === "]") {
2378
+ } else if (key.rightArrow) {
2373
2379
  if (page < pages - 1) {
2374
2380
  setPage(page + 1);
2375
2381
  toTop();
2376
2382
  }
2377
- } else if (input === "[") {
2383
+ } else if (key.leftArrow) {
2378
2384
  if (page > 0) {
2379
2385
  setPage(page - 1);
2380
2386
  toTop();
@@ -2513,7 +2519,7 @@ function AuditPanel({ active: active2, focused }) {
2513
2519
  const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
2514
2520
  const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
2515
2521
  const win = sessionsFiltered.slice(start2, start2 + listRows2);
2516
- const loading2 = source === "cloud" ? agentsQ.loading && !agentsQ.data : localQ.loading && !localQ.data;
2522
+ const loading2 = source === "cloud" ? agentsQ.loading : localQ.loading;
2517
2523
  return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2518
2524
  strip,
2519
2525
  /* @__PURE__ */ jsxs6(Box6, { children: [
@@ -2584,7 +2590,7 @@ function AuditPanel({ active: active2, focused }) {
2584
2590
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
2585
2591
  const windowed = pageRows.slice(start, start + listRows);
2586
2592
  const reasonW = Math.min(60, Math.max(12, cols - 67));
2587
- const loading = source === "cloud" ? cloudQ.loading && !cloudQ.data : localQ.loading && !localQ.data;
2593
+ const loading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
2588
2594
  return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2589
2595
  strip,
2590
2596
  /* @__PURE__ */ jsxs6(Box6, { children: [
@@ -2598,7 +2604,7 @@ function AuditPanel({ active: active2, focused }) {
2598
2604
  chip("search", search || "\xB7", !!search),
2599
2605
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
2600
2606
  ] }),
2601
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 [ ] page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
2607
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
2602
2608
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
2603
2609
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
2604
2610
  editing,
@@ -2658,97 +2664,11 @@ function Detail({ label, value, color }) {
2658
2664
  ] });
2659
2665
  }
2660
2666
 
2661
- // src/tui/panels/Agents.tsx
2662
- import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
2663
- import { useState as useState7 } from "react";
2664
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2665
- var statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
2666
- var sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
2667
- function Bar({ label, value, max = 100, width = 16, color = theme.accent }) {
2668
- const filled = max > 0 ? Math.round(Math.min(value, max) / max * width) : 0;
2669
- return /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2670
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
2671
- /* @__PURE__ */ jsx7(Text7, { color, children: "\u2588".repeat(filled) }),
2672
- /* @__PURE__ */ jsx7(Text7, { color: "#233457", children: "\u2591".repeat(Math.max(0, width - filled)) }),
2673
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + value })
2674
- ] });
2675
- }
2676
- function AgentsPanel({ focused }) {
2677
- const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
2678
- usePoll(live2.reload, 6e3, focused ? false : true);
2679
- const [sel, setSel] = useState7(0);
2680
- const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
2681
- const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
2682
- const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
2683
- useInput6(
2684
- (_input, key) => {
2685
- if (key.upArrow) setSel((n) => Math.max(0, n - 1));
2686
- else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
2687
- },
2688
- { isActive: focused }
2689
- );
2690
- const d = detail.data;
2691
- const base = d?.baseline;
2692
- const radar = base?.radar;
2693
- const anomalies2 = d?.anomalies ?? [];
2694
- const tmap = d?.trust_map;
2695
- const tools = tmap?.tools ?? [];
2696
- const resources = tmap?.resources ?? [];
2697
- const listW = 30;
2698
- return /* @__PURE__ */ jsx7(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs7(Box7, { children: [
2699
- /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
2700
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
2701
- agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
2702
- /* @__PURE__ */ jsxs7(Text7, { color: statusColor(a.status), children: [
2703
- a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
2704
- " "
2705
- ] }),
2706
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
2707
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `t${a.trust_score}` })
2708
- ] }, a.session_id))
2709
- ] }),
2710
- /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
2711
- /* @__PURE__ */ jsxs7(Box7, { children: [
2712
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.accentBright, children: truncate(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
2713
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
2714
- ] }),
2715
- base?.characterBlurb ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate(String(base.characterBlurb), 70) }) : null,
2716
- radar ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2717
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
2718
- /* @__PURE__ */ jsx7(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
2719
- /* @__PURE__ */ jsx7(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
2720
- /* @__PURE__ */ jsx7(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
2721
- /* @__PURE__ */ jsx7(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
2722
- /* @__PURE__ */ jsx7(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
2723
- ] }) : null,
2724
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2725
- /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
2726
- "Anomalies ",
2727
- anomalies2.length ? `(${anomalies2.length})` : ""
2728
- ] }),
2729
- anomalies2.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2730
- /* @__PURE__ */ jsx7(Text7, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
2731
- /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: String(an.kind).padEnd(12) }),
2732
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: truncate(String(an.description ?? ""), 44) })
2733
- ] }, i))
2734
- ] }),
2735
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2736
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Trust map \xB7 top tools" }),
2737
- tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2738
- /* @__PURE__ */ jsx7(Text7, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate(String(t.name), 16).padEnd(17)}` }),
2739
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
2740
- ] }, i)),
2741
- resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
2742
- ] })
2743
- ] }) })
2744
- ] }) });
2745
- }
2746
-
2747
2667
  // src/tui/panels/Settings.tsx
2748
- import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
2668
+ import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
2749
2669
  import TextInput5 from "ink-text-input";
2750
- import { useState as useState8 } from "react";
2751
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2670
+ import { useState as useState7 } from "react";
2671
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2752
2672
  var EVENTS = ["denials", "allowed", "all"];
2753
2673
  function alertBodyFor(channel) {
2754
2674
  const c2 = channel.trim();
@@ -2760,12 +2680,12 @@ function alertBodyFor(channel) {
2760
2680
  function SettingsPanel({ active: active2, focused }) {
2761
2681
  void active2;
2762
2682
  const { cols, rows } = usePanelSize();
2763
- const [sel, setSel] = useState8(0);
2764
- const [editing, setEditing] = useState8(null);
2765
- const [input, setInput] = useState8("");
2766
- const [confirmDel, setConfirmDel] = useState8(null);
2767
- const [msg, setMsg] = useState8(null);
2768
- const [busy, setBusy] = useState8(false);
2683
+ const [sel, setSel] = useState7(0);
2684
+ const [editing, setEditing] = useState7(null);
2685
+ const [input, setInput] = useState7("");
2686
+ const [confirmDel, setConfirmDel] = useState7(null);
2687
+ const [msg, setMsg] = useState7(null);
2688
+ const [busy, setBusy] = useState7(false);
2769
2689
  const localQ = useLoader(() => api.settings.getLocalLogs());
2770
2690
  const whQ = useLoader(() => api.settings.getWebhooks());
2771
2691
  const alertQ = useLoader(() => api.settings.getAlerts());
@@ -2835,7 +2755,7 @@ function SettingsPanel({ active: active2, focused }) {
2835
2755
  run("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
2836
2756
  }
2837
2757
  };
2838
- useInput7(
2758
+ useInput6(
2839
2759
  (inp, key) => {
2840
2760
  setMsg(null);
2841
2761
  if (key.upArrow) {
@@ -2874,7 +2794,7 @@ function SettingsPanel({ active: active2, focused }) {
2874
2794
  const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
2875
2795
  const error = localQ.error ?? whQ.error ?? alertQ.error;
2876
2796
  const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
2877
- const onOff = (on) => on ? /* @__PURE__ */ jsx8(Text8, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "off" });
2797
+ const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
2878
2798
  const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
2879
2799
  const listBudget = Math.max(4, rows - 8);
2880
2800
  const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
@@ -2882,90 +2802,89 @@ function SettingsPanel({ active: active2, focused }) {
2882
2802
  const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
2883
2803
  return i >= start && i < start + listBudget;
2884
2804
  };
2885
- return /* @__PURE__ */ jsx8(DataView, { loading, error, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2886
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
2887
- editing ? /* @__PURE__ */ jsxs8(Box8, { children: [
2888
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
2889
- /* @__PURE__ */ jsx8(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
2890
- ] }) : msg ? /* @__PURE__ */ jsx8(Text8, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx8(Text8, { children: " " }),
2891
- inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2892
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
2805
+ return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2806
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 enter/space toggle-edit-add \xB7 e events \xB7 d delete \xB7 r refresh" : "press \u2192 to configure" }),
2807
+ editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
2808
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
2809
+ /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
2810
+ ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
2811
+ inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2812
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
2893
2813
  "LOCAL LOGS ",
2894
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
2814
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
2895
2815
  ] }),
2896
- /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
2897
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
2898
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "enabled ".padEnd(10) }),
2816
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2817
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
2818
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
2899
2819
  onOff(!!local?.enabled),
2900
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
2820
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
2901
2821
  ] }),
2902
- /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
2903
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
2904
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "path ".padEnd(10) }),
2905
- local?.path ? /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(local.path, cols - 14) }) : /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "not set \u2014 enter to edit" })
2822
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2823
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
2824
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
2825
+ local?.path ? /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(local.path, cols - 14) }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "not set \u2014 enter to edit" })
2906
2826
  ] })
2907
2827
  ] }) : null,
2908
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2909
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
2828
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2829
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
2910
2830
  "WEBHOOKS ",
2911
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
2831
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
2912
2832
  "\u2014 POST every matching event to your endpoint (",
2913
2833
  webhooks.length,
2914
2834
  ")"
2915
2835
  ] })
2916
2836
  ] }),
2917
- webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
2918
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
2837
+ webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2838
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
2919
2839
  onOff(wh.enabled),
2920
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
2921
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(wh.url, cols - 16) })
2840
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
2841
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(wh.url, cols - 16) })
2922
2842
  ] }, wh.id)),
2923
- inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs8(Text8, { children: [
2924
- /* @__PURE__ */ jsx8(Text8, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
2925
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add webhook (enter)" })
2843
+ inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs7(Text7, { children: [
2844
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
2845
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
2926
2846
  ] }) : null
2927
2847
  ] }),
2928
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2929
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
2848
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2849
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
2930
2850
  "ALERTS ",
2931
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
2851
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
2932
2852
  "\u2014 notify on a signal spike (",
2933
2853
  alerts.length,
2934
2854
  ")"
2935
2855
  ] })
2936
2856
  ] }),
2937
- alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
2938
- /* @__PURE__ */ jsx8(Text8, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
2857
+ alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2858
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
2939
2859
  onOff(rule.enabled),
2940
- /* @__PURE__ */ jsx8(Text8, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
2941
- /* @__PURE__ */ jsx8(Text8, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
2942
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate(chanOf(rule), cols - 26) })
2860
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
2861
+ /* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
2862
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(chanOf(rule), cols - 26) })
2943
2863
  ] }, rule.id)),
2944
- inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs8(Text8, { children: [
2945
- /* @__PURE__ */ jsx8(Text8, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
2946
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
2864
+ inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs7(Text7, { children: [
2865
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
2866
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
2947
2867
  ] }) : null
2948
2868
  ] })
2949
2869
  ] }) });
2950
2870
  }
2951
2871
 
2952
2872
  // src/tui/App.tsx
2953
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2873
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2954
2874
  function LiveHint() {
2955
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
2956
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
2957
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
2958
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
2959
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
2875
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2876
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
2877
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
2878
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
2879
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
2960
2880
  "press ",
2961
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
2881
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
2962
2882
  " to go live"
2963
2883
  ] }) })
2964
2884
  ] });
2965
2885
  }
2966
2886
  var SECTIONS = [
2967
2887
  { label: "Live", Panel: LiveHint },
2968
- { label: "Agents", Panel: AgentsPanel },
2969
2888
  { label: "Policies", Panel: PoliciesPanel },
2970
2889
  { label: "Rate Limit", Panel: RateLimitPanel },
2971
2890
  { label: "DLP", Panel: DlpPanel },
@@ -2976,22 +2895,22 @@ var BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8F
2976
2895
  function Banner({ cols }) {
2977
2896
  const wide = cols >= 82;
2978
2897
  if (!wide) {
2979
- return /* @__PURE__ */ jsxs9(Box9, { children: [
2980
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
2981
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
2898
+ return /* @__PURE__ */ jsxs8(Box8, { children: [
2899
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
2900
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
2982
2901
  ] });
2983
2902
  }
2984
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
2985
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
2986
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
2903
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2904
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
2905
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
2987
2906
  ] });
2988
2907
  }
2989
2908
  function App() {
2990
2909
  const { exit } = useApp();
2991
- const [section, setSection] = useState9(0);
2992
- const [focus, setFocus] = useState9("nav");
2993
- const [help, setHelp] = useState9(false);
2994
- useInput8((input, key) => {
2910
+ const [section, setSection] = useState8(0);
2911
+ const [focus, setFocus] = useState8("nav");
2912
+ const [help, setHelp] = useState8(false);
2913
+ useInput7((input, key) => {
2995
2914
  if (help) {
2996
2915
  setHelp(false);
2997
2916
  return;
@@ -3012,18 +2931,18 @@ function App() {
3012
2931
  });
3013
2932
  const { cols, rows } = useTermSize();
3014
2933
  const current = SECTIONS[section];
3015
- if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
2934
+ if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
3016
2935
  if (current.label === "Live" && focus === "panel") {
3017
- return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
2936
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
3018
2937
  }
3019
2938
  const Panel = current.Panel;
3020
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
3021
- /* @__PURE__ */ jsx9(Banner, { cols }),
3022
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
3023
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
3024
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
2939
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
2940
+ /* @__PURE__ */ jsx8(Banner, { cols }),
2941
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
2942
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
2943
+ /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
3025
2944
  ] }),
3026
- /* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
2945
+ /* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
3027
2946
  ] });
3028
2947
  }
3029
2948
  var HELP = [
@@ -3031,25 +2950,25 @@ var HELP = [
3031
2950
  ["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
3032
2951
  ["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
3033
2952
  ["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]],
3034
- ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["[ / ]", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
2953
+ ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
3035
2954
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
3036
2955
  ];
3037
2956
  function HelpOverlay({ cols, rows }) {
3038
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
3039
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
3040
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
3041
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
3042
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
3043
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
3044
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
2957
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
2958
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
2959
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
2960
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
2961
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
2962
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
2963
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
3045
2964
  ] }, k))
3046
2965
  ] }, group)) }),
3047
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
2966
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
3048
2967
  ] });
3049
2968
  }
3050
2969
 
3051
2970
  // src/tui/index.tsx
3052
- import { jsx as jsx10 } from "react/jsx-runtime";
2971
+ import { jsx as jsx9 } from "react/jsx-runtime";
3053
2972
  async function launchTui() {
3054
2973
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
3055
2974
  process.stderr.write(
@@ -3063,7 +2982,7 @@ async function launchTui() {
3063
2982
  }
3064
2983
  process.stdout.write("\x1B[?1049h\x1B[H");
3065
2984
  try {
3066
- const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
2985
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
3067
2986
  await waitUntilExit();
3068
2987
  } finally {
3069
2988
  process.stdout.write("\x1B[?1049l");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.24",
3
+ "version": "0.81.25",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +0,0 @@
1
- export declare function AgentsPanel({ focused }: {
2
- active: boolean;
3
- focused: boolean;
4
- }): JSX.Element;
@@ -1,3 +0,0 @@
1
- export declare function StatsPanel({ active }: {
2
- active: boolean;
3
- }): JSX.Element;