@solongate/proxy 0.81.24 → 0.81.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui/index.js CHANGED
@@ -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
  }
@@ -169,12 +169,12 @@ function KeyHints({ hints }) {
169
169
  // src/tui/panels/Live.tsx
170
170
  import { Box as Box2, Text as Text2, useInput } from "ink";
171
171
  import TextInput from "ink-text-input";
172
- import { mkdirSync, writeFileSync } from "fs";
172
+ import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
173
173
  import { homedir as homedir4 } from "os";
174
174
  import { join as join4 } from "path";
175
175
 
176
176
  // src/tui/local-log.ts
177
- import { closeSync, openSync, readSync, statSync } from "fs";
177
+ import { closeSync, openSync, readFileSync as readFileSync2, readSync, statSync, writeFileSync } from "fs";
178
178
  import { homedir as homedir2 } from "os";
179
179
  import { join as join2 } from "path";
180
180
  var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
@@ -193,6 +193,39 @@ function tailLines(file, maxBytes = 131072) {
193
193
  return [];
194
194
  }
195
195
  }
196
+ function deleteLocalEntry(at, tool, session) {
197
+ try {
198
+ const lines = readFileSync2(LOCAL_LOG, "utf-8").split("\n");
199
+ let removed = 0;
200
+ const kept = lines.filter((line) => {
201
+ if (!line.trim()) return false;
202
+ if (removed) return true;
203
+ try {
204
+ const j = JSON.parse(line);
205
+ const hit = Date.parse(j.ts ?? "") === at && (j.tool ?? "?") === tool && (!session || j.session_id === session);
206
+ if (hit) {
207
+ removed++;
208
+ return false;
209
+ }
210
+ } catch {
211
+ }
212
+ return true;
213
+ });
214
+ if (removed) writeFileSync(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
215
+ return removed;
216
+ } catch {
217
+ return 0;
218
+ }
219
+ }
220
+ function clearLocalLog() {
221
+ try {
222
+ const n = readFileSync2(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
223
+ writeFileSync(LOCAL_LOG, "");
224
+ return n;
225
+ } catch {
226
+ return 0;
227
+ }
228
+ }
196
229
  function parseLocalLines(lines) {
197
230
  const out = [];
198
231
  for (const line of lines) {
@@ -210,7 +243,7 @@ function parseLocalLines(lines) {
210
243
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
211
244
 
212
245
  // src/api-client/client.ts
213
- import { readFileSync as readFileSync2, existsSync } from "fs";
246
+ import { readFileSync as readFileSync3, existsSync } from "fs";
214
247
  import { resolve, join as join3 } from "path";
215
248
  import { homedir as homedir3 } from "os";
216
249
  var DEFAULT_API_URL = "https://api.solongate.com";
@@ -234,7 +267,7 @@ function loginCredentialFile() {
234
267
  try {
235
268
  const p = join3(homedir3(), ".solongate", "cloud-guard.json");
236
269
  if (!existsSync(p)) return {};
237
- const c2 = JSON.parse(readFileSync2(p, "utf-8"));
270
+ const c2 = JSON.parse(readFileSync3(p, "utf-8"));
238
271
  return c2 && typeof c2 === "object" ? c2 : {};
239
272
  } catch {
240
273
  return {};
@@ -244,7 +277,7 @@ function dotenvApiKey() {
244
277
  try {
245
278
  const envPath = resolve(".env");
246
279
  if (!existsSync(envPath)) return void 0;
247
- for (const line of readFileSync2(envPath, "utf-8").split("\n")) {
280
+ for (const line of readFileSync3(envPath, "utf-8").split("\n")) {
248
281
  const trimmed = line.trim();
249
282
  if (!trimmed || trimmed.startsWith("#")) continue;
250
283
  const eq = trimmed.indexOf("=");
@@ -496,11 +529,19 @@ var audit_exports = {};
496
529
  __export(audit_exports, {
497
530
  block: () => block,
498
531
  list: () => list2,
532
+ remove: () => remove2,
533
+ removeAll: () => removeAll,
499
534
  whitelist: () => whitelist
500
535
  });
501
536
  function list2(query = {}) {
502
537
  return request("GET", "/audit-logs", { query });
503
538
  }
539
+ function remove2(ids) {
540
+ return request("DELETE", "/audit-logs", { body: { ids } });
541
+ }
542
+ function removeAll() {
543
+ return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
544
+ }
504
545
  function whitelist(id, scope = "exact") {
505
546
  return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
506
547
  }
@@ -593,6 +634,7 @@ function useLoader(fn, deps = []) {
593
634
  const [loading, setLoading] = useState(true);
594
635
  const [nonce, setNonce] = useState(0);
595
636
  const alive = useRef(true);
637
+ const quiet = useRef(false);
596
638
  const fnRef = useRef(fn);
597
639
  fnRef.current = fn;
598
640
  useEffect(() => {
@@ -602,7 +644,8 @@ function useLoader(fn, deps = []) {
602
644
  };
603
645
  }, []);
604
646
  useEffect(() => {
605
- setLoading(true);
647
+ if (!quiet.current) setLoading(true);
648
+ quiet.current = false;
606
649
  fnRef.current().then((d) => {
607
650
  if (!alive.current) return;
608
651
  setData(d);
@@ -615,7 +658,11 @@ function useLoader(fn, deps = []) {
615
658
  });
616
659
  }, [nonce, ...deps]);
617
660
  const reload = useCallback(() => setNonce((n) => n + 1), []);
618
- return { data, error, loading, reload };
661
+ const reloadQuiet = useCallback(() => {
662
+ quiet.current = true;
663
+ setNonce((n) => n + 1);
664
+ }, []);
665
+ return { data, error, loading, reload, reloadQuiet };
619
666
  }
620
667
  function usePoll(reload, intervalMs, enabled = true) {
621
668
  useEffect(() => {
@@ -968,13 +1015,13 @@ function LivePanel({ active: active2 }) {
968
1015
  const insights = useLoader(() => api.stats.securityInsights(7));
969
1016
  const guard = useLoader(() => api.settings.getGuardStatus());
970
1017
  usePoll(() => {
971
- if (!paused()) sessions.reload();
1018
+ if (!paused()) sessions.reloadQuiet();
972
1019
  }, 8e3, active2);
973
1020
  usePoll(() => {
974
- if (!paused()) insights.reload();
1021
+ if (!paused()) insights.reloadQuiet();
975
1022
  }, 2e4, active2);
976
1023
  usePoll(() => {
977
- if (!paused()) guard.reload();
1024
+ if (!paused()) guard.reloadQuiet();
978
1025
  }, 6e4, active2);
979
1026
  useEffect2(() => {
980
1027
  if (!detail) return;
@@ -1251,7 +1298,7 @@ function LivePanel({ active: active2 }) {
1251
1298
  const file = join4(homedir4(), ".solongate", "live-export.jsonl");
1252
1299
  try {
1253
1300
  mkdirSync(join4(homedir4(), ".solongate"), { recursive: true });
1254
- writeFileSync(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
1301
+ writeFileSync2(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
1255
1302
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
1256
1303
  } catch (err) {
1257
1304
  setActionMsg({ text: "\u2717 export failed: " + (err instanceof Error ? err.message : String(err)), level: "bad", until: Date.now() + 6e3 });
@@ -2231,12 +2278,14 @@ function AuditPanel({ active: active2, focused }) {
2231
2278
  const [si, setSi] = useState6(0);
2232
2279
  const [sessSearch, setSessSearch] = useState6("");
2233
2280
  const [sessSel, setSessSel] = useState6(0);
2281
+ const [confirm, setConfirm] = useState6(null);
2282
+ const [msg, setMsg] = useState6(null);
2234
2283
  const toTop = () => setSel(0);
2235
2284
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
2236
2285
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
2237
2286
  usePoll(() => {
2238
- statsQ.reload();
2239
- tsQ.reload();
2287
+ statsQ.reloadQuiet();
2288
+ tsQ.reloadQuiet();
2240
2289
  }, 15e3, active2 && source === "cloud");
2241
2290
  const query = {
2242
2291
  filter: DECISIONS[di],
@@ -2252,9 +2301,9 @@ function AuditPanel({ active: active2, focused }) {
2252
2301
  () => source === "cloud" ? api.audit.list(query) : Promise.resolve(null),
2253
2302
  [source, di, gi, tool, agent, search, sessFilter, page]
2254
2303
  );
2255
- usePoll(cloudQ.reload, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
2304
+ usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
2256
2305
  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);
2306
+ usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
2258
2307
  let pageRows = [];
2259
2308
  let total = 0;
2260
2309
  if (source === "cloud") {
@@ -2282,7 +2331,7 @@ function AuditPanel({ active: active2, focused }) {
2282
2331
  () => source === "cloud" ? api.agents.live({ limit: 100, includeDeactivated: true }) : Promise.resolve(null),
2283
2332
  [source]
2284
2333
  );
2285
- usePoll(agentsQ.reload, 8e3, active2 && source === "cloud" && view === "sessions");
2334
+ usePoll(agentsQ.reloadQuiet, 8e3, active2 && source === "cloud" && view === "sessions");
2286
2335
  let sessions = [];
2287
2336
  if (source === "cloud") {
2288
2337
  sessions = (agentsQ.data?.agents ?? []).map((a) => ({
@@ -2316,6 +2365,31 @@ function AuditPanel({ active: active2, focused }) {
2316
2365
  () => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
2317
2366
  [view, current?.session, source]
2318
2367
  );
2368
+ const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
2369
+ const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
2370
+ const doDelete = (kind) => {
2371
+ setMsg({ text: "deleting\u2026", level: "ok" });
2372
+ const run = async () => {
2373
+ if (source === "cloud") {
2374
+ if (kind === "one") {
2375
+ if (!current) throw new Error("nothing selected");
2376
+ await api.audit.remove([current.id]);
2377
+ } else {
2378
+ await api.audit.removeAll();
2379
+ }
2380
+ cloudQ.reload();
2381
+ statsQ.reloadQuiet();
2382
+ } else {
2383
+ const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
2384
+ if (!n) throw new Error("entry not found in the local file");
2385
+ localQ.reload();
2386
+ }
2387
+ };
2388
+ run().then(() => {
2389
+ setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
2390
+ toTop();
2391
+ }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
2392
+ };
2319
2393
  useInput5(
2320
2394
  (input, key) => {
2321
2395
  if (view === "detail") {
@@ -2326,6 +2400,11 @@ function AuditPanel({ active: active2, focused }) {
2326
2400
  else if (key.pageDown) setDetailScroll((n) => n + 10);
2327
2401
  return;
2328
2402
  }
2403
+ if (view === "logs" ? logsLoading : sessLoading) return;
2404
+ if (confirm && input !== "x" && input !== "X") {
2405
+ setConfirm(null);
2406
+ setMsg(null);
2407
+ }
2329
2408
  if (input === "s") {
2330
2409
  setSource((s) => s === "cloud" ? "local" : "cloud");
2331
2410
  setPage(0);
@@ -2342,7 +2421,7 @@ function AuditPanel({ active: active2, focused }) {
2342
2421
  else if (key.downArrow) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 1));
2343
2422
  else if (key.pageUp) setSessSel((n) => Math.max(0, n - 10));
2344
2423
  else if (key.pageDown) setSessSel((n) => Math.min(sessionsFiltered.length - 1, n + 10));
2345
- else if (key.return || key.rightArrow) {
2424
+ else if (key.return) {
2346
2425
  if (currentSess) {
2347
2426
  setSessFilter(currentSess.id);
2348
2427
  setPage(0);
@@ -2364,17 +2443,17 @@ function AuditPanel({ active: active2, focused }) {
2364
2443
  else if (key.downArrow) setSel((n) => Math.min(pageRows.length - 1, n + 1));
2365
2444
  else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
2366
2445
  else if (key.pageDown) setSel((n) => Math.min(pageRows.length - 1, n + 10));
2367
- else if (key.return || key.rightArrow) {
2446
+ else if (key.return) {
2368
2447
  if (current) {
2369
2448
  setDetailScroll(0);
2370
2449
  setView("detail");
2371
2450
  }
2372
- } else if (input === "]") {
2451
+ } else if (key.rightArrow) {
2373
2452
  if (page < pages - 1) {
2374
2453
  setPage(page + 1);
2375
2454
  toTop();
2376
2455
  }
2377
- } else if (input === "[") {
2456
+ } else if (key.leftArrow) {
2378
2457
  if (page > 0) {
2379
2458
  setPage(page - 1);
2380
2459
  toTop();
@@ -2387,6 +2466,23 @@ function AuditPanel({ active: active2, focused }) {
2387
2466
  setGi((n) => (n + 1) % SIGNALS.length);
2388
2467
  setPage(0);
2389
2468
  toTop();
2469
+ } else if (input === "x") {
2470
+ if (!current) return;
2471
+ if (confirm?.kind !== "one" || confirm.key !== current.id) {
2472
+ setConfirm({ kind: "one", key: current.id });
2473
+ setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
2474
+ return;
2475
+ }
2476
+ setConfirm(null);
2477
+ doDelete("one");
2478
+ } else if (input === "X") {
2479
+ if (confirm?.kind !== "all") {
2480
+ setConfirm({ kind: "all", key: "all" });
2481
+ setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
2482
+ return;
2483
+ }
2484
+ setConfirm(null);
2485
+ doDelete("all");
2390
2486
  } else if (input === "t") setEditing("tool");
2391
2487
  else if (input === "n") setEditing("agent");
2392
2488
  else if (input === "/") setEditing("search");
@@ -2513,8 +2609,7 @@ function AuditPanel({ active: active2, focused }) {
2513
2609
  const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
2514
2610
  const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
2515
2611
  const win = sessionsFiltered.slice(start2, start2 + listRows2);
2516
- const loading2 = source === "cloud" ? agentsQ.loading && !agentsQ.data : localQ.loading && !localQ.data;
2517
- return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2612
+ return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2518
2613
  strip,
2519
2614
  /* @__PURE__ */ jsxs6(Box6, { children: [
2520
2615
  srcChip,
@@ -2577,15 +2672,14 @@ function AuditPanel({ active: active2, focused }) {
2577
2672
  ] }) : null
2578
2673
  ] }) });
2579
2674
  }
2580
- const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
2675
+ const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
2581
2676
  const listRows = Math.max(4, rows - headerRows);
2582
2677
  const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
2583
2678
  const maxStart = Math.max(0, pageRows.length - listRows);
2584
2679
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
2585
2680
  const windowed = pageRows.slice(start, start + listRows);
2586
2681
  const reasonW = Math.min(60, Math.max(12, cols - 67));
2587
- const loading = source === "cloud" ? cloudQ.loading && !cloudQ.data : localQ.loading && !localQ.data;
2588
- return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2682
+ return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2589
2683
  strip,
2590
2684
  /* @__PURE__ */ jsxs6(Box6, { children: [
2591
2685
  srcChip,
@@ -2598,7 +2692,8 @@ function AuditPanel({ active: active2, focused }) {
2598
2692
  chip("search", search || "\xB7", !!search),
2599
2693
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
2600
2694
  ] }),
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" }),
2695
+ /* @__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 x del \xB7 X del ALL \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
2696
+ msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
2602
2697
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
2603
2698
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
2604
2699
  editing,
@@ -2658,97 +2753,11 @@ function Detail({ label, value, color }) {
2658
2753
  ] });
2659
2754
  }
2660
2755
 
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
2756
  // src/tui/panels/Settings.tsx
2748
- import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
2757
+ import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
2749
2758
  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";
2759
+ import { useState as useState7 } from "react";
2760
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2752
2761
  var EVENTS = ["denials", "allowed", "all"];
2753
2762
  function alertBodyFor(channel) {
2754
2763
  const c2 = channel.trim();
@@ -2760,12 +2769,12 @@ function alertBodyFor(channel) {
2760
2769
  function SettingsPanel({ active: active2, focused }) {
2761
2770
  void active2;
2762
2771
  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);
2772
+ const [sel, setSel] = useState7(0);
2773
+ const [editing, setEditing] = useState7(null);
2774
+ const [input, setInput] = useState7("");
2775
+ const [confirmDel, setConfirmDel] = useState7(null);
2776
+ const [msg, setMsg] = useState7(null);
2777
+ const [busy, setBusy] = useState7(false);
2769
2778
  const localQ = useLoader(() => api.settings.getLocalLogs());
2770
2779
  const whQ = useLoader(() => api.settings.getWebhooks());
2771
2780
  const alertQ = useLoader(() => api.settings.getAlerts());
@@ -2835,7 +2844,7 @@ function SettingsPanel({ active: active2, focused }) {
2835
2844
  run("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
2836
2845
  }
2837
2846
  };
2838
- useInput7(
2847
+ useInput6(
2839
2848
  (inp, key) => {
2840
2849
  setMsg(null);
2841
2850
  if (key.upArrow) {
@@ -2874,7 +2883,7 @@ function SettingsPanel({ active: active2, focused }) {
2874
2883
  const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
2875
2884
  const error = localQ.error ?? whQ.error ?? alertQ.error;
2876
2885
  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" });
2886
+ const onOff = (on) => on ? /* @__PURE__ */ jsx7(Text7, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "off" });
2878
2887
  const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
2879
2888
  const listBudget = Math.max(4, rows - 8);
2880
2889
  const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
@@ -2882,90 +2891,89 @@ function SettingsPanel({ active: active2, focused }) {
2882
2891
  const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
2883
2892
  return i >= start && i < start + listBudget;
2884
2893
  };
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: [
2894
+ return /* @__PURE__ */ jsx7(DataView, { loading, error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2895
+ /* @__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" }),
2896
+ editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
2897
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
2898
+ /* @__PURE__ */ jsx7(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
2899
+ ] }) : msg ? /* @__PURE__ */ jsx7(Text7, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : /* @__PURE__ */ jsx7(Text7, { children: " " }),
2900
+ inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2901
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
2893
2902
  "LOCAL LOGS ",
2894
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
2903
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
2895
2904
  ] }),
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) }),
2905
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2906
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
2907
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "enabled ".padEnd(10) }),
2899
2908
  onOff(!!local?.enabled),
2900
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " enter toggles" })
2909
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " enter toggles" })
2901
2910
  ] }),
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" })
2911
+ /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2912
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
2913
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "path ".padEnd(10) }),
2914
+ 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
2915
  ] })
2907
2916
  ] }) : null,
2908
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2909
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
2917
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2918
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
2910
2919
  "WEBHOOKS ",
2911
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
2920
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
2912
2921
  "\u2014 POST every matching event to your endpoint (",
2913
2922
  webhooks.length,
2914
2923
  ")"
2915
2924
  ] })
2916
2925
  ] }),
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 }) }),
2926
+ webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2927
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
2919
2928
  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) })
2929
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
2930
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(wh.url, cols - 16) })
2922
2931
  ] }, 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)" })
2932
+ inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs7(Text7, { children: [
2933
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
2934
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add webhook (enter)" })
2926
2935
  ] }) : null
2927
2936
  ] }),
2928
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2929
- /* @__PURE__ */ jsxs8(Text8, { bold: true, color: theme.accentBright, children: [
2937
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
2938
+ /* @__PURE__ */ jsxs7(Text7, { bold: true, color: theme.accentBright, children: [
2930
2939
  "ALERTS ",
2931
- /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
2940
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
2932
2941
  "\u2014 notify on a signal spike (",
2933
2942
  alerts.length,
2934
2943
  ")"
2935
2944
  ] })
2936
2945
  ] }),
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 }) }),
2946
+ alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs7(Text7, { wrap: "truncate", children: [
2947
+ /* @__PURE__ */ jsx7(Text7, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
2939
2948
  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) })
2949
+ /* @__PURE__ */ jsx7(Text7, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
2950
+ /* @__PURE__ */ jsx7(Text7, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
2951
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: truncate(chanOf(rule), cols - 26) })
2943
2952
  ] }, 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)" })
2953
+ inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs7(Text7, { children: [
2954
+ /* @__PURE__ */ jsx7(Text7, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
2955
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
2947
2956
  ] }) : null
2948
2957
  ] })
2949
2958
  ] }) });
2950
2959
  }
2951
2960
 
2952
2961
  // src/tui/App.tsx
2953
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2962
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2954
2963
  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: [
2964
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2965
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
2966
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
2967
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
2968
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
2960
2969
  "press ",
2961
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
2970
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
2962
2971
  " to go live"
2963
2972
  ] }) })
2964
2973
  ] });
2965
2974
  }
2966
2975
  var SECTIONS = [
2967
2976
  { label: "Live", Panel: LiveHint },
2968
- { label: "Agents", Panel: AgentsPanel },
2969
2977
  { label: "Policies", Panel: PoliciesPanel },
2970
2978
  { label: "Rate Limit", Panel: RateLimitPanel },
2971
2979
  { label: "DLP", Panel: DlpPanel },
@@ -2976,22 +2984,22 @@ var BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8F
2976
2984
  function Banner({ cols }) {
2977
2985
  const wide = cols >= 82;
2978
2986
  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" })
2987
+ return /* @__PURE__ */ jsxs8(Box8, { children: [
2988
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
2989
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
2982
2990
  ] });
2983
2991
  }
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" })
2992
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2993
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
2994
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
2987
2995
  ] });
2988
2996
  }
2989
2997
  function App() {
2990
2998
  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) => {
2999
+ const [section, setSection] = useState8(0);
3000
+ const [focus, setFocus] = useState8("nav");
3001
+ const [help, setHelp] = useState8(false);
3002
+ useInput7((input, key) => {
2995
3003
  if (help) {
2996
3004
  setHelp(false);
2997
3005
  return;
@@ -3012,18 +3020,18 @@ function App() {
3012
3020
  });
3013
3021
  const { cols, rows } = useTermSize();
3014
3022
  const current = SECTIONS[section];
3015
- if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
3023
+ if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
3016
3024
  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 }) });
3025
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
3018
3026
  }
3019
3027
  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" }) })
3028
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
3029
+ /* @__PURE__ */ jsx8(Banner, { cols }),
3030
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
3031
+ /* @__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)) }),
3032
+ /* @__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
3033
  ] }),
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"]] }) })
3034
+ /* @__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
3035
  ] });
3028
3036
  }
3029
3037
  var HELP = [
@@ -3031,25 +3039,25 @@ var HELP = [
3031
3039
  ["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
3040
  ["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
3041
  ["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"]]],
3042
+ ["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"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
3035
3043
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
3036
3044
  ];
3037
3045
  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 })
3046
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
3047
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
3048
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
3049
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
3050
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
3051
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
3052
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
3045
3053
  ] }, k))
3046
3054
  ] }, group)) }),
3047
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
3055
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
3048
3056
  ] });
3049
3057
  }
3050
3058
 
3051
3059
  // src/tui/index.tsx
3052
- import { jsx as jsx10 } from "react/jsx-runtime";
3060
+ import { jsx as jsx9 } from "react/jsx-runtime";
3053
3061
  async function launchTui() {
3054
3062
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
3055
3063
  process.stderr.write(
@@ -3063,7 +3071,7 @@ async function launchTui() {
3063
3071
  }
3064
3072
  process.stdout.write("\x1B[?1049h\x1B[H");
3065
3073
  try {
3066
- const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
3074
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
3067
3075
  await waitUntilExit();
3068
3076
  } finally {
3069
3077
  process.stdout.write("\x1B[?1049l");