@solongate/proxy 0.81.26 → 0.81.28

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
@@ -8877,6 +8877,9 @@ var init_Dlp = __esm({
8877
8877
  // src/tui/panels/Audit.tsx
8878
8878
  import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
8879
8879
  import TextInput4 from "ink-text-input";
8880
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
8881
+ import { homedir as homedir6 } from "os";
8882
+ import { join as join8 } from "path";
8880
8883
  import { useState as useState6 } from "react";
8881
8884
  import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
8882
8885
  function loadLocalRows() {
@@ -8916,6 +8919,7 @@ function AuditPanel({ active: active2, focused }) {
8916
8919
  const [sessSel, setSessSel] = useState6(0);
8917
8920
  const [confirm, setConfirm] = useState6(null);
8918
8921
  const [msg, setMsg] = useState6(null);
8922
+ const [showHelp, setShowHelp] = useState6(false);
8919
8923
  const toTop = () => setSel(0);
8920
8924
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
8921
8925
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
@@ -8940,26 +8944,25 @@ function AuditPanel({ active: active2, focused }) {
8940
8944
  usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
8941
8945
  const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
8942
8946
  usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
8947
+ const q = search.trim().toLowerCase();
8948
+ const localFiltered = (localQ.data ?? []).filter((r) => {
8949
+ if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
8950
+ if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
8951
+ if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
8952
+ if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
8953
+ if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
8954
+ if (sessFilter && r.session !== sessFilter) return false;
8955
+ if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
8956
+ return true;
8957
+ });
8943
8958
  let pageRows = [];
8944
8959
  let total = 0;
8945
8960
  if (source === "cloud") {
8946
8961
  pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
8947
8962
  total = cloudQ.data?.total ?? 0;
8948
8963
  } else {
8949
- const all = localQ.data ?? [];
8950
- const q = search.trim().toLowerCase();
8951
- const filtered = all.filter((r) => {
8952
- if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
8953
- if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
8954
- if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
8955
- if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
8956
- if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
8957
- if (sessFilter && r.session !== sessFilter) return false;
8958
- if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
8959
- return true;
8960
- });
8961
- total = filtered.length;
8962
- pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
8964
+ total = localFiltered.length;
8965
+ pageRows = localFiltered.slice(page * PAGE, page * PAGE + PAGE);
8963
8966
  }
8964
8967
  const pages = Math.max(1, Math.ceil(total / PAGE));
8965
8968
  const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
@@ -9026,8 +9029,33 @@ function AuditPanel({ active: active2, focused }) {
9026
9029
  toTop();
9027
9030
  }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
9028
9031
  };
9032
+ const doExport = (kind) => {
9033
+ setMsg({ text: "exporting\u2026", level: "ok" });
9034
+ const run12 = async () => {
9035
+ const dir = join8(homedir6(), ".solongate");
9036
+ const file = join8(dir, `audit-export-${source}.jsonl`);
9037
+ let rows2;
9038
+ if (kind === "page") rows2 = pageRows;
9039
+ else if (source === "cloud") {
9040
+ const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
9041
+ rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
9042
+ } else rows2 = localFiltered;
9043
+ mkdirSync4(dir, { recursive: true });
9044
+ writeFileSync5(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
9045
+ return { n: rows2.length, file };
9046
+ };
9047
+ run12().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
9048
+ };
9029
9049
  useInput5(
9030
9050
  (input, key) => {
9051
+ if (showHelp) {
9052
+ setShowHelp(false);
9053
+ return;
9054
+ }
9055
+ if (input === "?") {
9056
+ setShowHelp(true);
9057
+ return;
9058
+ }
9031
9059
  if (view === "detail") {
9032
9060
  if (key.leftArrow || key.escape) setView("logs");
9033
9061
  else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
@@ -9106,7 +9134,7 @@ function AuditPanel({ active: active2, focused }) {
9106
9134
  if (!current) return;
9107
9135
  if (confirm?.kind !== "one" || confirm.key !== current.id) {
9108
9136
  setConfirm({ kind: "one", key: current.id });
9109
- setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
9137
+ setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
9110
9138
  return;
9111
9139
  }
9112
9140
  setConfirm(null);
@@ -9114,12 +9142,14 @@ function AuditPanel({ active: active2, focused }) {
9114
9142
  } else if (input === "X") {
9115
9143
  if (confirm?.kind !== "all") {
9116
9144
  setConfirm({ kind: "all", key: "all" });
9117
- setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
9145
+ setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
9118
9146
  return;
9119
9147
  }
9120
9148
  setConfirm(null);
9121
9149
  doDelete("all");
9122
- } else if (input === "t") setEditing("tool");
9150
+ } else if (input === "e") doExport("page");
9151
+ else if (input === "E") doExport("all");
9152
+ else if (input === "t") setEditing("tool");
9123
9153
  else if (input === "n") setEditing("agent");
9124
9154
  else if (input === "/") setEditing("search");
9125
9155
  else if (input === "c") {
@@ -9175,6 +9205,19 @@ function AuditPanel({ active: active2, focused }) {
9175
9205
  /* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
9176
9206
  /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
9177
9207
  ] });
9208
+ if (showHelp) {
9209
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9210
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "AUDIT \u2014 all keys" }),
9211
+ AUDIT_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
9212
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
9213
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
9214
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
9215
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
9216
+ ] }, k))
9217
+ ] }, group)),
9218
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "press any key to close" }) })
9219
+ ] });
9220
+ }
9178
9221
  if (view === "detail" && current) {
9179
9222
  const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
9180
9223
  const bodyW = Math.max(20, cols - 2);
@@ -9254,7 +9297,7 @@ function AuditPanel({ active: active2, focused }) {
9254
9297
  chip("status", SESS_STATUS[si] ?? "all", si !== 0),
9255
9298
  chip("search", sessSearch || "\xB7", !!sessSearch)
9256
9299
  ] }),
9257
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 f status \xB7 / search \xB7 s source \xB7 v logs \xB7 c clear" : "press \u2192 to browse" }),
9300
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 v logs \xB7 ? all keys" : "press \u2192 to browse" }),
9258
9301
  editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9259
9302
  /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
9260
9303
  /* @__PURE__ */ jsx6(
@@ -9328,7 +9371,7 @@ function AuditPanel({ active: active2, focused }) {
9328
9371
  chip("search", search || "\xB7", !!search),
9329
9372
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
9330
9373
  ] }),
9331
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 x del \xB7 X del ALL \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
9374
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 v sessions \xB7 ? all keys" : "press \u2192 to browse" }),
9332
9375
  msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
9333
9376
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9334
9377
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
@@ -9388,7 +9431,7 @@ function Detail({ label, value, color }) {
9388
9431
  /* @__PURE__ */ jsx6(Text6, { color, wrap: "truncate", children: value })
9389
9432
  ] });
9390
9433
  }
9391
- var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, sessStatus2, STATUS_DOT;
9434
+ var DECISIONS, SIGNALS, SESS_STATUS, PAGE, LOCAL_MAX_BYTES, cloudRow, AUDIT_HELP, sessStatus2, STATUS_DOT;
9392
9435
  var init_Audit = __esm({
9393
9436
  "src/tui/panels/Audit.tsx"() {
9394
9437
  "use strict";
@@ -9418,6 +9461,45 @@ var init_Audit = __esm({
9418
9461
  burst: !!e.rate_limit_burst,
9419
9462
  args: e.arguments_summary ? JSON.stringify(e.arguments_summary) : null
9420
9463
  });
9464
+ AUDIT_HELP = [
9465
+ [
9466
+ "Logs",
9467
+ [
9468
+ ["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
9469
+ ["enter", "open the FULL entry (reason + arguments)"],
9470
+ ["\u2190 \u2192", "previous / next page (500 per page, jumps to top)"],
9471
+ ["f", "decision filter: all \u2192 DENY \u2192 ALLOW"],
9472
+ ["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
9473
+ ["t / n", "tool / agent filter (type, enter done)"],
9474
+ ["/", "free-text search"],
9475
+ ["x", "delete ONLY the selected entry (press x twice)"],
9476
+ ["X", "delete ALL matched logs of the source (press X twice)"],
9477
+ ["e", "export this page \u2192 ~/.solongate/audit-export-<src>.jsonl"],
9478
+ ["E", "export ALL matched rows (cloud: up to 10k)"],
9479
+ ["c", "clear every filter (incl. session)"]
9480
+ ]
9481
+ ],
9482
+ [
9483
+ "Sessions",
9484
+ [
9485
+ ["\u2191\u2193", "select a session"],
9486
+ ["enter", "open that session's logs"],
9487
+ ["f", "status filter: all \u2192 active \u2192 idle \u2192 ended"],
9488
+ ["/", "search agent / session id"],
9489
+ ["c", "clear session filters"]
9490
+ ]
9491
+ ],
9492
+ [
9493
+ "Anywhere in Audit",
9494
+ [
9495
+ ["v", "switch logs \u2194 sessions"],
9496
+ ["s", "switch source cloud \u2194 local file"],
9497
+ ["?", "this help \xB7 any key closes"],
9498
+ ["esc", "back to the menu"]
9499
+ ]
9500
+ ],
9501
+ ["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
9502
+ ];
9421
9503
  sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
9422
9504
  STATUS_DOT = {
9423
9505
  active: { ch: "\u25CF", color: theme.ok },
@@ -10481,8 +10563,8 @@ var init_agents2 = __esm({
10481
10563
 
10482
10564
  // src/commands/doctor.ts
10483
10565
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
10484
- import { homedir as homedir6 } from "os";
10485
- import { join as join8 } from "path";
10566
+ import { homedir as homedir7 } from "os";
10567
+ import { join as join9 } from "path";
10486
10568
  async function run6(argv) {
10487
10569
  const { flags } = parse(argv);
10488
10570
  const json = flagBool(flags, "json");
@@ -10542,14 +10624,14 @@ var init_doctor = __esm({
10542
10624
  init_api_client();
10543
10625
  init_format();
10544
10626
  init_args();
10545
- LOCAL_LOG2 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
10627
+ LOCAL_LOG2 = join9(homedir7(), ".solongate", "local-logs", "solongate-audit.jsonl");
10546
10628
  }
10547
10629
  });
10548
10630
 
10549
10631
  // src/commands/watch.ts
10550
10632
  import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
10551
- import { homedir as homedir7 } from "os";
10552
- import { join as join9 } from "path";
10633
+ import { homedir as homedir8 } from "os";
10634
+ import { join as join10 } from "path";
10553
10635
  function tailLocal(file, maxBytes = 131072) {
10554
10636
  try {
10555
10637
  const size = statSync3(file).size;
@@ -10656,7 +10738,7 @@ var init_watch = __esm({
10656
10738
  init_cli_utils();
10657
10739
  init_args();
10658
10740
  init_format();
10659
- LOCAL_LOG3 = join9(homedir7(), ".solongate", "local-logs", "solongate-audit.jsonl");
10741
+ LOCAL_LOG3 = join10(homedir8(), ".solongate", "local-logs", "solongate-audit.jsonl");
10660
10742
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
10661
10743
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
10662
10744
  }
@@ -10958,9 +11040,9 @@ __export(global_install_exports, {
10958
11040
  runGlobalRestore: () => runGlobalRestore,
10959
11041
  unlockProtected: () => unlockProtected
10960
11042
  });
10961
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10962
- import { resolve as resolve4, join as join10, dirname } from "path";
10963
- import { homedir as homedir8 } from "os";
11043
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
11044
+ import { resolve as resolve4, join as join11, dirname } from "path";
11045
+ import { homedir as homedir9 } from "os";
10964
11046
  import { fileURLToPath } from "url";
10965
11047
  import { createInterface } from "readline";
10966
11048
  import { execFileSync as execFileSync2 } from "child_process";
@@ -11023,10 +11105,10 @@ function unlockFile(file) {
11023
11105
  function protectedTargets() {
11024
11106
  const p = globalPaths();
11025
11107
  return [
11026
- join10(p.hooksDir, "guard.mjs"),
11027
- join10(p.hooksDir, "audit.mjs"),
11028
- join10(p.hooksDir, "stop.mjs"),
11029
- join10(p.hooksDir, "shield.mjs"),
11108
+ join11(p.hooksDir, "guard.mjs"),
11109
+ join11(p.hooksDir, "audit.mjs"),
11110
+ join11(p.hooksDir, "stop.mjs"),
11111
+ join11(p.hooksDir, "shield.mjs"),
11030
11112
  p.configPath,
11031
11113
  p.settingsPath
11032
11114
  ];
@@ -11038,25 +11120,25 @@ function unlockProtected() {
11038
11120
  for (const f of protectedTargets()) unlockFile(f);
11039
11121
  }
11040
11122
  function globalPaths() {
11041
- const home = homedir8();
11042
- const sgDir = join10(home, ".solongate");
11043
- const hooksDir = join10(sgDir, "hooks");
11044
- const claudeDir = join10(home, ".claude");
11123
+ const home = homedir9();
11124
+ const sgDir = join11(home, ".solongate");
11125
+ const hooksDir = join11(sgDir, "hooks");
11126
+ const claudeDir = join11(home, ".claude");
11045
11127
  return {
11046
11128
  home,
11047
11129
  sgDir,
11048
11130
  hooksDir,
11049
11131
  claudeDir,
11050
- settingsPath: join10(claudeDir, "settings.json"),
11051
- backupPath: join10(claudeDir, "settings.solongate.bak"),
11052
- configPath: join10(sgDir, "cloud-guard.json")
11132
+ settingsPath: join11(claudeDir, "settings.json"),
11133
+ backupPath: join11(claudeDir, "settings.solongate.bak"),
11134
+ configPath: join11(sgDir, "cloud-guard.json")
11053
11135
  };
11054
11136
  }
11055
11137
  function readHook(filename) {
11056
- return readFileSync8(join10(HOOKS_DIR, filename), "utf-8");
11138
+ return readFileSync8(join11(HOOKS_DIR, filename), "utf-8");
11057
11139
  }
11058
11140
  function readGuard() {
11059
- const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
11141
+ const bundled = join11(HOOKS_DIR, "guard.bundled.mjs");
11060
11142
  return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
11061
11143
  }
11062
11144
  function ask(question) {
@@ -11071,13 +11153,13 @@ function runGlobalRestore() {
11071
11153
  unlockProtected();
11072
11154
  removeClaudeShim();
11073
11155
  if (existsSync6(p.backupPath)) {
11074
- writeFileSync5(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
11156
+ writeFileSync6(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
11075
11157
  console.log(` Restored ${p.settingsPath} from backup.`);
11076
11158
  } else if (existsSync6(p.settingsPath)) {
11077
11159
  try {
11078
11160
  const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
11079
11161
  delete s.hooks;
11080
- writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
11162
+ writeFileSync6(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
11081
11163
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
11082
11164
  } catch {
11083
11165
  }
@@ -11111,7 +11193,7 @@ function shimTargets() {
11111
11193
  return [];
11112
11194
  }
11113
11195
  }
11114
- return [".bashrc", ".zshrc", ".profile"].map((f) => join10(homedir8(), f)).filter((f) => existsSync6(f));
11196
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join11(homedir9(), f)).filter((f) => existsSync6(f));
11115
11197
  }
11116
11198
  function writeShimBlock(file, block2) {
11117
11199
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
@@ -11121,8 +11203,8 @@ function writeShimBlock(file, block2) {
11121
11203
  if (content.length && !content.endsWith("\n")) content += "\n";
11122
11204
  content += block2 + "\n";
11123
11205
  }
11124
- mkdirSync4(dirname(file), { recursive: true });
11125
- writeFileSync5(file, content);
11206
+ mkdirSync5(dirname(file), { recursive: true });
11207
+ writeFileSync6(file, content);
11126
11208
  }
11127
11209
  function installClaudeShim(shieldPath) {
11128
11210
  const real = resolveRealClaude();
@@ -11173,22 +11255,22 @@ async function runGlobalInstall(opts = {}) {
11173
11255
  process.exit(1);
11174
11256
  }
11175
11257
  const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
11176
- mkdirSync4(p.hooksDir, { recursive: true });
11177
- mkdirSync4(p.claudeDir, { recursive: true });
11258
+ mkdirSync5(p.hooksDir, { recursive: true });
11259
+ mkdirSync5(p.claudeDir, { recursive: true });
11178
11260
  unlockProtected();
11179
- writeFileSync5(join10(p.hooksDir, "guard.mjs"), readGuard());
11180
- writeFileSync5(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11181
- writeFileSync5(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11182
- writeFileSync5(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
11261
+ writeFileSync6(join11(p.hooksDir, "guard.mjs"), readGuard());
11262
+ writeFileSync6(join11(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11263
+ writeFileSync6(join11(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11264
+ writeFileSync6(join11(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
11183
11265
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
11184
- installClaudeShim(join10(p.hooksDir, "shield.mjs"));
11185
- writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11266
+ installClaudeShim(join11(p.hooksDir, "shield.mjs"));
11267
+ writeFileSync6(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11186
11268
  console.log(` Wrote ${p.configPath}`);
11187
11269
  let existing = {};
11188
11270
  if (existsSync6(p.settingsPath)) {
11189
11271
  const raw = readFileSync8(p.settingsPath, "utf-8");
11190
11272
  if (!existsSync6(p.backupPath)) {
11191
- writeFileSync5(p.backupPath, raw);
11273
+ writeFileSync6(p.backupPath, raw);
11192
11274
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
11193
11275
  }
11194
11276
  try {
@@ -11197,9 +11279,9 @@ async function runGlobalInstall(opts = {}) {
11197
11279
  existing = {};
11198
11280
  }
11199
11281
  }
11200
- const guardAbs = join10(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
11201
- const auditAbs = join10(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
11202
- const stopAbs = join10(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
11282
+ const guardAbs = join11(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
11283
+ const auditAbs = join11(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
11284
+ const stopAbs = join11(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
11203
11285
  const nodeBin = process.execPath.replace(/\\/g, "/");
11204
11286
  const call = process.platform === "win32" ? "& " : "";
11205
11287
  const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
@@ -11211,7 +11293,7 @@ async function runGlobalInstall(opts = {}) {
11211
11293
  Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
11212
11294
  }
11213
11295
  };
11214
- writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11296
+ writeFileSync6(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11215
11297
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
11216
11298
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
11217
11299
  lockProtected();
@@ -11393,9 +11475,9 @@ import { spawn as spawn3 } from "child_process";
11393
11475
  import { URL as URL2 } from "url";
11394
11476
  import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
11395
11477
  import { resolve as resolve5 } from "path";
11396
- import { homedir as homedir9 } from "os";
11478
+ import { homedir as homedir10 } from "os";
11397
11479
  function findCacheFile() {
11398
- const dir = resolve5(homedir9(), ".solongate");
11480
+ const dir = resolve5(homedir10(), ".solongate");
11399
11481
  const envSel = process.env.SOLONGATE_AGENT_ID;
11400
11482
  if (envSel) {
11401
11483
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
@@ -11710,8 +11792,8 @@ __export(logs_server_exports, {
11710
11792
  });
11711
11793
  import { createServer as createServer2 } from "http";
11712
11794
  import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
11713
- import { resolve as resolve6, join as join11, isAbsolute } from "path";
11714
- import { homedir as homedir10 } from "os";
11795
+ import { resolve as resolve6, join as join12, isAbsolute } from "path";
11796
+ import { homedir as homedir11 } from "os";
11715
11797
  import { readdirSync as readdirSync2 } from "fs";
11716
11798
  function allowedOrigins() {
11717
11799
  const base = [
@@ -11728,15 +11810,15 @@ function resolveLocalLogDir(rawPath) {
11728
11810
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
11729
11811
  if (!dir) return null;
11730
11812
  if (isAbsolute(dir)) return dir;
11731
- return resolve6(homedir10(), ".solongate", "local-logs");
11813
+ return resolve6(homedir11(), ".solongate", "local-logs");
11732
11814
  }
11733
11815
  async function findLogDir() {
11734
- const base = resolve6(homedir10(), ".solongate");
11816
+ const base = resolve6(homedir11(), ".solongate");
11735
11817
  try {
11736
11818
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
11737
11819
  for (const f of files) {
11738
11820
  try {
11739
- const c2 = JSON.parse(readFileSync10(join11(base, f), "utf-8"));
11821
+ const c2 = JSON.parse(readFileSync10(join12(base, f), "utf-8"));
11740
11822
  const p = c2?.security?.localLogs?.path;
11741
11823
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
11742
11824
  } catch {
@@ -11745,7 +11827,7 @@ async function findLogDir() {
11745
11827
  } catch {
11746
11828
  }
11747
11829
  try {
11748
- const cfgRaw = readFileSync10(join11(base, "cloud-guard.json"), "utf-8");
11830
+ const cfgRaw = readFileSync10(join12(base, "cloud-guard.json"), "utf-8");
11749
11831
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
11750
11832
  if (apiKey) {
11751
11833
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -11774,7 +11856,7 @@ function setCors(req, res) {
11774
11856
  }
11775
11857
  function fileInfo(dir) {
11776
11858
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
11777
- const file = join11(dir, LOG_FILENAME);
11859
+ const file = join12(dir, LOG_FILENAME);
11778
11860
  try {
11779
11861
  const st = statSync5(file);
11780
11862
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -11887,7 +11969,7 @@ var init_logs_server = __esm({
11887
11969
 
11888
11970
  // src/inject.ts
11889
11971
  var inject_exports = {};
11890
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync8, copyFileSync } from "fs";
11972
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync8, copyFileSync } from "fs";
11891
11973
  import { resolve as resolve7 } from "path";
11892
11974
  import { execSync } from "child_process";
11893
11975
  function parseInjectArgs(argv) {
@@ -12195,7 +12277,7 @@ async function main2() {
12195
12277
  log3("");
12196
12278
  log3(` Backup: ${backupPath}`);
12197
12279
  }
12198
- writeFileSync6(entryFile, result.modified);
12280
+ writeFileSync7(entryFile, result.modified);
12199
12281
  log3("");
12200
12282
  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");
12201
12283
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -12224,8 +12306,8 @@ var init_inject = __esm({
12224
12306
 
12225
12307
  // src/create.ts
12226
12308
  var create_exports = {};
12227
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "fs";
12228
- import { resolve as resolve8, join as join12 } from "path";
12309
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, existsSync as existsSync9 } from "fs";
12310
+ import { resolve as resolve8, join as join13 } from "path";
12229
12311
  import { execSync as execSync2 } from "child_process";
12230
12312
  function withSpinner(message, fn) {
12231
12313
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -12304,8 +12386,8 @@ EXAMPLES
12304
12386
  `);
12305
12387
  }
12306
12388
  function createProject(dir, name, _policy) {
12307
- writeFileSync7(
12308
- join12(dir, "package.json"),
12389
+ writeFileSync8(
12390
+ join13(dir, "package.json"),
12309
12391
  JSON.stringify(
12310
12392
  {
12311
12393
  name,
@@ -12334,8 +12416,8 @@ function createProject(dir, name, _policy) {
12334
12416
  2
12335
12417
  ) + "\n"
12336
12418
  );
12337
- writeFileSync7(
12338
- join12(dir, "tsconfig.json"),
12419
+ writeFileSync8(
12420
+ join13(dir, "tsconfig.json"),
12339
12421
  JSON.stringify(
12340
12422
  {
12341
12423
  compilerOptions: {
@@ -12355,9 +12437,9 @@ function createProject(dir, name, _policy) {
12355
12437
  2
12356
12438
  ) + "\n"
12357
12439
  );
12358
- mkdirSync5(join12(dir, "src"), { recursive: true });
12359
- writeFileSync7(
12360
- join12(dir, "src", "index.ts"),
12440
+ mkdirSync6(join13(dir, "src"), { recursive: true });
12441
+ writeFileSync8(
12442
+ join13(dir, "src", "index.ts"),
12361
12443
  `#!/usr/bin/env node
12362
12444
 
12363
12445
  console.log = (...args: unknown[]) => {
@@ -12398,8 +12480,8 @@ console.log('');
12398
12480
  console.log('Press Ctrl+C to stop.');
12399
12481
  `
12400
12482
  );
12401
- writeFileSync7(
12402
- join12(dir, ".mcp.json"),
12483
+ writeFileSync8(
12484
+ join13(dir, ".mcp.json"),
12403
12485
  JSON.stringify(
12404
12486
  {
12405
12487
  mcpServers: {
@@ -12416,13 +12498,13 @@ console.log('Press Ctrl+C to stop.');
12416
12498
  2
12417
12499
  ) + "\n"
12418
12500
  );
12419
- writeFileSync7(
12420
- join12(dir, ".env"),
12501
+ writeFileSync8(
12502
+ join13(dir, ".env"),
12421
12503
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
12422
12504
  `
12423
12505
  );
12424
- writeFileSync7(
12425
- join12(dir, ".gitignore"),
12506
+ writeFileSync8(
12507
+ join13(dir, ".gitignore"),
12426
12508
  `node_modules/
12427
12509
  dist/
12428
12510
  *.solongate-backup
@@ -12441,7 +12523,7 @@ async function main3() {
12441
12523
  process.exit(1);
12442
12524
  }
12443
12525
  withSpinner(`Setting up ${opts.name}...`, () => {
12444
- mkdirSync5(dir, { recursive: true });
12526
+ mkdirSync6(dir, { recursive: true });
12445
12527
  createProject(dir, opts.name, opts.policy);
12446
12528
  });
12447
12529
  if (!opts.noInstall) {
@@ -12515,7 +12597,7 @@ var init_create = __esm({
12515
12597
 
12516
12598
  // src/pull-push.ts
12517
12599
  var pull_push_exports = {};
12518
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "fs";
12600
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "fs";
12519
12601
  import { resolve as resolve9 } from "path";
12520
12602
  function loadEnv() {
12521
12603
  if (process.env.SOLONGATE_API_KEY) return;
@@ -12710,7 +12792,7 @@ async function pull(apiKey, file, policyId) {
12710
12792
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
12711
12793
  const { id: _id, ...policyWithoutId } = policy;
12712
12794
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
12713
- writeFileSync8(file, json, "utf-8");
12795
+ writeFileSync9(file, json, "utf-8");
12714
12796
  log5("");
12715
12797
  log5(green2(" Saved to: ") + file);
12716
12798
  log5(` ${dim2("Name:")} ${policy.name}`);
package/dist/tui/index.js CHANGED
@@ -2214,6 +2214,9 @@ function RegexTest({ re }) {
2214
2214
  // src/tui/panels/Audit.tsx
2215
2215
  import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
2216
2216
  import TextInput4 from "ink-text-input";
2217
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
2218
+ import { homedir as homedir5 } from "os";
2219
+ import { join as join5 } from "path";
2217
2220
  import { useState as useState6 } from "react";
2218
2221
  import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2219
2222
  var DECISIONS = [void 0, "DENY", "ALLOW"];
@@ -2255,6 +2258,45 @@ function loadLocalRows() {
2255
2258
  args: j.arguments ? JSON.stringify(j.arguments) : null
2256
2259
  })).sort((a, b) => b.at - a.at);
2257
2260
  }
2261
+ var AUDIT_HELP = [
2262
+ [
2263
+ "Logs",
2264
+ [
2265
+ ["\u2191\u2193 / PgUp PgDn", "select a row (window follows)"],
2266
+ ["enter", "open the FULL entry (reason + arguments)"],
2267
+ ["\u2190 \u2192", "previous / next page (500 per page, jumps to top)"],
2268
+ ["f", "decision filter: all \u2192 DENY \u2192 ALLOW"],
2269
+ ["g", "signal filter: all \u2192 dlp \u2192 ratelimit"],
2270
+ ["t / n", "tool / agent filter (type, enter done)"],
2271
+ ["/", "free-text search"],
2272
+ ["x", "delete ONLY the selected entry (press x twice)"],
2273
+ ["X", "delete ALL matched logs of the source (press X twice)"],
2274
+ ["e", "export this page \u2192 ~/.solongate/audit-export-<src>.jsonl"],
2275
+ ["E", "export ALL matched rows (cloud: up to 10k)"],
2276
+ ["c", "clear every filter (incl. session)"]
2277
+ ]
2278
+ ],
2279
+ [
2280
+ "Sessions",
2281
+ [
2282
+ ["\u2191\u2193", "select a session"],
2283
+ ["enter", "open that session's logs"],
2284
+ ["f", "status filter: all \u2192 active \u2192 idle \u2192 ended"],
2285
+ ["/", "search agent / session id"],
2286
+ ["c", "clear session filters"]
2287
+ ]
2288
+ ],
2289
+ [
2290
+ "Anywhere in Audit",
2291
+ [
2292
+ ["v", "switch logs \u2194 sessions"],
2293
+ ["s", "switch source cloud \u2194 local file"],
2294
+ ["?", "this help \xB7 any key closes"],
2295
+ ["esc", "back to the menu"]
2296
+ ]
2297
+ ],
2298
+ ["Entry detail", [["\u2191\u2193 / PgUp PgDn", "scroll the arguments"], ["\u2190 / esc", "back to the list"]]]
2299
+ ];
2258
2300
  var sessStatus2 = (lastAt) => Date.now() - lastAt < 6e4 ? "active" : Date.now() - lastAt < 3e5 ? "idle" : "ended";
2259
2301
  var STATUS_DOT = {
2260
2302
  active: { ch: "\u25CF", color: theme.ok },
@@ -2280,6 +2322,7 @@ function AuditPanel({ active: active2, focused }) {
2280
2322
  const [sessSel, setSessSel] = useState6(0);
2281
2323
  const [confirm, setConfirm] = useState6(null);
2282
2324
  const [msg, setMsg] = useState6(null);
2325
+ const [showHelp, setShowHelp] = useState6(false);
2283
2326
  const toTop = () => setSel(0);
2284
2327
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
2285
2328
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
@@ -2304,26 +2347,25 @@ function AuditPanel({ active: active2, focused }) {
2304
2347
  usePoll(cloudQ.reloadQuiet, 6e3, active2 && source === "cloud" && view === "logs" && !editing && page === 0);
2305
2348
  const localQ = useLoader(() => source === "local" ? Promise.resolve(loadLocalRows()) : Promise.resolve(null), [source]);
2306
2349
  usePoll(localQ.reloadQuiet, 6e3, active2 && source === "local" && view === "logs" && !editing && page === 0);
2350
+ const q = search.trim().toLowerCase();
2351
+ const localFiltered = (localQ.data ?? []).filter((r) => {
2352
+ if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
2353
+ if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
2354
+ if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
2355
+ if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
2356
+ if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
2357
+ if (sessFilter && r.session !== sessFilter) return false;
2358
+ if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
2359
+ return true;
2360
+ });
2307
2361
  let pageRows = [];
2308
2362
  let total = 0;
2309
2363
  if (source === "cloud") {
2310
2364
  pageRows = (cloudQ.data?.entries ?? []).map(cloudRow).sort((a, b) => b.at - a.at);
2311
2365
  total = cloudQ.data?.total ?? 0;
2312
2366
  } else {
2313
- const all = localQ.data ?? [];
2314
- const q = search.trim().toLowerCase();
2315
- const filtered = all.filter((r) => {
2316
- if (DECISIONS[di] && r.decision !== DECISIONS[di]) return false;
2317
- if (SIGNALS[gi] === "dlp" && r.dlp.length === 0) return false;
2318
- if (SIGNALS[gi] === "ratelimit" && !r.burst) return false;
2319
- if (tool && !r.tool.toLowerCase().includes(tool.toLowerCase())) return false;
2320
- if (agent && (r.agent ?? "").toLowerCase() !== agent.toLowerCase()) return false;
2321
- if (sessFilter && r.session !== sessFilter) return false;
2322
- if (q && !`${r.tool} ${r.agent ?? ""} ${r.reason ?? ""} ${r.args ?? ""}`.toLowerCase().includes(q)) return false;
2323
- return true;
2324
- });
2325
- total = filtered.length;
2326
- pageRows = filtered.slice(page * PAGE, page * PAGE + PAGE);
2367
+ total = localFiltered.length;
2368
+ pageRows = localFiltered.slice(page * PAGE, page * PAGE + PAGE);
2327
2369
  }
2328
2370
  const pages = Math.max(1, Math.ceil(total / PAGE));
2329
2371
  const current = pageRows[Math.min(sel, Math.max(0, pageRows.length - 1))];
@@ -2390,8 +2432,33 @@ function AuditPanel({ active: active2, focused }) {
2390
2432
  toTop();
2391
2433
  }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
2392
2434
  };
2435
+ const doExport = (kind) => {
2436
+ setMsg({ text: "exporting\u2026", level: "ok" });
2437
+ const run = async () => {
2438
+ const dir = join5(homedir5(), ".solongate");
2439
+ const file = join5(dir, `audit-export-${source}.jsonl`);
2440
+ let rows2;
2441
+ if (kind === "page") rows2 = pageRows;
2442
+ else if (source === "cloud") {
2443
+ const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
2444
+ rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
2445
+ } else rows2 = localFiltered;
2446
+ mkdirSync2(dir, { recursive: true });
2447
+ writeFileSync3(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
2448
+ return { n: rows2.length, file };
2449
+ };
2450
+ run().then(({ n, file }) => setMsg({ text: `\u2713 exported ${n} rows \u2192 ${file}`, level: "ok" })).catch((e) => setMsg({ text: "\u2717 export failed: " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
2451
+ };
2393
2452
  useInput5(
2394
2453
  (input, key) => {
2454
+ if (showHelp) {
2455
+ setShowHelp(false);
2456
+ return;
2457
+ }
2458
+ if (input === "?") {
2459
+ setShowHelp(true);
2460
+ return;
2461
+ }
2395
2462
  if (view === "detail") {
2396
2463
  if (key.leftArrow || key.escape) setView("logs");
2397
2464
  else if (key.upArrow) setDetailScroll((n) => Math.max(0, n - 1));
@@ -2470,7 +2537,7 @@ function AuditPanel({ active: active2, focused }) {
2470
2537
  if (!current) return;
2471
2538
  if (confirm?.kind !== "one" || confirm.key !== current.id) {
2472
2539
  setConfirm({ kind: "one", key: current.id });
2473
- setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
2540
+ setMsg({ text: `x = delete ONLY the selected entry: ${current.decision} ${current.tool} (${ago(current.at)} ago) \u2014 press x again`, level: "bad" });
2474
2541
  return;
2475
2542
  }
2476
2543
  setConfirm(null);
@@ -2478,12 +2545,14 @@ function AuditPanel({ active: active2, focused }) {
2478
2545
  } else if (input === "X") {
2479
2546
  if (confirm?.kind !== "all") {
2480
2547
  setConfirm({ kind: "all", key: "all" });
2481
- setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
2548
+ setMsg({ text: `\u26A0 X = delete ALL ${source} logs \u2014 every one of the ${total} matched entries! press X again`, level: "bad" });
2482
2549
  return;
2483
2550
  }
2484
2551
  setConfirm(null);
2485
2552
  doDelete("all");
2486
- } else if (input === "t") setEditing("tool");
2553
+ } else if (input === "e") doExport("page");
2554
+ else if (input === "E") doExport("all");
2555
+ else if (input === "t") setEditing("tool");
2487
2556
  else if (input === "n") setEditing("agent");
2488
2557
  else if (input === "/") setEditing("search");
2489
2558
  else if (input === "c") {
@@ -2539,6 +2608,19 @@ function AuditPanel({ active: active2, focused }) {
2539
2608
  /* @__PURE__ */ jsx6(Text6, { color: source === "local" ? theme.ok : "#4f6db8", bold: true, children: source }),
2540
2609
  /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " (s) " })
2541
2610
  ] });
2611
+ if (showHelp) {
2612
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2613
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accentBright, children: "AUDIT \u2014 all keys" }),
2614
+ AUDIT_HELP.map(([group, keys]) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
2615
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.accent, children: group }),
2616
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs6(Text6, { wrap: "truncate", children: [
2617
+ /* @__PURE__ */ jsx6(Text6, { color: theme.accentBright, children: (" " + k).padEnd(20) }),
2618
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: desc })
2619
+ ] }, k))
2620
+ ] }, group)),
2621
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "press any key to close" }) })
2622
+ ] });
2623
+ }
2542
2624
  if (view === "detail" && current) {
2543
2625
  const sessionCalls = source === "cloud" ? (sessionQ.data?.entries ?? []).map(cloudRow) : localAll.filter((r) => r.session && r.session === current.session).slice(0, 40);
2544
2626
  const bodyW = Math.max(20, cols - 2);
@@ -2618,7 +2700,7 @@ function AuditPanel({ active: active2, focused }) {
2618
2700
  chip("status", SESS_STATUS[si] ?? "all", si !== 0),
2619
2701
  chip("search", sessSearch || "\xB7", !!sessSearch)
2620
2702
  ] }),
2621
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 f status \xB7 / search \xB7 s source \xB7 v logs \xB7 c clear" : "press \u2192 to browse" }),
2703
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter \u2192 session logs \xB7 v logs \xB7 ? all keys" : "press \u2192 to browse" }),
2622
2704
  editing === "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
2623
2705
  /* @__PURE__ */ jsx6(Text6, { color: theme.warn, children: "search: " }),
2624
2706
  /* @__PURE__ */ jsx6(
@@ -2692,7 +2774,7 @@ function AuditPanel({ active: active2, focused }) {
2692
2774
  chip("search", search || "\xB7", !!search),
2693
2775
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
2694
2776
  ] }),
2695
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 x del \xB7 X del ALL \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
2777
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 v sessions \xB7 ? all keys" : "press \u2192 to browse" }),
2696
2778
  msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
2697
2779
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
2698
2780
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.26",
3
+ "version": "0.81.28",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {