@solongate/proxy 0.81.18 → 0.81.20

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
@@ -6750,6 +6750,25 @@ function sparkline(values, width2) {
6750
6750
  if (max === 0) return BLOCKS[0].repeat(v.length);
6751
6751
  return v.map((n) => BLOCKS[Math.min(BLOCKS.length - 1, Math.round(n / max * (BLOCKS.length - 1)))]).join("");
6752
6752
  }
6753
+ function prettyJson(s) {
6754
+ try {
6755
+ return JSON.stringify(JSON.parse(s), null, 2);
6756
+ } catch {
6757
+ return s;
6758
+ }
6759
+ }
6760
+ function wrapLines(s, width2) {
6761
+ const w = Math.max(8, width2);
6762
+ const out2 = [];
6763
+ for (const raw of (s ?? "").split("\n")) {
6764
+ if (raw.length <= w) {
6765
+ out2.push(raw);
6766
+ continue;
6767
+ }
6768
+ for (let i = 0; i < raw.length; i += w) out2.push(raw.slice(i, i + w));
6769
+ }
6770
+ return out2;
6771
+ }
6753
6772
  function truncate2(s, n) {
6754
6773
  if (!s) return "";
6755
6774
  return s.length <= n ? s : s.slice(0, Math.max(0, n - 1)) + "\u2026";
@@ -6903,12 +6922,16 @@ __export(settings_exports, {
6903
6922
  deleteWebhook: () => deleteWebhook,
6904
6923
  getAlerts: () => getAlerts,
6905
6924
  getGuardStatus: () => getGuardStatus,
6925
+ getLocalLogs: () => getLocalLogs,
6906
6926
  getRateLimitHistory: () => getRateLimitHistory,
6907
6927
  getSecurityLayers: () => getSecurityLayers,
6908
6928
  getSelfProtection: () => getSelfProtection,
6909
6929
  getWebhooks: () => getWebhooks,
6930
+ setAlertEnabled: () => setAlertEnabled,
6931
+ setLocalLogs: () => setLocalLogs,
6910
6932
  setSecurityLayers: () => setSecurityLayers,
6911
- setSelfProtection: () => setSelfProtection
6933
+ setSelfProtection: () => setSelfProtection,
6934
+ updateWebhook: () => updateWebhook
6912
6935
  });
6913
6936
  function getSecurityLayers() {
6914
6937
  return request("GET", "/settings/security-layers");
@@ -6931,6 +6954,12 @@ function getSelfProtection() {
6931
6954
  function setSelfProtection(enabled) {
6932
6955
  return request("PUT", "/settings/self-protection", { body: { enabled } });
6933
6956
  }
6957
+ function getLocalLogs() {
6958
+ return request("GET", "/settings/local-logs");
6959
+ }
6960
+ function setLocalLogs(cfg) {
6961
+ return request("PUT", "/settings/local-logs", { body: cfg });
6962
+ }
6934
6963
  function getAlerts() {
6935
6964
  return request("GET", "/settings/denial-alerts");
6936
6965
  }
@@ -6940,6 +6969,9 @@ function createAlert(body) {
6940
6969
  function deleteAlert(id) {
6941
6970
  return request("DELETE", "/settings/denial-alerts", { query: { id } });
6942
6971
  }
6972
+ function setAlertEnabled(id, enabled) {
6973
+ return request("PATCH", "/settings/denial-alerts", { query: { id }, body: { enabled } });
6974
+ }
6943
6975
  function getWebhooks() {
6944
6976
  return request("GET", "/settings/denial-webhook");
6945
6977
  }
@@ -6949,6 +6981,9 @@ function createWebhook(body) {
6949
6981
  function deleteWebhook(id) {
6950
6982
  return request("DELETE", "/settings/denial-webhook", { query: { id } });
6951
6983
  }
6984
+ function updateWebhook(id, patch) {
6985
+ return request("PATCH", "/settings/denial-webhook", { body: { id, ...patch } });
6986
+ }
6952
6987
  var init_settings = __esm({
6953
6988
  "src/api-client/settings.ts"() {
6954
6989
  "use strict";
@@ -7090,145 +7125,34 @@ var init_api_client = __esm({
7090
7125
  });
7091
7126
 
7092
7127
  // src/tui/notify.ts
7093
- import { spawn, execFileSync as execFileSync2 } from "child_process";
7094
- import { writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
7095
- import { join as join6 } from "path";
7096
- import { tmpdir as tmpdir2 } from "os";
7097
- function captureFocusTarget() {
7128
+ import { spawn } from "child_process";
7129
+ function desktopNotify(title, msg) {
7098
7130
  try {
7099
7131
  if (process.platform === "linux") {
7100
- focusToken = (process.env.WINDOWID || "").trim() || null;
7101
- if (!focusToken) {
7102
- try {
7103
- focusToken = execFileSync2("xdotool", ["getactivewindow"], { encoding: "utf-8", timeout: 1500, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
7104
- } catch {
7105
- focusToken = null;
7106
- }
7107
- }
7132
+ const p = spawn("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
7133
+ p.on("error", () => {
7134
+ });
7135
+ p.unref();
7108
7136
  } else if (process.platform === "win32") {
7109
- const ps = `Add-Type -Name W -Namespace N -MemberDefinition '[DllImport("user32.dll")]public static extern System.IntPtr GetForegroundWindow();' | Out-Null; [N.W]::GetForegroundWindow().ToInt64()`;
7110
- try {
7111
- focusToken = execFileSync2("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { encoding: "utf-8", timeout: 4e3, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
7112
- } catch {
7113
- focusToken = null;
7114
- }
7137
+ const q = (s) => s.replace(/'/g, "''");
7138
+ const ps = `Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$n=New-Object System.Windows.Forms.NotifyIcon;$n.Icon=[System.Drawing.SystemIcons]::Information;$n.Visible=$true;$n.ShowBalloonTip(6000,'${q(title)}','${q(msg)}',[System.Windows.Forms.ToolTipIcon]::Warning);Start-Sleep -Milliseconds 6500;$n.Dispose()`;
7139
+ const p = spawn("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "ignore", detached: true, windowsHide: true });
7140
+ p.on("error", () => {
7141
+ });
7142
+ p.unref();
7143
+ } else if (process.platform === "darwin") {
7144
+ const esc = (s) => s.replace(/"/g, '\\"');
7145
+ const p = spawn("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
7146
+ p.on("error", () => {
7147
+ });
7148
+ p.unref();
7115
7149
  }
7116
- } catch {
7117
- focusToken = null;
7118
- }
7119
- }
7120
- function desktopNotify(title, msg) {
7121
- try {
7122
- if (process.platform === "linux") linuxNotify(title, msg);
7123
- else if (process.platform === "win32") winNotify(title, msg);
7124
- else if (process.platform === "darwin") macNotify(title, msg);
7125
- } catch {
7126
- }
7127
- }
7128
- function linuxNotify(title, msg) {
7129
- let p;
7130
- try {
7131
- p = spawn("notify-send", ["--wait", "--action=default=Open", "-a", "SolonGate", title, msg], { stdio: ["ignore", "pipe", "ignore"] });
7132
- } catch {
7133
- return;
7134
- }
7135
- p.on("error", () => {
7136
- });
7137
- p.stdout?.on("data", (d) => {
7138
- if (String(d).trim() === "default") raiseLinuxWindow();
7139
- });
7140
- p.unref();
7141
- }
7142
- function raiseLinuxWindow() {
7143
- try {
7144
- process.stdout.write("\x1B[5t");
7145
- } catch {
7146
- }
7147
- if (!focusToken) return;
7148
- try {
7149
- const x = spawn("xdotool", ["windowactivate", focusToken], { stdio: "ignore" });
7150
- x.on("error", () => {
7151
- try {
7152
- const hex = "0x" + Number(focusToken).toString(16);
7153
- const w = spawn("wmctrl", ["-ia", hex], { stdio: "ignore" });
7154
- w.on("error", () => {
7155
- });
7156
- w.unref();
7157
- } catch {
7158
- }
7159
- });
7160
- x.unref();
7161
- } catch {
7162
- }
7163
- }
7164
- function ensureWinScript() {
7165
- if (winScriptPath && existsSync4(winScriptPath)) return winScriptPath;
7166
- const p = join6(tmpdir2(), "solongate-toast.ps1");
7167
- const script = [
7168
- "param([string]$Title,[string]$Msg,[string]$Hwnd)",
7169
- "Add-Type -AssemblyName System.Windows.Forms",
7170
- "Add-Type -AssemblyName System.Drawing",
7171
- `Add-Type -Name Win -Namespace Sg -MemberDefinition '[DllImport("user32.dll")]public static extern bool SetForegroundWindow(System.IntPtr h);[DllImport("user32.dll")]public static extern bool ShowWindow(System.IntPtr h,int c);'`,
7172
- "$ni=New-Object System.Windows.Forms.NotifyIcon",
7173
- "$ni.Icon=[System.Drawing.SystemIcons]::Information",
7174
- "$ni.Visible=$true",
7175
- "$script:clicked=$false",
7176
- "$ni.add_BalloonTipClicked({$script:clicked=$true})",
7177
- "$ni.ShowBalloonTip(8000,$Title,$Msg,[System.Windows.Forms.ToolTipIcon]::Warning)",
7178
- "$sw=[System.Diagnostics.Stopwatch]::StartNew()",
7179
- "while($sw.Elapsed.TotalSeconds -lt 9 -and -not $script:clicked){[System.Windows.Forms.Application]::DoEvents();Start-Sleep -Milliseconds 100}",
7180
- "if($script:clicked -and $Hwnd){try{$h=[System.IntPtr][int64]$Hwnd;[Sg.Win]::ShowWindow($h,9)|Out-Null;[Sg.Win]::SetForegroundWindow($h)|Out-Null}catch{}}",
7181
- "$ni.Visible=$false;$ni.Dispose()"
7182
- ].join("\n");
7183
- try {
7184
- writeFileSync3(p, script);
7185
- winScriptPath = p;
7186
- return p;
7187
- } catch {
7188
- return null;
7189
- }
7190
- }
7191
- function winNotify(title, msg) {
7192
- const script = ensureWinScript();
7193
- if (!script) return;
7194
- try {
7195
- const p = spawn("powershell", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", script, "-Title", title, "-Msg", msg, "-Hwnd", focusToken || ""], { stdio: "ignore", detached: true, windowsHide: true });
7196
- p.on("error", () => {
7197
- });
7198
- p.unref();
7199
7150
  } catch {
7200
7151
  }
7201
7152
  }
7202
- function macBundleId() {
7203
- const prog = (process.env.TERM_PROGRAM || "").toLowerCase();
7204
- if (prog.includes("iterm")) return "com.googlecode.iterm2";
7205
- if (prog.includes("apple_terminal")) return "com.apple.Terminal";
7206
- if (prog.includes("vscode")) return "com.microsoft.VSCode";
7207
- return "com.apple.Terminal";
7208
- }
7209
- function macNotify(title, msg) {
7210
- try {
7211
- const p = spawn("terminal-notifier", ["-title", title, "-message", msg, "-activate", macBundleId(), "-sender", macBundleId()], { stdio: "ignore", detached: true });
7212
- p.on("error", () => {
7213
- try {
7214
- const esc = (s) => s.replace(/"/g, '\\"');
7215
- const a = spawn("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
7216
- a.on("error", () => {
7217
- });
7218
- a.unref();
7219
- } catch {
7220
- }
7221
- });
7222
- p.unref();
7223
- } catch {
7224
- }
7225
- }
7226
- var focusToken, winScriptPath;
7227
7153
  var init_notify = __esm({
7228
7154
  "src/tui/notify.ts"() {
7229
7155
  "use strict";
7230
- focusToken = null;
7231
- winScriptPath = null;
7232
7156
  }
7233
7157
  });
7234
7158
 
@@ -7271,6 +7195,24 @@ function usePoll(reload, intervalMs, enabled = true) {
7271
7195
  return () => clearInterval(t);
7272
7196
  }, [reload, intervalMs, enabled]);
7273
7197
  }
7198
+ function useTermSize() {
7199
+ const read = () => ({ cols: process.stdout.columns ?? 100, rows: process.stdout.rows ?? 30 });
7200
+ const [size, setSize] = useState(read);
7201
+ useEffect(() => {
7202
+ const onResize = () => setSize(read());
7203
+ process.stdout.on("resize", onResize);
7204
+ return () => {
7205
+ process.stdout.off("resize", onResize);
7206
+ };
7207
+ }, []);
7208
+ return size;
7209
+ }
7210
+ function usePanelSize() {
7211
+ const { cols, rows } = useTermSize();
7212
+ const wide = cols >= 82;
7213
+ const shellRows = wide ? 12 : 7;
7214
+ return { cols: Math.max(30, cols - 22), rows: Math.max(8, rows - shellRows) };
7215
+ }
7274
7216
  var init_hooks = __esm({
7275
7217
  "src/tui/hooks.ts"() {
7276
7218
  "use strict";
@@ -7280,9 +7222,9 @@ var init_hooks = __esm({
7280
7222
  // src/tui/panels/Live.tsx
7281
7223
  import { Box as Box2, Text as Text2, useInput } from "ink";
7282
7224
  import TextInput from "ink-text-input";
7283
- import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync4 } from "fs";
7225
+ import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
7284
7226
  import { homedir as homedir4 } from "os";
7285
- import { join as join7 } from "path";
7227
+ import { join as join6 } from "path";
7286
7228
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7287
7229
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7288
7230
  function tailLines(file, maxBytes = 131072) {
@@ -7420,6 +7362,9 @@ function LivePanel({ active: active2 }) {
7420
7362
  const [detail, setDetail] = useState2(null);
7421
7363
  const [detailCloud, setDetailCloud] = useState2([]);
7422
7364
  const [detailScroll, setDetailScroll] = useState2(0);
7365
+ const [inspect, setInspect] = useState2(null);
7366
+ const [inspectScroll, setInspectScroll] = useState2(0);
7367
+ const inspectFromRef = useRef2("stream");
7423
7368
  const seenRef = useRef2(/* @__PURE__ */ new Set());
7424
7369
  const lastLocalTs = useRef2(0);
7425
7370
  const pausedUntil = useRef2(0);
@@ -7625,8 +7570,7 @@ function LivePanel({ active: active2 }) {
7625
7570
  else if (e.burst) fireAlert("sec:" + e.id, "Rate-limit burst", `BURST ${e.tool}${who}`, "warn");
7626
7571
  }
7627
7572
  }, [tick, active2, fireAlert]);
7628
- const cols = process.stdout.columns ?? 100;
7629
- const rows = process.stdout.rows ?? 30;
7573
+ const { cols, rows } = useTermSize();
7630
7574
  const ins = insights.data ?? {};
7631
7575
  const spin = SPIN[tick % SPIN.length];
7632
7576
  const nowMs = Date.now();
@@ -7772,8 +7716,18 @@ function LivePanel({ active: active2 }) {
7772
7716
  setEditingSearch(true);
7773
7717
  return;
7774
7718
  }
7719
+ if (mode === "inspect") {
7720
+ if (key.leftArrow) {
7721
+ setMode(inspectFromRef.current);
7722
+ setInspect(null);
7723
+ } else if (key.upArrow) setInspectScroll((n) => Math.max(0, n - 1));
7724
+ else if (key.downArrow) setInspectScroll((n) => n + 1);
7725
+ else if (key.pageUp) setInspectScroll((n) => Math.max(0, n - 10));
7726
+ else if (key.pageDown) setInspectScroll((n) => n + 10);
7727
+ return;
7728
+ }
7775
7729
  if (mode === "detail") {
7776
- const maxD = Math.max(0, detailEntries.length - (rows - 10));
7730
+ const maxD = Math.max(0, detailEntries.length - 1);
7777
7731
  if (key.leftArrow) {
7778
7732
  setMode("stream");
7779
7733
  setDetail(null);
@@ -7781,6 +7735,15 @@ function LivePanel({ active: active2 }) {
7781
7735
  else if (key.downArrow) setDetailScroll((n) => Math.min(maxD, n + 1));
7782
7736
  else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
7783
7737
  else if (key.pageDown) setDetailScroll((n) => Math.min(maxD, n + 10));
7738
+ else if (key.return) {
7739
+ const e = detailEntries[Math.min(detailScroll, maxD)];
7740
+ if (e) {
7741
+ inspectFromRef.current = "detail";
7742
+ setInspect(e);
7743
+ setInspectScroll(0);
7744
+ setMode("inspect");
7745
+ }
7746
+ }
7784
7747
  return;
7785
7748
  }
7786
7749
  if (mode === "pick") {
@@ -7802,6 +7765,13 @@ function LivePanel({ active: active2 }) {
7802
7765
  if (input === "f") {
7803
7766
  setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
7804
7767
  setSel(0);
7768
+ } else if (key.return) {
7769
+ if (selEntry) {
7770
+ inspectFromRef.current = "stream";
7771
+ setInspect(selEntry);
7772
+ setInspectScroll(0);
7773
+ setMode("inspect");
7774
+ }
7805
7775
  } else if (input === "s") {
7806
7776
  setPickIdx(0);
7807
7777
  setMode("pick");
@@ -7811,10 +7781,10 @@ function LivePanel({ active: active2 }) {
7811
7781
  else if (input === "x") toggleSignal("dlp");
7812
7782
  else if (input === "r") toggleSignal("ratelimit");
7813
7783
  else if (input === "e") {
7814
- const file = join7(homedir4(), ".solongate", "live-export.jsonl");
7784
+ const file = join6(homedir4(), ".solongate", "live-export.jsonl");
7815
7785
  try {
7816
- mkdirSync3(join7(homedir4(), ".solongate"), { recursive: true });
7817
- writeFileSync4(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7786
+ mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
7787
+ writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7818
7788
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7819
7789
  } catch (err2) {
7820
7790
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -7864,6 +7834,52 @@ function LivePanel({ active: active2 }) {
7864
7834
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: ` ${spin} up ${fmtUp(nowMs - startRef.current)} \xB7 ${hhmmss(nowMs)} \xB7 api ${latNow}ms ` }),
7865
7835
  frozen ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#123d1f", color: "#7bd88f", bold: true, children: " \u23F5 COPY MODE \u2014 screen frozen, select & copy freely \xB7 space resume " }) : backingOff ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: " RATE LIMITED \xB7 backing off " }) : lastDeny ? /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#3d1220", color: "#ff6b6b", bold: true, children: ` \u26A0 ${hhmmss(lastDeny.at)} ${lastDeny.tool} DENIED ` }) : /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#4f6db8", children: " \u2713 clean " })
7866
7836
  ] });
7837
+ if (mode === "inspect" && inspect) {
7838
+ const e = inspect;
7839
+ const bodyW = Math.max(20, cols - 4);
7840
+ const lines = wrapLines(prettyJson(e.detail || "(no arguments / reason recorded)"), bodyW);
7841
+ const bodyRows = Math.max(4, rows - 7);
7842
+ const maxScroll2 = Math.max(0, lines.length - bodyRows);
7843
+ const off = Math.min(inspectScroll, maxScroll2);
7844
+ const win = lines.slice(off, off + bodyRows);
7845
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
7846
+ titleBar,
7847
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7848
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#AAC8FA", bold: true, children: " ENTRY " }),
7849
+ /* @__PURE__ */ jsx2(Text2, { color: decisionColor(e.decision), bold: true, children: " " + e.decision }),
7850
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, bold: true, children: " " + e.tool }),
7851
+ /* @__PURE__ */ jsx2(Text2, { color: isLoc(e) ? theme.ok : "#4f6db8", children: " " + (isLoc(e) ? "LOC" : "CLD") }),
7852
+ e.dlp ? /* @__PURE__ */ jsx2(Text2, { color: theme.bad, children: " DLP!" }) : null,
7853
+ e.burst ? /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: " BURST" }) : null,
7854
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
7855
+ ] }),
7856
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7857
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 when " }),
7858
+ /* @__PURE__ */ jsx2(Text2, { children: new Date(e.at).toLocaleString() }),
7859
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 perm " }),
7860
+ /* @__PURE__ */ jsx2(Text2, { children: e.permission || "\u2014" }),
7861
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 eval " }),
7862
+ /* @__PURE__ */ jsx2(Text2, { color: e.evalMs != null && e.evalMs > 500 ? theme.warn : void 0, children: e.evalMs != null ? `${e.evalMs}ms` : "\u2014" }),
7863
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 agent " }),
7864
+ /* @__PURE__ */ jsx2(Text2, { children: e.agent ?? "\u2014" }),
7865
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7866
+ ] }),
7867
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7868
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "\u2502 session " }),
7869
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: e.session ?? "\u2014" }),
7870
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502 rule " }),
7871
+ /* @__PURE__ */ jsx2(Text2, { color: e.rule && e.decision !== "ALLOW" ? theme.bad : theme.dim, children: e.rule ?? "\u2014" }),
7872
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7873
+ ] }),
7874
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "FULL CONTENT", extra: `${lines.length} lines${maxScroll2 ? ` \xB7 \u25BC${maxScroll2 - off} more \xB7 \u2191\u2193 scroll` : ""} \xB7 space copy \xB7 \u2190 back`, width: innerW }),
7875
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win.map((l, i) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", children: l || " " }, off + i)) }),
7876
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7877
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " ENTRY " }),
7878
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${e.id} ` }),
7879
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 space copy mode \xB7 \u2190 back \xB7 esc menu " })
7880
+ ] })
7881
+ ] });
7882
+ }
7867
7883
  if (mode === "detail" && detail) {
7868
7884
  const d = detailEntries;
7869
7885
  const allow = d.filter((e) => e.decision === "ALLOW").length;
@@ -7883,9 +7899,10 @@ function LivePanel({ active: active2 }) {
7883
7899
  const last = d[0]?.at;
7884
7900
  const tlRows = Math.max(4, rows - 10);
7885
7901
  const tlBody = Math.max(3, tlRows - 1);
7886
- const maxD = Math.max(0, d.length - tlBody);
7887
- const dScroll = Math.min(detailScroll, maxD);
7888
- const tl = d.slice(dScroll, dScroll + tlBody);
7902
+ const dSel = Math.min(detailScroll, Math.max(0, d.length - 1));
7903
+ const maxStart = Math.max(0, d.length - tlBody);
7904
+ const start = Math.min(Math.max(0, dSel - Math.floor((tlBody - 1) / 2)), maxStart);
7905
+ const tl = d.slice(start, start + tlBody);
7889
7906
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
7890
7907
  titleBar,
7891
7908
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
@@ -7923,16 +7940,16 @@ function LivePanel({ active: active2 }) {
7923
7940
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: perms.map(([p, c2]) => `${p}\xD7${c2}`).join(" ") || "\u2014" }),
7924
7941
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7925
7942
  ] }),
7926
- /* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${d.length} calls \xB7 newest first${dScroll ? ` \xB7 \u25BC${dScroll} older` : ""} \xB7 \u2191\u2193 scroll \xB7 / search \xB7 \u2190 back`, width: innerW }),
7943
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "TIMELINE", extra: `${dSel + 1}/${d.length} \xB7 newest first \xB7 \u2191\u2193 select \xB7 enter full entry \xB7 / search \xB7 \u2190 back`, width: innerW }),
7927
7944
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: tlRows, overflow: "hidden", children: [
7928
7945
  searchRow,
7929
7946
  tl.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: detailCloud.length === 0 ? spin + " loading history\u2026" : "no entries" }) : null,
7930
- tl.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
7947
+ tl.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: start + i === dSel }, e.id))
7931
7948
  ] }),
7932
7949
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7933
7950
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " SESSION " }),
7934
7951
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` ${detail.id} ` }),
7935
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 scroll \xB7 \u2190 back \xB7 esc menu " })
7952
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 enter full entry \xB7 \u2190 back \xB7 esc menu " })
7936
7953
  ] })
7937
7954
  ] });
7938
7955
  }
@@ -8045,7 +8062,7 @@ function LivePanel({ active: active2 }) {
8045
8062
  PaneTitle,
8046
8063
  {
8047
8064
  label: "TOOL STREAM",
8048
- extra: `${selClamped + 1}/${visibleDesc.length}${signal !== "none" ? ` \xB7 ${signal}` : ""}${q ? " \xB7 search" : ""} \xB7 \u2191\u2193 select \xB7 w allow \xB7 b block \xB7 d/x/r ${filter} \xB7 s`,
8065
+ extra: `${selClamped + 1}/${visibleDesc.length}${signal !== "none" ? ` \xB7 ${signal}` : ""}${q ? " \xB7 search" : ""} \xB7 \u2191\u2193 select \xB7 enter full \xB7 w allow \xB7 b block \xB7 d/x/r ${filter} \xB7 s`,
8049
8066
  width: innerW
8050
8067
  }
8051
8068
  ),
@@ -8053,7 +8070,7 @@ function LivePanel({ active: active2 }) {
8053
8070
  searchRow,
8054
8071
  localOn === false ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8055
8072
  /* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: "local logs off" }),
8056
- /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable: dashboard \u2192 Settings \u2192 Local logs \xB7 hooks write ~/.solongate/local-logs \xB7 view: `solongate logs-server`" })
8073
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2014 enable: dataroom \u2192 Settings (local logs) or dashboard \u2192 Settings \xB7 hooks write ~/.solongate/local-logs" })
8057
8074
  ] }) : null,
8058
8075
  windowed.length === 0 ? /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
8059
8076
  spin,
@@ -8064,7 +8081,7 @@ function LivePanel({ active: active2 }) {
8064
8081
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8065
8082
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
8066
8083
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
8067
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 w allow \xB7 b block \xB7 d/x/r \xB7 / search \xB7 f \xB7 s \xB7 space copy \xB7 esc \xB7 q " })
8084
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 enter full \xB7 w allow \xB7 b block \xB7 d/x/r \xB7 / search \xB7 f \xB7 s \xB7 space copy \xB7 esc \xB7 q " })
8068
8085
  ] })
8069
8086
  ] });
8070
8087
  }
@@ -8081,8 +8098,8 @@ var init_Live = __esm({
8081
8098
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8082
8099
  BG = "#12234f";
8083
8100
  DIM_FLOOR = "#233457";
8084
- LOCAL_LOG = join7(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
8085
- RING = join7(process.cwd(), ".solongate", ".eval-ring.jsonl");
8101
+ LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
8102
+ RING = join6(process.cwd(), ".solongate", ".eval-ring.jsonl");
8086
8103
  hhmmss = (ts) => {
8087
8104
  const d = new Date(ts);
8088
8105
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -8893,8 +8910,9 @@ var init_Stats = __esm({
8893
8910
  import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
8894
8911
  import TextInput4 from "ink-text-input";
8895
8912
  import { useState as useState6 } from "react";
8896
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
8913
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
8897
8914
  function AuditPanel({ active: active2, focused }) {
8915
+ const { cols, rows } = usePanelSize();
8898
8916
  const [di, setDi] = useState6(0);
8899
8917
  const [gi, setGi] = useState6(0);
8900
8918
  const [tool, setTool] = useState6("");
@@ -8902,6 +8920,7 @@ function AuditPanel({ active: active2, focused }) {
8902
8920
  const [search, setSearch] = useState6("");
8903
8921
  const [sel, setSel] = useState6(0);
8904
8922
  const [view, setView] = useState6("list");
8923
+ const [detailScroll, setDetailScroll] = useState6(0);
8905
8924
  const [editing, setEditing] = useState6(null);
8906
8925
  const query = {
8907
8926
  filter: DECISIONS[di],
@@ -8909,7 +8928,7 @@ function AuditPanel({ active: active2, focused }) {
8909
8928
  tool: tool || void 0,
8910
8929
  agent_name: agent || void 0,
8911
8930
  search: search || void 0,
8912
- limit: 100
8931
+ limit: FETCH_LIMIT
8913
8932
  };
8914
8933
  const auditQ = useLoader(() => api.audit.list(query), [di, gi, tool, agent, search]);
8915
8934
  usePoll(auditQ.reload, 6e3, active2 && view === "list" && !editing);
@@ -8923,15 +8942,29 @@ function AuditPanel({ active: active2, focused }) {
8923
8942
  (input, key) => {
8924
8943
  if (view === "detail") {
8925
8944
  if (key.leftArrow || key.escape) setView("list");
8945
+ else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
8946
+ else if (key.downArrow) setDetailScroll((n) => n + 1);
8947
+ else if (key.pageUp) setDetailScroll((n) => Math.max(0, n - 10));
8948
+ else if (key.pageDown) setDetailScroll((n) => n + 10);
8926
8949
  return;
8927
8950
  }
8951
+ const clearSel = () => setSel(0);
8928
8952
  if (key.upArrow) setSel((n) => Math.max(0, n - 1));
8929
8953
  else if (key.downArrow) setSel((n) => Math.min(entries.length - 1, n + 1));
8954
+ else if (key.pageUp) setSel((n) => Math.max(0, n - 10));
8955
+ else if (key.pageDown) setSel((n) => Math.min(entries.length - 1, n + 10));
8930
8956
  else if (key.return || key.rightArrow) {
8931
- if (current) setView("detail");
8932
- } else if (input === "f") setDi((n) => (n + 1) % DECISIONS.length);
8933
- else if (input === "g") setGi((n) => (n + 1) % SIGNALS.length);
8934
- else if (input === "t") setEditing("tool");
8957
+ if (current) {
8958
+ setDetailScroll(0);
8959
+ setView("detail");
8960
+ }
8961
+ } else if (input === "f") {
8962
+ setDi((n) => (n + 1) % DECISIONS.length);
8963
+ clearSel();
8964
+ } else if (input === "g") {
8965
+ setGi((n) => (n + 1) % SIGNALS.length);
8966
+ clearSel();
8967
+ } else if (input === "t") setEditing("tool");
8935
8968
  else if (input === "n") setEditing("agent");
8936
8969
  else if (input === "/") setEditing("search");
8937
8970
  else if (input === "c") {
@@ -8940,49 +8973,64 @@ function AuditPanel({ active: active2, focused }) {
8940
8973
  setTool("");
8941
8974
  setAgent("");
8942
8975
  setSearch("");
8976
+ clearSel();
8943
8977
  }
8944
8978
  },
8945
8979
  { isActive: focused && !editing }
8946
8980
  );
8947
8981
  if (view === "detail" && current) {
8948
8982
  const sessionCalls = sessionQ.data?.entries ?? [];
8983
+ const bodyW = Math.max(20, cols - 2);
8984
+ const reasonLines = wrapLines("reason: " + (current.reason ?? "\u2014"), bodyW);
8985
+ const argLines = current.arguments_summary ? wrapLines(prettyJson(JSON.stringify(current.arguments_summary)), bodyW) : ["(no arguments recorded)"];
8986
+ const sessionRows = current.session_id ? Math.min(4, sessionCalls.length) + 2 : 1;
8987
+ const reasonShown = reasonLines.slice(0, 4);
8988
+ const argBudget = Math.max(3, rows - 2 - 5 - reasonShown.length - 1 - sessionRows - 1);
8989
+ const maxScroll = Math.max(0, argLines.length - argBudget);
8990
+ const off = Math.min(detailScroll, maxScroll);
8991
+ const argWin = argLines.slice(off, off + argBudget);
8949
8992
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
8950
8993
  /* @__PURE__ */ jsxs7(Box7, { children: [
8951
8994
  /* @__PURE__ */ jsx7(Text7, { color: decisionColor(current.decision), bold: true, children: current.decision }),
8952
8995
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: " " + current.tool_name }),
8953
8996
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: " " + current.permission + " \xB7 " + current.trust_level })
8954
8997
  ] }),
8955
- /* @__PURE__ */ jsx7(Detail, { label: "reason", value: current.reason ?? "\u2014" }),
8998
+ reasonShown.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: i === 0 ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
8999
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "reason " }),
9000
+ /* @__PURE__ */ jsx7(Text7, { children: l.slice("reason: ".length) })
9001
+ ] }) : /* @__PURE__ */ jsx7(Text7, { children: " " + l }) }, "r" + i)),
8956
9002
  /* @__PURE__ */ jsx7(Detail, { label: "rule", value: current.matched_rule_id ?? "\u2014" }),
8957
9003
  /* @__PURE__ */ jsx7(Detail, { label: "agent", value: current.agent_name ?? "\u2014" }),
8958
9004
  /* @__PURE__ */ jsx7(Detail, { label: "session", value: current.session_id ?? "\u2014" }),
8959
9005
  /* @__PURE__ */ jsx7(Detail, { label: "dlp", value: current.dlp_matches?.length ? current.dlp_matches.join(", ") : "none", color: current.dlp_matches?.length ? theme.bad : void 0 }),
8960
- /* @__PURE__ */ jsx7(Detail, { label: "when", value: new Date(current.created_at).toLocaleString() }),
8961
- current.arguments_summary ? /* @__PURE__ */ jsx7(Detail, { label: "args", value: truncate2(JSON.stringify(current.arguments_summary), 60) }) : null,
8962
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
9006
+ /* @__PURE__ */ jsx7(Detail, { label: "when", value: new Date(current.created_at).toLocaleString() + (current.evaluation_time_ms != null ? ` \xB7 eval ${current.evaluation_time_ms}ms` : "") }),
9007
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: `arguments \u2014 full \xB7 ${argLines.length} lines${maxScroll ? ` \xB7 \u25BC${maxScroll - off} more \xB7 \u2191\u2193 scroll` : ""}` }),
9008
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: argBudget, overflow: "hidden", children: argWin.map((l, i) => /* @__PURE__ */ jsx7(Text7, { wrap: "truncate", children: l || " " }, off + i)) }),
9009
+ current.session_id ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
8963
9010
  /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
8964
- "Session ",
8965
- current.session_id ? `\xB7 ${sessionCalls.length} calls` : "(no session id)"
9011
+ "Session \xB7 ",
9012
+ sessionCalls.length,
9013
+ " calls"
8966
9014
  ] }),
8967
- current.session_id ? /* @__PURE__ */ jsx7(
9015
+ /* @__PURE__ */ jsx7(
8968
9016
  Table,
8969
9017
  {
8970
9018
  columns: [
8971
9019
  { header: "DECISION", width: 9 },
8972
9020
  { header: "TOOL", width: 20 },
8973
- { header: "REASON", width: 26 },
9021
+ { header: "REASON", width: Math.max(16, cols - 48) },
8974
9022
  { header: "WHEN", width: 6 }
8975
9023
  ],
8976
- rows: sessionCalls.slice(0, 12).map((e) => [
9024
+ rows: sessionCalls.slice(0, Math.max(1, sessionRows - 2)).map((e) => [
8977
9025
  { value: e.decision, color: decisionColor(e.decision) },
8978
9026
  { value: truncate2(e.tool_name, 20), color: theme.accent },
8979
- { value: truncate2(e.reason ?? "\u2014", 26) },
9027
+ { value: truncate2(e.reason ?? "\u2014", Math.max(16, cols - 48)) },
8980
9028
  { value: ago(e.created_at), dim: true }
8981
9029
  ])
8982
9030
  }
8983
- ) : null
8984
- ] }),
8985
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2190 back to list" })
9031
+ )
9032
+ ] }) : /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "(no session id)" }),
9033
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "\u2191\u2193 scroll arguments \xB7 \u2190/esc back to list" })
8986
9034
  ] });
8987
9035
  }
8988
9036
  const chip = (label, val, on) => /* @__PURE__ */ jsxs7(Text7, { children: [
@@ -8993,6 +9041,13 @@ function AuditPanel({ active: active2, focused }) {
8993
9041
  /* @__PURE__ */ jsx7(Text7, { color: on ? theme.accentBright : theme.dim, children: val }),
8994
9042
  /* @__PURE__ */ jsx7(Text7, { children: " " })
8995
9043
  ] });
9044
+ const headerRows = 4 + (editing ? 1 : 0);
9045
+ const listRows = Math.max(4, rows - headerRows);
9046
+ const selClamped = Math.min(sel, Math.max(0, entries.length - 1));
9047
+ const maxStart = Math.max(0, entries.length - listRows);
9048
+ const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
9049
+ const windowed = entries.slice(start, start + listRows);
9050
+ const reasonW = Math.min(60, Math.max(12, cols - 67));
8996
9051
  return /* @__PURE__ */ jsx7(DataView, { loading: auditQ.loading && !auditQ.data, error: auditQ.error, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
8997
9052
  /* @__PURE__ */ jsxs7(Box7, { children: [
8998
9053
  chip("dec", DECISIONS[di] ?? "all", di !== 0),
@@ -9001,8 +9056,8 @@ function AuditPanel({ active: active2, focused }) {
9001
9056
  chip("agent", agent || "\xB7", !!agent),
9002
9057
  chip("search", search || "\xB7", !!search)
9003
9058
  ] }),
9004
- /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: focused ? "\u2191\u2193 \xB7 enter detail \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 c clear" : "press \u2192 to browse" }),
9005
- editing ? /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
9059
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 c clear" : "press \u2192 to browse" }),
9060
+ editing ? /* @__PURE__ */ jsxs7(Box7, { children: [
9006
9061
  /* @__PURE__ */ jsxs7(Text7, { color: theme.warn, children: [
9007
9062
  editing,
9008
9063
  ": "
@@ -9011,16 +9066,15 @@ function AuditPanel({ active: active2, focused }) {
9011
9066
  TextInput4,
9012
9067
  {
9013
9068
  value: editing === "tool" ? tool : editing === "agent" ? agent : search,
9014
- onChange: editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch,
9069
+ onChange: (v) => {
9070
+ (editing === "tool" ? setTool : editing === "agent" ? setAgent : setSearch)(v);
9071
+ setSel(0);
9072
+ },
9015
9073
  onSubmit: () => setEditing(null)
9016
9074
  }
9017
9075
  )
9018
9076
  ] }) : null,
9019
- /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.dim, children: [
9020
- auditQ.data?.total ?? 0,
9021
- " matched \xB7 showing ",
9022
- Math.min(entries.length, 12)
9023
- ] }) }),
9077
+ /* @__PURE__ */ jsx7(Text7, { color: theme.dim, wrap: "truncate", children: `${auditQ.data?.total ?? 0} matched \xB7 ${entries.length} loaded \xB7 ${selClamped + 1}/${entries.length}${start ? ` \xB7 \u25B2${start} newer` : ""}${start + listRows < entries.length ? ` \xB7 \u25BC${entries.length - start - listRows} older` : ""}` }),
9024
9078
  /* @__PURE__ */ jsx7(
9025
9079
  Table,
9026
9080
  {
@@ -9029,22 +9083,22 @@ function AuditPanel({ active: active2, focused }) {
9029
9083
  { header: "DECISION", width: 9 },
9030
9084
  { header: "TOOL", width: 18 },
9031
9085
  { header: "AGENT", width: 14 },
9032
- { header: "REASON", width: 24 },
9086
+ { header: "REASON", width: reasonW },
9033
9087
  { header: "DLP", width: 4 },
9034
9088
  { header: "WHEN", width: 6 }
9035
9089
  ],
9036
- rows: entries.slice(0, 12).map((e, i) => rowFor(e, i === sel && focused))
9090
+ rows: windowed.map((e, i) => rowFor(e, start + i === selClamped && focused, reasonW))
9037
9091
  }
9038
9092
  )
9039
9093
  ] }) });
9040
9094
  }
9041
- function rowFor(e, active2) {
9095
+ function rowFor(e, active2, reasonW) {
9042
9096
  return [
9043
9097
  { value: active2 ? "\u25B8" : "", color: theme.accentBright },
9044
9098
  { value: e.decision, color: decisionColor(e.decision) },
9045
9099
  { value: truncate2(e.tool_name, 18), color: theme.accent },
9046
9100
  { value: truncate2(e.agent_name ?? "\u2014", 14), dim: true },
9047
- { value: truncate2(e.reason ?? "\u2014", 24) },
9101
+ { value: truncate2(e.reason ?? "\u2014", reasonW) },
9048
9102
  { value: e.dlp_matches?.length ? String(e.dlp_matches.length) : "0", color: e.dlp_matches?.length ? theme.bad : void 0, dim: !e.dlp_matches?.length },
9049
9103
  { value: ago(e.created_at), dim: true }
9050
9104
  ];
@@ -9052,10 +9106,10 @@ function rowFor(e, active2) {
9052
9106
  function Detail({ label, value, color }) {
9053
9107
  return /* @__PURE__ */ jsxs7(Box7, { children: [
9054
9108
  /* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: label.padEnd(9) }),
9055
- /* @__PURE__ */ jsx7(Text7, { color, children: value })
9109
+ /* @__PURE__ */ jsx7(Text7, { color, wrap: "truncate", children: value })
9056
9110
  ] });
9057
9111
  }
9058
- var DECISIONS, SIGNALS;
9112
+ var DECISIONS, SIGNALS, FETCH_LIMIT;
9059
9113
  var init_Audit = __esm({
9060
9114
  "src/tui/panels/Audit.tsx"() {
9061
9115
  "use strict";
@@ -9065,13 +9119,14 @@ var init_Audit = __esm({
9065
9119
  init_theme();
9066
9120
  DECISIONS = [void 0, "DENY", "ALLOW"];
9067
9121
  SIGNALS = [void 0, "dlp", "ratelimit"];
9122
+ FETCH_LIMIT = 500;
9068
9123
  }
9069
9124
  });
9070
9125
 
9071
9126
  // src/tui/panels/Agents.tsx
9072
9127
  import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
9073
9128
  import { useState as useState7 } from "react";
9074
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
9129
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
9075
9130
  function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
9076
9131
  const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
9077
9132
  return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
@@ -9115,7 +9170,7 @@ function AgentsPanel({ focused }) {
9115
9170
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `t${a.trust_score}` })
9116
9171
  ] }, a.session_id))
9117
9172
  ] }),
9118
- /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(Fragment4, { children: [
9173
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
9119
9174
  /* @__PURE__ */ jsxs8(Box8, { children: [
9120
9175
  /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
9121
9176
  /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
@@ -9164,41 +9219,256 @@ var init_Agents = __esm({
9164
9219
  }
9165
9220
  });
9166
9221
 
9167
- // src/tui/App.tsx
9168
- import { Box as Box9, Text as Text9, useApp, useInput as useInput7 } from "ink";
9222
+ // src/tui/panels/Settings.tsx
9223
+ import { Box as Box9, Text as Text9, useInput as useInput7 } from "ink";
9224
+ import TextInput5 from "ink-text-input";
9169
9225
  import { useState as useState8 } from "react";
9170
9226
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
9227
+ function alertBodyFor(channel) {
9228
+ const c2 = channel.trim();
9229
+ const base = { name: "dataroom alert", threshold: 5, windowSeconds: 60, signal: "any" };
9230
+ if (/^https?:\/\//i.test(c2)) return { ...base, slackUrl: c2 };
9231
+ if (c2.includes("@")) return { ...base, emails: [c2] };
9232
+ return { ...base, telegram: [c2] };
9233
+ }
9234
+ function SettingsPanel({ active: active2, focused }) {
9235
+ void active2;
9236
+ const { cols, rows } = usePanelSize();
9237
+ const [sel, setSel] = useState8(0);
9238
+ const [editing, setEditing] = useState8(null);
9239
+ const [input, setInput] = useState8("");
9240
+ const [confirmDel, setConfirmDel] = useState8(null);
9241
+ const [msg, setMsg] = useState8(null);
9242
+ const [busy, setBusy] = useState8(false);
9243
+ const localQ = useLoader(() => api.settings.getLocalLogs());
9244
+ const whQ = useLoader(() => api.settings.getWebhooks());
9245
+ const alertQ = useLoader(() => api.settings.getAlerts());
9246
+ const local = localQ.data;
9247
+ const webhooks = whQ.data?.webhooks ?? [];
9248
+ const alerts = alertQ.data?.rules ?? [];
9249
+ const rowsAll = [
9250
+ { kind: "ll-enabled" },
9251
+ { kind: "ll-path" },
9252
+ ...webhooks.map((wh) => ({ kind: "wh", wh })),
9253
+ { kind: "wh-add" },
9254
+ ...alerts.map((rule) => ({ kind: "alert", rule })),
9255
+ { kind: "alert-add" }
9256
+ ];
9257
+ const selClamped = Math.min(sel, rowsAll.length - 1);
9258
+ const cur = rowsAll[selClamped];
9259
+ const keyOf = (r) => r.kind === "wh" ? "wh:" + r.wh.id : r.kind === "alert" ? "alert:" + r.rule.id : r.kind;
9260
+ const reloadAll = () => {
9261
+ localQ.reload();
9262
+ whQ.reload();
9263
+ alertQ.reload();
9264
+ };
9265
+ const run12 = (label, fn, reload) => {
9266
+ if (busy) return;
9267
+ setBusy(true);
9268
+ setMsg({ text: label + "\u2026", level: "ok" });
9269
+ fn().then(() => {
9270
+ setMsg({ text: "\u2713 " + label, level: "ok" });
9271
+ reload();
9272
+ }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" })).finally(() => setBusy(false));
9273
+ };
9274
+ const activate = (r) => {
9275
+ if (r.kind === "ll-enabled") {
9276
+ if (!local) return;
9277
+ if (!local.enabled && !local.path.trim()) {
9278
+ setMsg({ text: "set a path first (\u2193 then enter)", level: "bad" });
9279
+ return;
9280
+ }
9281
+ run12(local.enabled ? "local logs disabled" : "local logs enabled", () => api.settings.setLocalLogs({ enabled: !local.enabled, path: local.path }), localQ.reload);
9282
+ } else if (r.kind === "ll-path") {
9283
+ setInput(local?.path ?? "");
9284
+ setEditing("path");
9285
+ } else if (r.kind === "wh") {
9286
+ run12(r.wh.enabled ? "webhook disabled" : "webhook enabled", () => api.settings.updateWebhook(r.wh.id, { enabled: !r.wh.enabled }), whQ.reload);
9287
+ } else if (r.kind === "wh-add") {
9288
+ setInput("");
9289
+ setEditing("wh-url");
9290
+ } else if (r.kind === "alert") {
9291
+ run12(r.rule.enabled ? "alert disabled" : "alert enabled", () => api.settings.setAlertEnabled(r.rule.id, !r.rule.enabled), alertQ.reload);
9292
+ } else {
9293
+ setInput("");
9294
+ setEditing("alert-channel");
9295
+ }
9296
+ };
9297
+ const submitInput = () => {
9298
+ const v = input.trim();
9299
+ const which = editing;
9300
+ setEditing(null);
9301
+ if (which === "path") {
9302
+ const enabled = (local?.enabled ?? false) && v.length > 0;
9303
+ run12(v ? `path saved${enabled ? "" : " (press enter on enabled to turn on)"}` : "path cleared (local logs off)", () => api.settings.setLocalLogs({ enabled, path: v }), localQ.reload);
9304
+ } else if (which === "wh-url") {
9305
+ if (!v) return;
9306
+ run12("webhook added", () => api.settings.createWebhook({ url: v, events: "denials" }), whQ.reload);
9307
+ } else if (which === "alert-channel") {
9308
+ if (!v) return;
9309
+ run12("alert rule added", () => api.settings.createAlert(alertBodyFor(v)), alertQ.reload);
9310
+ }
9311
+ };
9312
+ useInput7(
9313
+ (inp, key) => {
9314
+ setMsg(null);
9315
+ if (key.upArrow) {
9316
+ setSel((n) => Math.max(0, n - 1));
9317
+ setConfirmDel(null);
9318
+ } else if (key.downArrow) {
9319
+ setSel((n) => Math.min(rowsAll.length - 1, n + 1));
9320
+ setConfirmDel(null);
9321
+ } else if (key.return || inp === " ") activate(cur);
9322
+ else if (inp === "e" && cur.kind === "wh") {
9323
+ const next = EVENTS[(EVENTS.indexOf(cur.wh.events) + 1) % EVENTS.length];
9324
+ run12(`webhook events \u2192 ${next}`, () => api.settings.updateWebhook(cur.wh.id, { events: next }), whQ.reload);
9325
+ } else if (inp === "e" && cur.kind === "ll-path") activate(cur);
9326
+ else if (inp === "a") {
9327
+ if (cur.kind === "wh" || cur.kind === "wh-add") {
9328
+ setInput("");
9329
+ setEditing("wh-url");
9330
+ } else if (cur.kind === "alert" || cur.kind === "alert-add") {
9331
+ setInput("");
9332
+ setEditing("alert-channel");
9333
+ }
9334
+ } else if (inp === "d" && (cur.kind === "wh" || cur.kind === "alert")) {
9335
+ const k = keyOf(cur);
9336
+ if (confirmDel !== k) {
9337
+ setConfirmDel(k);
9338
+ setMsg({ text: "press d again to delete", level: "bad" });
9339
+ return;
9340
+ }
9341
+ setConfirmDel(null);
9342
+ if (cur.kind === "wh") run12("webhook deleted", () => api.settings.deleteWebhook(cur.wh.id), whQ.reload);
9343
+ else run12("alert deleted", () => api.settings.deleteAlert(cur.rule.id), alertQ.reload);
9344
+ } else if (inp === "r") reloadAll();
9345
+ },
9346
+ { isActive: focused && !editing }
9347
+ );
9348
+ const loading = localQ.loading && !localQ.data || whQ.loading && !whQ.data || alertQ.loading && !alertQ.data;
9349
+ const error = localQ.error ?? whQ.error ?? alertQ.error;
9350
+ const mark = (r) => keyOf(r) === keyOf(cur) && focused ? "\u25B8 " : " ";
9351
+ const onOff = (on) => on ? /* @__PURE__ */ jsx9(Text9, { color: theme.ok, children: "on " }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "off" });
9352
+ const chanOf = (rule) => [...(rule.slackUrls ?? []).map((u) => "slack " + u), ...rule.emails ?? [], ...(rule.telegram ?? []).map((t) => "tg " + t)].join(", ") || "\u2014";
9353
+ const listBudget = Math.max(4, rows - 8);
9354
+ const start = Math.min(Math.max(0, selClamped - Math.floor(listBudget / 2)), Math.max(0, rowsAll.length - listBudget));
9355
+ const inWindow = (r) => {
9356
+ const i = rowsAll.findIndex((x) => keyOf(x) === keyOf(r));
9357
+ return i >= start && i < start + listBudget;
9358
+ };
9359
+ return /* @__PURE__ */ jsx9(DataView, { loading, error, children: /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9360
+ /* @__PURE__ */ jsx9(Text9, { 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" }),
9361
+ editing ? /* @__PURE__ */ jsxs9(Box9, { children: [
9362
+ /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: editing === "path" ? "local log path: " : editing === "wh-url" ? "webhook url: " : "channel (slack url / email / telegram id): " }),
9363
+ /* @__PURE__ */ jsx9(TextInput5, { value: input, onChange: setInput, onSubmit: submitInput })
9364
+ ] }) : msg ? /* @__PURE__ */ jsx9(Text9, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : /* @__PURE__ */ jsx9(Text9, { children: " " }),
9365
+ inWindow(rowsAll[0]) || inWindow(rowsAll[1]) ? /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
9366
+ /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
9367
+ "LOCAL LOGS ",
9368
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "\u2014 hooks mirror every decision to a file on the agent machine" })
9369
+ ] }),
9370
+ /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9371
+ /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "ll-enabled" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[0]) }),
9372
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "enabled ".padEnd(10) }),
9373
+ onOff(!!local?.enabled),
9374
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " enter toggles" })
9375
+ ] }),
9376
+ /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9377
+ /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "ll-path" && focused ? theme.accentBright : theme.dim, children: mark(rowsAll[1]) }),
9378
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "path ".padEnd(10) }),
9379
+ local?.path ? /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(local.path, cols - 14) }) : /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "not set \u2014 enter to edit" })
9380
+ ] })
9381
+ ] }) : null,
9382
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
9383
+ /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
9384
+ "WEBHOOKS ",
9385
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
9386
+ "\u2014 POST every matching event to your endpoint (",
9387
+ webhooks.length,
9388
+ ")"
9389
+ ] })
9390
+ ] }),
9391
+ webhooks.filter((wh) => inWindow({ kind: "wh", wh })).map((wh) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9392
+ /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "wh:" + wh.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh", wh }) }),
9393
+ onOff(wh.enabled),
9394
+ /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: (" " + wh.events).padEnd(9) }),
9395
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(wh.url, cols - 16) })
9396
+ ] }, wh.id)),
9397
+ inWindow(rowsAll.find((r) => r.kind === "wh-add")) ? /* @__PURE__ */ jsxs9(Text9, { children: [
9398
+ /* @__PURE__ */ jsx9(Text9, { color: cur.kind === "wh-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "wh-add" }) }),
9399
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "+ add webhook (enter)" })
9400
+ ] }) : null
9401
+ ] }),
9402
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
9403
+ /* @__PURE__ */ jsxs9(Text9, { bold: true, color: theme.accentBright, children: [
9404
+ "ALERTS ",
9405
+ /* @__PURE__ */ jsxs9(Text9, { color: theme.dim, children: [
9406
+ "\u2014 notify on a signal spike (",
9407
+ alerts.length,
9408
+ ")"
9409
+ ] })
9410
+ ] }),
9411
+ alerts.filter((rule) => inWindow({ kind: "alert", rule })).map((rule) => /* @__PURE__ */ jsxs9(Text9, { wrap: "truncate", children: [
9412
+ /* @__PURE__ */ jsx9(Text9, { color: keyOf(cur) === "alert:" + rule.id && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert", rule }) }),
9413
+ onOff(rule.enabled),
9414
+ /* @__PURE__ */ jsx9(Text9, { color: theme.warn, children: (" " + rule.signal).padEnd(11) }),
9415
+ /* @__PURE__ */ jsx9(Text9, { children: `\u2265${rule.threshold}/${rule.windowSeconds}s ` }),
9416
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: truncate2(chanOf(rule), cols - 26) })
9417
+ ] }, rule.id)),
9418
+ inWindow(rowsAll[rowsAll.length - 1]) ? /* @__PURE__ */ jsxs9(Text9, { children: [
9419
+ /* @__PURE__ */ jsx9(Text9, { color: cur.kind === "alert-add" && focused ? theme.accentBright : theme.dim, children: mark({ kind: "alert-add" }) }),
9420
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "+ add alert (enter \u2014 one channel: slack url, email or telegram chat id)" })
9421
+ ] }) : null
9422
+ ] })
9423
+ ] }) });
9424
+ }
9425
+ var EVENTS;
9426
+ var init_Settings = __esm({
9427
+ "src/tui/panels/Settings.tsx"() {
9428
+ "use strict";
9429
+ init_api_client();
9430
+ init_components();
9431
+ init_hooks();
9432
+ init_theme();
9433
+ EVENTS = ["denials", "allowed", "all"];
9434
+ }
9435
+ });
9436
+
9437
+ // src/tui/App.tsx
9438
+ import { Box as Box10, Text as Text10, useApp, useInput as useInput8 } from "ink";
9439
+ import { useState as useState9 } from "react";
9440
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
9171
9441
  function LiveHint() {
9172
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9173
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9174
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9175
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9176
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
9442
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
9443
+ /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9444
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9445
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9446
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text10, { children: [
9177
9447
  "press ",
9178
- /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
9448
+ /* @__PURE__ */ jsx10(Text10, { color: theme.accent, children: "enter" }),
9179
9449
  " to go live"
9180
9450
  ] }) })
9181
9451
  ] });
9182
9452
  }
9183
- function Banner() {
9184
- const wide = (process.stdout.columns ?? 80) >= 82;
9453
+ function Banner({ cols }) {
9454
+ const wide = cols >= 82;
9185
9455
  if (!wide) {
9186
- return /* @__PURE__ */ jsxs9(Box9, { children: [
9187
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9188
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
9456
+ return /* @__PURE__ */ jsxs10(Box10, { children: [
9457
+ /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9458
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " \u2014 security control center" })
9189
9459
  ] });
9190
9460
  }
9191
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9192
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9193
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9461
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
9462
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx10(Text10, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9463
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9194
9464
  ] });
9195
9465
  }
9196
9466
  function App() {
9197
9467
  const { exit } = useApp();
9198
- const [section, setSection] = useState8(0);
9199
- const [focus, setFocus] = useState8("nav");
9200
- const [help, setHelp] = useState8(false);
9201
- useInput7((input, key) => {
9468
+ const [section, setSection] = useState9(0);
9469
+ const [focus, setFocus] = useState9("nav");
9470
+ const [help, setHelp] = useState9(false);
9471
+ useInput8((input, key) => {
9202
9472
  if (help) {
9203
9473
  setHelp(false);
9204
9474
  return;
@@ -9217,34 +9487,33 @@ function App() {
9217
9487
  else if (input === "q" && SECTIONS[section].label === "Live") exit();
9218
9488
  }
9219
9489
  });
9220
- const cols = process.stdout.columns ?? 100;
9221
- const rows = process.stdout.rows ?? 30;
9490
+ const { cols, rows } = useTermSize();
9222
9491
  const current = SECTIONS[section];
9223
- if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
9492
+ if (help) return /* @__PURE__ */ jsx10(HelpOverlay, { cols, rows });
9224
9493
  if (current.label === "Live" && focus === "panel") {
9225
- return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
9494
+ return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx10(LivePanel, { active: true, focused: true }) });
9226
9495
  }
9227
9496
  const Panel = current.Panel;
9228
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9229
- /* @__PURE__ */ jsx9(Banner, {}),
9230
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
9231
- /* @__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)) }),
9232
- /* @__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" }) })
9497
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9498
+ /* @__PURE__ */ jsx10(Banner, { cols }),
9499
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexGrow: 1, children: [
9500
+ /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx10(Text10, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
9501
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx10(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
9233
9502
  ] }),
9234
- /* @__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"]] }) })
9503
+ /* @__PURE__ */ jsx10(Box10, { children: focus === "nav" ? /* @__PURE__ */ jsx10(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx10(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9235
9504
  ] });
9236
9505
  }
9237
9506
  function HelpOverlay({ cols, rows }) {
9238
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9239
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9240
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
9241
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
9242
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
9243
- /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9244
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
9507
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9508
+ /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9509
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
9510
+ /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.accent, children: group }),
9511
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs10(Text10, { children: [
9512
+ /* @__PURE__ */ jsx10(Text10, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9513
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: desc })
9245
9514
  ] }, k))
9246
9515
  ] }, group)) }),
9247
- /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
9516
+ /* @__PURE__ */ jsx10(Text10, { color: theme.dim, children: "press any key to close" })
9248
9517
  ] });
9249
9518
  }
9250
9519
  var SECTIONS, BANNER_HEX, HELP;
@@ -9261,6 +9530,8 @@ var init_App = __esm({
9261
9530
  init_Stats();
9262
9531
  init_Audit();
9263
9532
  init_Agents();
9533
+ init_Settings();
9534
+ init_hooks();
9264
9535
  SECTIONS = [
9265
9536
  { label: "Live", Panel: LiveHint },
9266
9537
  { label: "Agents", Panel: AgentsPanel },
@@ -9268,14 +9539,17 @@ var init_App = __esm({
9268
9539
  { label: "Rate Limit", Panel: RateLimitPanel },
9269
9540
  { label: "DLP", Panel: DlpPanel },
9270
9541
  { label: "Stats", Panel: StatsPanel },
9271
- { label: "Audit", Panel: AuditPanel }
9542
+ { label: "Audit", Panel: AuditPanel },
9543
+ { label: "Settings", Panel: SettingsPanel }
9272
9544
  ];
9273
9545
  BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
9274
9546
  HELP = [
9275
9547
  ["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
9276
- ["Live", [["\u2191\u2193", "select a stream row"], ["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)"]]],
9548
+ ["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)"]]],
9277
9549
  ["Policies", [["\u2191\u2193", "browse / select"], ["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"], ["v", "versions / rollback"], ["s", "save"], ["x", "discard"]]],
9278
- ["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"]]]
9550
+ ["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"]]],
9551
+ ["Audit", [["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry detail"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
9552
+ ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
9279
9553
  ];
9280
9554
  }
9281
9555
  });
@@ -9286,7 +9560,7 @@ __export(tui_exports, {
9286
9560
  launchTui: () => launchTui
9287
9561
  });
9288
9562
  import { render } from "ink";
9289
- import { jsx as jsx10 } from "react/jsx-runtime";
9563
+ import { jsx as jsx11 } from "react/jsx-runtime";
9290
9564
  async function launchTui() {
9291
9565
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
9292
9566
  process.stderr.write(
@@ -9298,10 +9572,9 @@ async function launchTui() {
9298
9572
  process.stderr.write("Not logged in. Run `solongate login` first.\n");
9299
9573
  return;
9300
9574
  }
9301
- captureFocusTarget();
9302
9575
  process.stdout.write("\x1B[?1049h\x1B[H");
9303
9576
  try {
9304
- const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
9577
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx11(App, {}));
9305
9578
  await waitUntilExit();
9306
9579
  } finally {
9307
9580
  process.stdout.write("\x1B[?1049l");
@@ -9312,7 +9585,6 @@ var init_tui = __esm({
9312
9585
  "use strict";
9313
9586
  init_App();
9314
9587
  init_client();
9315
- init_notify();
9316
9588
  }
9317
9589
  });
9318
9590
 
@@ -10004,9 +10276,9 @@ var init_agents2 = __esm({
10004
10276
  });
10005
10277
 
10006
10278
  // src/commands/doctor.ts
10007
- import { existsSync as existsSync5, statSync as statSync2 } from "fs";
10279
+ import { existsSync as existsSync4, statSync as statSync2 } from "fs";
10008
10280
  import { homedir as homedir5 } from "os";
10009
- import { join as join8 } from "path";
10281
+ import { join as join7 } from "path";
10010
10282
  async function run6(argv) {
10011
10283
  const { flags } = parse(argv);
10012
10284
  const json = flagBool(flags, "json");
@@ -10036,7 +10308,7 @@ async function run6(argv) {
10036
10308
  } catch {
10037
10309
  }
10038
10310
  }
10039
- if (existsSync5(LOCAL_LOG2)) {
10311
+ if (existsSync4(LOCAL_LOG2)) {
10040
10312
  const st = statSync2(LOCAL_LOG2);
10041
10313
  const ageMin = (Date.now() - st.mtimeMs) / 6e4;
10042
10314
  checks.push({ name: "local logs", ok: true, detail: `on \xB7 ${(st.size / 1024).toFixed(0)}KB \xB7 last write ${ageMin < 1 ? "just now" : Math.round(ageMin) + "m ago"}` });
@@ -10066,14 +10338,14 @@ var init_doctor = __esm({
10066
10338
  init_api_client();
10067
10339
  init_format();
10068
10340
  init_args();
10069
- LOCAL_LOG2 = join8(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10341
+ LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10070
10342
  }
10071
10343
  });
10072
10344
 
10073
10345
  // src/commands/watch.ts
10074
- import { closeSync as closeSync2, existsSync as existsSync6, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
10346
+ import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
10075
10347
  import { homedir as homedir6 } from "os";
10076
- import { join as join9 } from "path";
10348
+ import { join as join8 } from "path";
10077
10349
  function tailLocal(file, maxBytes = 131072) {
10078
10350
  try {
10079
10351
  const size = statSync3(file).size;
@@ -10117,7 +10389,7 @@ async function run7(argv) {
10117
10389
  for (const r of rows) if (keep(r)) print(r, json);
10118
10390
  };
10119
10391
  const pollLocal = () => {
10120
- if (cloudOnly || !existsSync6(LOCAL_LOG3)) return;
10392
+ if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
10121
10393
  const rows = [];
10122
10394
  for (const line of tailLocal(LOCAL_LOG3)) {
10123
10395
  try {
@@ -10180,7 +10452,7 @@ var init_watch = __esm({
10180
10452
  init_cli_utils();
10181
10453
  init_args();
10182
10454
  init_format();
10183
- LOCAL_LOG3 = join9(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10455
+ LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10184
10456
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
10185
10457
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
10186
10458
  }
@@ -10482,32 +10754,32 @@ __export(global_install_exports, {
10482
10754
  runGlobalRestore: () => runGlobalRestore,
10483
10755
  unlockProtected: () => unlockProtected
10484
10756
  });
10485
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
10486
- import { resolve as resolve4, join as join10, dirname } from "path";
10757
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10758
+ import { resolve as resolve4, join as join9, dirname } from "path";
10487
10759
  import { homedir as homedir7 } from "os";
10488
10760
  import { fileURLToPath } from "url";
10489
10761
  import { createInterface } from "readline";
10490
- import { execFileSync as execFileSync3 } from "child_process";
10762
+ import { execFileSync as execFileSync2 } from "child_process";
10491
10763
  function lockFile(file) {
10492
- if (!existsSync7(file)) return;
10764
+ if (!existsSync6(file)) return;
10493
10765
  try {
10494
10766
  if (process.platform === "win32") {
10495
10767
  try {
10496
- execFileSync3("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10768
+ execFileSync2("icacls", [file, "/deny", "*S-1-1-0:(WD,AD,DC,DE)"], { stdio: "ignore" });
10497
10769
  } catch {
10498
10770
  }
10499
10771
  try {
10500
- execFileSync3("attrib", ["+R", file], { stdio: "ignore" });
10772
+ execFileSync2("attrib", ["+R", file], { stdio: "ignore" });
10501
10773
  } catch {
10502
10774
  }
10503
10775
  } else if (process.platform === "darwin") {
10504
10776
  try {
10505
- execFileSync3("chflags", ["uchg", file], { stdio: "ignore" });
10777
+ execFileSync2("chflags", ["uchg", file], { stdio: "ignore" });
10506
10778
  } catch {
10507
10779
  }
10508
10780
  } else {
10509
10781
  try {
10510
- execFileSync3("chattr", ["+i", file], { stdio: "ignore" });
10782
+ execFileSync2("chattr", ["+i", file], { stdio: "ignore" });
10511
10783
  } catch {
10512
10784
  }
10513
10785
  }
@@ -10515,29 +10787,29 @@ function lockFile(file) {
10515
10787
  }
10516
10788
  }
10517
10789
  function unlockFile(file) {
10518
- if (!existsSync7(file)) return;
10790
+ if (!existsSync6(file)) return;
10519
10791
  try {
10520
10792
  if (process.platform === "win32") {
10521
10793
  try {
10522
- execFileSync3("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10794
+ execFileSync2("icacls", [file, "/remove:d", "*S-1-1-0"], { stdio: "ignore" });
10523
10795
  } catch {
10524
10796
  }
10525
10797
  try {
10526
- execFileSync3("icacls", [file, "/reset"], { stdio: "ignore" });
10798
+ execFileSync2("icacls", [file, "/reset"], { stdio: "ignore" });
10527
10799
  } catch {
10528
10800
  }
10529
10801
  try {
10530
- execFileSync3("attrib", ["-R", file], { stdio: "ignore" });
10802
+ execFileSync2("attrib", ["-R", file], { stdio: "ignore" });
10531
10803
  } catch {
10532
10804
  }
10533
10805
  } else if (process.platform === "darwin") {
10534
10806
  try {
10535
- execFileSync3("chflags", ["nouchg", file], { stdio: "ignore" });
10807
+ execFileSync2("chflags", ["nouchg", file], { stdio: "ignore" });
10536
10808
  } catch {
10537
10809
  }
10538
10810
  } else {
10539
10811
  try {
10540
- execFileSync3("chattr", ["-i", file], { stdio: "ignore" });
10812
+ execFileSync2("chattr", ["-i", file], { stdio: "ignore" });
10541
10813
  } catch {
10542
10814
  }
10543
10815
  }
@@ -10547,10 +10819,10 @@ function unlockFile(file) {
10547
10819
  function protectedTargets() {
10548
10820
  const p = globalPaths();
10549
10821
  return [
10550
- join10(p.hooksDir, "guard.mjs"),
10551
- join10(p.hooksDir, "audit.mjs"),
10552
- join10(p.hooksDir, "stop.mjs"),
10553
- join10(p.hooksDir, "shield.mjs"),
10822
+ join9(p.hooksDir, "guard.mjs"),
10823
+ join9(p.hooksDir, "audit.mjs"),
10824
+ join9(p.hooksDir, "stop.mjs"),
10825
+ join9(p.hooksDir, "shield.mjs"),
10554
10826
  p.configPath,
10555
10827
  p.settingsPath
10556
10828
  ];
@@ -10563,25 +10835,25 @@ function unlockProtected() {
10563
10835
  }
10564
10836
  function globalPaths() {
10565
10837
  const home = homedir7();
10566
- const sgDir = join10(home, ".solongate");
10567
- const hooksDir = join10(sgDir, "hooks");
10568
- const claudeDir = join10(home, ".claude");
10838
+ const sgDir = join9(home, ".solongate");
10839
+ const hooksDir = join9(sgDir, "hooks");
10840
+ const claudeDir = join9(home, ".claude");
10569
10841
  return {
10570
10842
  home,
10571
10843
  sgDir,
10572
10844
  hooksDir,
10573
10845
  claudeDir,
10574
- settingsPath: join10(claudeDir, "settings.json"),
10575
- backupPath: join10(claudeDir, "settings.solongate.bak"),
10576
- configPath: join10(sgDir, "cloud-guard.json")
10846
+ settingsPath: join9(claudeDir, "settings.json"),
10847
+ backupPath: join9(claudeDir, "settings.solongate.bak"),
10848
+ configPath: join9(sgDir, "cloud-guard.json")
10577
10849
  };
10578
10850
  }
10579
10851
  function readHook(filename) {
10580
- return readFileSync7(join10(HOOKS_DIR, filename), "utf-8");
10852
+ return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
10581
10853
  }
10582
10854
  function readGuard() {
10583
- const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
10584
- return existsSync7(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
10855
+ const bundled = join9(HOOKS_DIR, "guard.bundled.mjs");
10856
+ return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
10585
10857
  }
10586
10858
  function ask(question) {
10587
10859
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -10594,14 +10866,14 @@ function runGlobalRestore() {
10594
10866
  const p = globalPaths();
10595
10867
  unlockProtected();
10596
10868
  removeClaudeShim();
10597
- if (existsSync7(p.backupPath)) {
10598
- writeFileSync5(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10869
+ if (existsSync6(p.backupPath)) {
10870
+ writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10599
10871
  console.log(` Restored ${p.settingsPath} from backup.`);
10600
- } else if (existsSync7(p.settingsPath)) {
10872
+ } else if (existsSync6(p.settingsPath)) {
10601
10873
  try {
10602
10874
  const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
10603
10875
  delete s.hooks;
10604
- writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10876
+ writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10605
10877
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
10606
10878
  } catch {
10607
10879
  }
@@ -10616,7 +10888,7 @@ function escapeRe(s) {
10616
10888
  function resolveRealClaude() {
10617
10889
  try {
10618
10890
  const finder = process.platform === "win32" ? "where" : "which";
10619
- const out2 = execFileSync3(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
10891
+ const out2 = execFileSync2(finder, ["claude"], { encoding: "utf-8" }).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
10620
10892
  if (process.platform === "win32") {
10621
10893
  const low = (s) => s.toLowerCase();
10622
10894
  return out2.find((l) => low(l).endsWith(".cmd")) || out2.find((l) => low(l).endsWith(".exe")) || out2.find((l) => low(l).endsWith(".bat")) || out2[0] || null;
@@ -10629,24 +10901,24 @@ function resolveRealClaude() {
10629
10901
  function shimTargets() {
10630
10902
  if (process.platform === "win32") {
10631
10903
  try {
10632
- const prof = execFileSync3("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
10904
+ const prof = execFileSync2("powershell", ["-NoProfile", "-Command", "$PROFILE.CurrentUserAllHosts"], { encoding: "utf-8" }).trim();
10633
10905
  return prof ? [prof] : [];
10634
10906
  } catch {
10635
10907
  return [];
10636
10908
  }
10637
10909
  }
10638
- return [".bashrc", ".zshrc", ".profile"].map((f) => join10(homedir7(), f)).filter((f) => existsSync7(f));
10910
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join9(homedir7(), f)).filter((f) => existsSync6(f));
10639
10911
  }
10640
10912
  function writeShimBlock(file, block2) {
10641
10913
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10642
- let content = existsSync7(file) ? readFileSync7(file, "utf-8") : "";
10914
+ let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
10643
10915
  content = content.replace(re, "");
10644
10916
  if (block2) {
10645
10917
  if (content.length && !content.endsWith("\n")) content += "\n";
10646
10918
  content += block2 + "\n";
10647
10919
  }
10648
10920
  mkdirSync4(dirname(file), { recursive: true });
10649
- writeFileSync5(file, content);
10921
+ writeFileSync4(file, content);
10650
10922
  }
10651
10923
  function installClaudeShim(shieldPath) {
10652
10924
  const real = resolveRealClaude();
@@ -10700,19 +10972,19 @@ async function runGlobalInstall(opts = {}) {
10700
10972
  mkdirSync4(p.hooksDir, { recursive: true });
10701
10973
  mkdirSync4(p.claudeDir, { recursive: true });
10702
10974
  unlockProtected();
10703
- writeFileSync5(join10(p.hooksDir, "guard.mjs"), readGuard());
10704
- writeFileSync5(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10705
- writeFileSync5(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10706
- writeFileSync5(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10975
+ writeFileSync4(join9(p.hooksDir, "guard.mjs"), readGuard());
10976
+ writeFileSync4(join9(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10977
+ writeFileSync4(join9(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10978
+ writeFileSync4(join9(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10707
10979
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
10708
- installClaudeShim(join10(p.hooksDir, "shield.mjs"));
10709
- writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10980
+ installClaudeShim(join9(p.hooksDir, "shield.mjs"));
10981
+ writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10710
10982
  console.log(` Wrote ${p.configPath}`);
10711
10983
  let existing = {};
10712
- if (existsSync7(p.settingsPath)) {
10984
+ if (existsSync6(p.settingsPath)) {
10713
10985
  const raw = readFileSync7(p.settingsPath, "utf-8");
10714
- if (!existsSync7(p.backupPath)) {
10715
- writeFileSync5(p.backupPath, raw);
10986
+ if (!existsSync6(p.backupPath)) {
10987
+ writeFileSync4(p.backupPath, raw);
10716
10988
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
10717
10989
  }
10718
10990
  try {
@@ -10721,9 +10993,9 @@ async function runGlobalInstall(opts = {}) {
10721
10993
  existing = {};
10722
10994
  }
10723
10995
  }
10724
- const guardAbs = join10(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10725
- const auditAbs = join10(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10726
- const stopAbs = join10(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10996
+ const guardAbs = join9(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10997
+ const auditAbs = join9(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10998
+ const stopAbs = join9(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10727
10999
  const nodeBin = process.execPath.replace(/\\/g, "/");
10728
11000
  const call = process.platform === "win32" ? "& " : "";
10729
11001
  const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
@@ -10735,7 +11007,7 @@ async function runGlobalInstall(opts = {}) {
10735
11007
  Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
10736
11008
  }
10737
11009
  };
10738
- writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11010
+ writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10739
11011
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
10740
11012
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
10741
11013
  lockProtected();
@@ -10915,7 +11187,7 @@ import { createServer, request as httpRequest } from "http";
10915
11187
  import { request as httpsRequest } from "https";
10916
11188
  import { spawn as spawn3 } from "child_process";
10917
11189
  import { URL as URL2 } from "url";
10918
- import { readFileSync as readFileSync8, existsSync as existsSync8, readdirSync, statSync as statSync4 } from "fs";
11190
+ import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
10919
11191
  import { resolve as resolve5 } from "path";
10920
11192
  import { homedir as homedir8 } from "os";
10921
11193
  function findCacheFile() {
@@ -10923,7 +11195,7 @@ function findCacheFile() {
10923
11195
  const envSel = process.env.SOLONGATE_AGENT_ID;
10924
11196
  if (envSel) {
10925
11197
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
10926
- if (existsSync8(f)) return f;
11198
+ if (existsSync7(f)) return f;
10927
11199
  }
10928
11200
  let best = null, bestTs = -1;
10929
11201
  try {
@@ -10944,7 +11216,7 @@ function findCacheFile() {
10944
11216
  function loadCfg() {
10945
11217
  try {
10946
11218
  const f = findCacheFile();
10947
- if (f && existsSync8(f)) {
11219
+ if (f && existsSync7(f)) {
10948
11220
  const c2 = JSON.parse(readFileSync8(f, "utf-8"));
10949
11221
  const d = c2?.security?.dlpRedact;
10950
11222
  const g = c2?.security?.ghost;
@@ -11234,7 +11506,7 @@ __export(logs_server_exports, {
11234
11506
  });
11235
11507
  import { createServer as createServer2 } from "http";
11236
11508
  import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11237
- import { resolve as resolve6, join as join11, isAbsolute } from "path";
11509
+ import { resolve as resolve6, join as join10, isAbsolute } from "path";
11238
11510
  import { homedir as homedir9 } from "os";
11239
11511
  import { readdirSync as readdirSync2 } from "fs";
11240
11512
  function allowedOrigins() {
@@ -11260,7 +11532,7 @@ async function findLogDir() {
11260
11532
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
11261
11533
  for (const f of files) {
11262
11534
  try {
11263
- const c2 = JSON.parse(readFileSync9(join11(base, f), "utf-8"));
11535
+ const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
11264
11536
  const p = c2?.security?.localLogs?.path;
11265
11537
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
11266
11538
  } catch {
@@ -11269,7 +11541,7 @@ async function findLogDir() {
11269
11541
  } catch {
11270
11542
  }
11271
11543
  try {
11272
- const cfgRaw = readFileSync9(join11(base, "cloud-guard.json"), "utf-8");
11544
+ const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
11273
11545
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
11274
11546
  if (apiKey) {
11275
11547
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -11298,7 +11570,7 @@ function setCors(req, res) {
11298
11570
  }
11299
11571
  function fileInfo(dir) {
11300
11572
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
11301
- const file = join11(dir, LOG_FILENAME);
11573
+ const file = join10(dir, LOG_FILENAME);
11302
11574
  try {
11303
11575
  const st = statSync5(file);
11304
11576
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -11411,7 +11683,7 @@ var init_logs_server = __esm({
11411
11683
 
11412
11684
  // src/inject.ts
11413
11685
  var inject_exports = {};
11414
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync9, copyFileSync } from "fs";
11686
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync8, copyFileSync } from "fs";
11415
11687
  import { resolve as resolve7 } from "path";
11416
11688
  import { execSync } from "child_process";
11417
11689
  function parseInjectArgs(argv) {
@@ -11469,7 +11741,7 @@ WHAT IT DOES
11469
11741
  `);
11470
11742
  }
11471
11743
  function detectProject() {
11472
- if (!existsSync9(resolve7("package.json"))) return false;
11744
+ if (!existsSync8(resolve7("package.json"))) return false;
11473
11745
  try {
11474
11746
  const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11475
11747
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -11485,13 +11757,13 @@ function findTsEntryFile() {
11485
11757
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
11486
11758
  if (typeof binPath === "string") {
11487
11759
  const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
11488
- if (existsSync9(resolve7(srcPath))) return resolve7(srcPath);
11489
- if (existsSync9(resolve7(binPath))) return resolve7(binPath);
11760
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11761
+ if (existsSync8(resolve7(binPath))) return resolve7(binPath);
11490
11762
  }
11491
11763
  }
11492
11764
  if (pkg.main) {
11493
11765
  const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
11494
- if (existsSync9(resolve7(srcPath))) return resolve7(srcPath);
11766
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11495
11767
  }
11496
11768
  } catch {
11497
11769
  }
@@ -11505,7 +11777,7 @@ function findTsEntryFile() {
11505
11777
  ];
11506
11778
  for (const c2 of candidates) {
11507
11779
  const full = resolve7(c2);
11508
- if (existsSync9(full)) {
11780
+ if (existsSync8(full)) {
11509
11781
  try {
11510
11782
  const content = readFileSync10(full, "utf-8");
11511
11783
  if (content.includes("McpServer") || content.includes("McpServer")) {
@@ -11516,13 +11788,13 @@ function findTsEntryFile() {
11516
11788
  }
11517
11789
  }
11518
11790
  for (const c2 of candidates) {
11519
- if (existsSync9(resolve7(c2))) return resolve7(c2);
11791
+ if (existsSync8(resolve7(c2))) return resolve7(c2);
11520
11792
  }
11521
11793
  return null;
11522
11794
  }
11523
11795
  function detectPackageManager() {
11524
- if (existsSync9(resolve7("pnpm-lock.yaml"))) return "pnpm";
11525
- if (existsSync9(resolve7("yarn.lock"))) return "yarn";
11796
+ if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
11797
+ if (existsSync8(resolve7("yarn.lock"))) return "yarn";
11526
11798
  return "npm";
11527
11799
  }
11528
11800
  function installSdk() {
@@ -11665,7 +11937,7 @@ async function main2() {
11665
11937
  }
11666
11938
  log3(" Language: TypeScript");
11667
11939
  const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
11668
- if (!entryFile || !existsSync9(entryFile)) {
11940
+ if (!entryFile || !existsSync8(entryFile)) {
11669
11941
  log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
11670
11942
  log3("");
11671
11943
  log3(" Specify it manually: --file <path>");
@@ -11678,7 +11950,7 @@ async function main2() {
11678
11950
  log3("");
11679
11951
  const backupPath = entryFile + ".solongate-backup";
11680
11952
  if (opts.restore) {
11681
- if (!existsSync9(backupPath)) {
11953
+ if (!existsSync8(backupPath)) {
11682
11954
  log3(" No backup found. Nothing to restore.");
11683
11955
  process.exit(1);
11684
11956
  }
@@ -11714,12 +11986,12 @@ async function main2() {
11714
11986
  log3(" To apply: npx @solongate/proxy inject");
11715
11987
  process.exit(0);
11716
11988
  }
11717
- if (!existsSync9(backupPath)) {
11989
+ if (!existsSync8(backupPath)) {
11718
11990
  copyFileSync(entryFile, backupPath);
11719
11991
  log3("");
11720
11992
  log3(` Backup: ${backupPath}`);
11721
11993
  }
11722
- writeFileSync6(entryFile, result.modified);
11994
+ writeFileSync5(entryFile, result.modified);
11723
11995
  log3("");
11724
11996
  log3(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
11725
11997
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -11748,8 +12020,8 @@ var init_inject = __esm({
11748
12020
 
11749
12021
  // src/create.ts
11750
12022
  var create_exports = {};
11751
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
11752
- import { resolve as resolve8, join as join12 } from "path";
12023
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
12024
+ import { resolve as resolve8, join as join11 } from "path";
11753
12025
  import { execSync as execSync2 } from "child_process";
11754
12026
  function withSpinner(message, fn) {
11755
12027
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -11828,8 +12100,8 @@ EXAMPLES
11828
12100
  `);
11829
12101
  }
11830
12102
  function createProject(dir, name, _policy) {
11831
- writeFileSync7(
11832
- join12(dir, "package.json"),
12103
+ writeFileSync6(
12104
+ join11(dir, "package.json"),
11833
12105
  JSON.stringify(
11834
12106
  {
11835
12107
  name,
@@ -11858,8 +12130,8 @@ function createProject(dir, name, _policy) {
11858
12130
  2
11859
12131
  ) + "\n"
11860
12132
  );
11861
- writeFileSync7(
11862
- join12(dir, "tsconfig.json"),
12133
+ writeFileSync6(
12134
+ join11(dir, "tsconfig.json"),
11863
12135
  JSON.stringify(
11864
12136
  {
11865
12137
  compilerOptions: {
@@ -11879,9 +12151,9 @@ function createProject(dir, name, _policy) {
11879
12151
  2
11880
12152
  ) + "\n"
11881
12153
  );
11882
- mkdirSync5(join12(dir, "src"), { recursive: true });
11883
- writeFileSync7(
11884
- join12(dir, "src", "index.ts"),
12154
+ mkdirSync5(join11(dir, "src"), { recursive: true });
12155
+ writeFileSync6(
12156
+ join11(dir, "src", "index.ts"),
11885
12157
  `#!/usr/bin/env node
11886
12158
 
11887
12159
  console.log = (...args: unknown[]) => {
@@ -11922,8 +12194,8 @@ console.log('');
11922
12194
  console.log('Press Ctrl+C to stop.');
11923
12195
  `
11924
12196
  );
11925
- writeFileSync7(
11926
- join12(dir, ".mcp.json"),
12197
+ writeFileSync6(
12198
+ join11(dir, ".mcp.json"),
11927
12199
  JSON.stringify(
11928
12200
  {
11929
12201
  mcpServers: {
@@ -11940,13 +12212,13 @@ console.log('Press Ctrl+C to stop.');
11940
12212
  2
11941
12213
  ) + "\n"
11942
12214
  );
11943
- writeFileSync7(
11944
- join12(dir, ".env"),
12215
+ writeFileSync6(
12216
+ join11(dir, ".env"),
11945
12217
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
11946
12218
  `
11947
12219
  );
11948
- writeFileSync7(
11949
- join12(dir, ".gitignore"),
12220
+ writeFileSync6(
12221
+ join11(dir, ".gitignore"),
11950
12222
  `node_modules/
11951
12223
  dist/
11952
12224
  *.solongate-backup
@@ -11960,7 +12232,7 @@ async function main3() {
11960
12232
  const opts = parseCreateArgs(process.argv);
11961
12233
  const dir = resolve8(opts.name);
11962
12234
  printBanner("Create MCP Server");
11963
- if (existsSync10(dir)) {
12235
+ if (existsSync9(dir)) {
11964
12236
  log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
11965
12237
  process.exit(1);
11966
12238
  }
@@ -12039,12 +12311,12 @@ var init_create = __esm({
12039
12311
 
12040
12312
  // src/pull-push.ts
12041
12313
  var pull_push_exports = {};
12042
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync11 } from "fs";
12314
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
12043
12315
  import { resolve as resolve9 } from "path";
12044
12316
  function loadEnv() {
12045
12317
  if (process.env.SOLONGATE_API_KEY) return;
12046
12318
  const envPath = resolve9(".env");
12047
- if (!existsSync11(envPath)) return;
12319
+ if (!existsSync10(envPath)) return;
12048
12320
  try {
12049
12321
  const content = readFileSync11(envPath, "utf-8");
12050
12322
  for (const line of content.split("\n")) {
@@ -12234,7 +12506,7 @@ async function pull(apiKey, file, policyId) {
12234
12506
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
12235
12507
  const { id: _id, ...policyWithoutId } = policy;
12236
12508
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
12237
- writeFileSync8(file, json, "utf-8");
12509
+ writeFileSync7(file, json, "utf-8");
12238
12510
  log5("");
12239
12511
  log5(green2(" Saved to: ") + file);
12240
12512
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -12246,7 +12518,7 @@ async function pull(apiKey, file, policyId) {
12246
12518
  log5("");
12247
12519
  }
12248
12520
  async function push(apiKey, file, policyId) {
12249
- if (!existsSync11(file)) {
12521
+ if (!existsSync10(file)) {
12250
12522
  log5(red2(`ERROR: File not found: ${file}`));
12251
12523
  process.exit(1);
12252
12524
  }