@solongate/proxy 0.81.25 → 0.81.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,19 @@ export interface AuditQuery {
12
12
  signal?: 'dlp' | 'ratelimit';
13
13
  }
14
14
  export declare function list(query?: AuditQuery): Promise<AuditList>;
15
+ export declare function remove(ids: string[]): Promise<{
16
+ deleted: {
17
+ logs: number;
18
+ agents: number;
19
+ };
20
+ }>;
21
+ /** Deletes EVERY audit log of the project (agents/sessions are kept). */
22
+ export declare function removeAll(): Promise<{
23
+ deleted: {
24
+ logs: number;
25
+ agents: number;
26
+ };
27
+ }>;
15
28
  export declare function whitelist(id: string, scope?: 'exact' | 'tool'): Promise<{
16
29
  ok: true;
17
30
  deduped: boolean;
@@ -291,11 +291,19 @@ var audit_exports = {};
291
291
  __export(audit_exports, {
292
292
  block: () => block,
293
293
  list: () => list2,
294
+ remove: () => remove2,
295
+ removeAll: () => removeAll,
294
296
  whitelist: () => whitelist
295
297
  });
296
298
  function list2(query = {}) {
297
299
  return request("GET", "/audit-logs", { query });
298
300
  }
301
+ function remove2(ids) {
302
+ return request("DELETE", "/audit-logs", { body: { ids } });
303
+ }
304
+ function removeAll() {
305
+ return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
306
+ }
299
307
  function whitelist(id, scope = "exact") {
300
308
  return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
301
309
  }
package/dist/index.js CHANGED
@@ -6845,7 +6845,7 @@ var init_components = __esm({
6845
6845
  });
6846
6846
 
6847
6847
  // src/tui/local-log.ts
6848
- import { closeSync, openSync, readSync, statSync } from "fs";
6848
+ import { closeSync, openSync, readFileSync as readFileSync6, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
6849
6849
  import { homedir as homedir4 } from "os";
6850
6850
  import { join as join6 } from "path";
6851
6851
  function tailLines(file, maxBytes = 131072) {
@@ -6863,6 +6863,39 @@ function tailLines(file, maxBytes = 131072) {
6863
6863
  return [];
6864
6864
  }
6865
6865
  }
6866
+ function deleteLocalEntry(at, tool, session) {
6867
+ try {
6868
+ const lines = readFileSync6(LOCAL_LOG, "utf-8").split("\n");
6869
+ let removed = 0;
6870
+ const kept = lines.filter((line) => {
6871
+ if (!line.trim()) return false;
6872
+ if (removed) return true;
6873
+ try {
6874
+ const j = JSON.parse(line);
6875
+ const hit = Date.parse(j.ts ?? "") === at && (j.tool ?? "?") === tool && (!session || j.session_id === session);
6876
+ if (hit) {
6877
+ removed++;
6878
+ return false;
6879
+ }
6880
+ } catch {
6881
+ }
6882
+ return true;
6883
+ });
6884
+ if (removed) writeFileSync3(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
6885
+ return removed;
6886
+ } catch {
6887
+ return 0;
6888
+ }
6889
+ }
6890
+ function clearLocalLog() {
6891
+ try {
6892
+ const n = readFileSync6(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
6893
+ writeFileSync3(LOCAL_LOG, "");
6894
+ return n;
6895
+ } catch {
6896
+ return 0;
6897
+ }
6898
+ }
6866
6899
  function parseLocalLines(lines) {
6867
6900
  const out2 = [];
6868
6901
  for (const line of lines) {
@@ -7062,11 +7095,19 @@ var audit_exports = {};
7062
7095
  __export(audit_exports, {
7063
7096
  block: () => block,
7064
7097
  list: () => list2,
7098
+ remove: () => remove2,
7099
+ removeAll: () => removeAll,
7065
7100
  whitelist: () => whitelist
7066
7101
  });
7067
7102
  function list2(query = {}) {
7068
7103
  return request("GET", "/audit-logs", { query });
7069
7104
  }
7105
+ function remove2(ids) {
7106
+ return request("DELETE", "/audit-logs", { body: { ids } });
7107
+ }
7108
+ function removeAll() {
7109
+ return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
7110
+ }
7070
7111
  function whitelist(id, scope = "exact") {
7071
7112
  return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
7072
7113
  }
@@ -7267,7 +7308,7 @@ var init_hooks = __esm({
7267
7308
  // src/tui/panels/Live.tsx
7268
7309
  import { Box as Box2, Text as Text2, useInput } from "ink";
7269
7310
  import TextInput from "ink-text-input";
7270
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
7311
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
7271
7312
  import { homedir as homedir5 } from "os";
7272
7313
  import { join as join7 } from "path";
7273
7314
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
@@ -7818,7 +7859,7 @@ function LivePanel({ active: active2 }) {
7818
7859
  const file = join7(homedir5(), ".solongate", "live-export.jsonl");
7819
7860
  try {
7820
7861
  mkdirSync3(join7(homedir5(), ".solongate"), { recursive: true });
7821
- writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7862
+ writeFileSync4(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7822
7863
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7823
7864
  } catch (err2) {
7824
7865
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -8873,6 +8914,8 @@ function AuditPanel({ active: active2, focused }) {
8873
8914
  const [si, setSi] = useState6(0);
8874
8915
  const [sessSearch, setSessSearch] = useState6("");
8875
8916
  const [sessSel, setSessSel] = useState6(0);
8917
+ const [confirm, setConfirm] = useState6(null);
8918
+ const [msg, setMsg] = useState6(null);
8876
8919
  const toTop = () => setSel(0);
8877
8920
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
8878
8921
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
@@ -8958,6 +9001,31 @@ function AuditPanel({ active: active2, focused }) {
8958
9001
  () => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
8959
9002
  [view, current?.session, source]
8960
9003
  );
9004
+ const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
9005
+ const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
9006
+ const doDelete = (kind) => {
9007
+ setMsg({ text: "deleting\u2026", level: "ok" });
9008
+ const run12 = async () => {
9009
+ if (source === "cloud") {
9010
+ if (kind === "one") {
9011
+ if (!current) throw new Error("nothing selected");
9012
+ await api.audit.remove([current.id]);
9013
+ } else {
9014
+ await api.audit.removeAll();
9015
+ }
9016
+ cloudQ.reload();
9017
+ statsQ.reloadQuiet();
9018
+ } else {
9019
+ const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
9020
+ if (!n) throw new Error("entry not found in the local file");
9021
+ localQ.reload();
9022
+ }
9023
+ };
9024
+ run12().then(() => {
9025
+ setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
9026
+ toTop();
9027
+ }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
9028
+ };
8961
9029
  useInput5(
8962
9030
  (input, key) => {
8963
9031
  if (view === "detail") {
@@ -8968,6 +9036,11 @@ function AuditPanel({ active: active2, focused }) {
8968
9036
  else if (key.pageDown) setDetailScroll((n) => n + 10);
8969
9037
  return;
8970
9038
  }
9039
+ if (view === "logs" ? logsLoading : sessLoading) return;
9040
+ if (confirm && input !== "x" && input !== "X") {
9041
+ setConfirm(null);
9042
+ setMsg(null);
9043
+ }
8971
9044
  if (input === "s") {
8972
9045
  setSource((s) => s === "cloud" ? "local" : "cloud");
8973
9046
  setPage(0);
@@ -9029,6 +9102,23 @@ function AuditPanel({ active: active2, focused }) {
9029
9102
  setGi((n) => (n + 1) % SIGNALS.length);
9030
9103
  setPage(0);
9031
9104
  toTop();
9105
+ } else if (input === "x") {
9106
+ if (!current) return;
9107
+ if (confirm?.kind !== "one" || confirm.key !== current.id) {
9108
+ setConfirm({ kind: "one", key: current.id });
9109
+ setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
9110
+ return;
9111
+ }
9112
+ setConfirm(null);
9113
+ doDelete("one");
9114
+ } else if (input === "X") {
9115
+ if (confirm?.kind !== "all") {
9116
+ setConfirm({ kind: "all", key: "all" });
9117
+ setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
9118
+ return;
9119
+ }
9120
+ setConfirm(null);
9121
+ doDelete("all");
9032
9122
  } else if (input === "t") setEditing("tool");
9033
9123
  else if (input === "n") setEditing("agent");
9034
9124
  else if (input === "/") setEditing("search");
@@ -9155,8 +9245,7 @@ function AuditPanel({ active: active2, focused }) {
9155
9245
  const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
9156
9246
  const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
9157
9247
  const win = sessionsFiltered.slice(start2, start2 + listRows2);
9158
- const loading2 = source === "cloud" ? agentsQ.loading : localQ.loading;
9159
- return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9248
+ return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9160
9249
  strip,
9161
9250
  /* @__PURE__ */ jsxs6(Box6, { children: [
9162
9251
  srcChip,
@@ -9219,15 +9308,14 @@ function AuditPanel({ active: active2, focused }) {
9219
9308
  ] }) : null
9220
9309
  ] }) });
9221
9310
  }
9222
- const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
9311
+ const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
9223
9312
  const listRows = Math.max(4, rows - headerRows);
9224
9313
  const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
9225
9314
  const maxStart = Math.max(0, pageRows.length - listRows);
9226
9315
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
9227
9316
  const windowed = pageRows.slice(start, start + listRows);
9228
9317
  const reasonW = Math.min(60, Math.max(12, cols - 67));
9229
- const loading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
9230
- return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9318
+ return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
9231
9319
  strip,
9232
9320
  /* @__PURE__ */ jsxs6(Box6, { children: [
9233
9321
  srcChip,
@@ -9240,7 +9328,8 @@ function AuditPanel({ active: active2, focused }) {
9240
9328
  chip("search", search || "\xB7", !!search),
9241
9329
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
9242
9330
  ] }),
9243
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
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" }),
9332
+ msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate2(msg.text, cols) }) : null,
9244
9333
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
9245
9334
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
9246
9335
  editing,
@@ -9663,7 +9752,7 @@ var init_App = __esm({
9663
9752
  ["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)"]]],
9664
9753
  ["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
9665
9754
  ["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"]]],
9666
- ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
9755
+ ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
9667
9756
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
9668
9757
  ];
9669
9758
  }
@@ -9801,7 +9890,7 @@ var init_args = __esm({
9801
9890
  });
9802
9891
 
9803
9892
  // src/commands/policy.ts
9804
- import { readFileSync as readFileSync6 } from "fs";
9893
+ import { readFileSync as readFileSync7 } from "fs";
9805
9894
  async function run(argv) {
9806
9895
  const { positionals, flags } = parse(argv);
9807
9896
  const sub = positionals[0];
@@ -9976,7 +10065,7 @@ function printRules(rules) {
9976
10065
  }
9977
10066
  async function resolveRules(target) {
9978
10067
  if (target.endsWith(".json")) {
9979
- const parsed = JSON.parse(readFileSync6(target, "utf-8"));
10068
+ const parsed = JSON.parse(readFileSync7(target, "utf-8"));
9980
10069
  return parsed.rules ?? [];
9981
10070
  }
9982
10071
  const p = await api.policies.get(target);
@@ -10869,7 +10958,7 @@ __export(global_install_exports, {
10869
10958
  runGlobalRestore: () => runGlobalRestore,
10870
10959
  unlockProtected: () => unlockProtected
10871
10960
  });
10872
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10961
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10873
10962
  import { resolve as resolve4, join as join10, dirname } from "path";
10874
10963
  import { homedir as homedir8 } from "os";
10875
10964
  import { fileURLToPath } from "url";
@@ -10964,11 +11053,11 @@ function globalPaths() {
10964
11053
  };
10965
11054
  }
10966
11055
  function readHook(filename) {
10967
- return readFileSync7(join10(HOOKS_DIR, filename), "utf-8");
11056
+ return readFileSync8(join10(HOOKS_DIR, filename), "utf-8");
10968
11057
  }
10969
11058
  function readGuard() {
10970
11059
  const bundled = join10(HOOKS_DIR, "guard.bundled.mjs");
10971
- return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
11060
+ return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
10972
11061
  }
10973
11062
  function ask(question) {
10974
11063
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -10982,13 +11071,13 @@ function runGlobalRestore() {
10982
11071
  unlockProtected();
10983
11072
  removeClaudeShim();
10984
11073
  if (existsSync6(p.backupPath)) {
10985
- writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
11074
+ writeFileSync5(p.settingsPath, readFileSync8(p.backupPath, "utf-8"));
10986
11075
  console.log(` Restored ${p.settingsPath} from backup.`);
10987
11076
  } else if (existsSync6(p.settingsPath)) {
10988
11077
  try {
10989
- const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
11078
+ const s = JSON.parse(readFileSync8(p.settingsPath, "utf-8"));
10990
11079
  delete s.hooks;
10991
- writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
11080
+ writeFileSync5(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10992
11081
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
10993
11082
  } catch {
10994
11083
  }
@@ -11026,14 +11115,14 @@ function shimTargets() {
11026
11115
  }
11027
11116
  function writeShimBlock(file, block2) {
11028
11117
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
11029
- let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
11118
+ let content = existsSync6(file) ? readFileSync8(file, "utf-8") : "";
11030
11119
  content = content.replace(re, "");
11031
11120
  if (block2) {
11032
11121
  if (content.length && !content.endsWith("\n")) content += "\n";
11033
11122
  content += block2 + "\n";
11034
11123
  }
11035
11124
  mkdirSync4(dirname(file), { recursive: true });
11036
- writeFileSync4(file, content);
11125
+ writeFileSync5(file, content);
11037
11126
  }
11038
11127
  function installClaudeShim(shieldPath) {
11039
11128
  const real = resolveRealClaude();
@@ -11071,7 +11160,7 @@ async function runGlobalInstall(opts = {}) {
11071
11160
  let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
11072
11161
  if (!apiKey || apiKey === "sg_live_your_key_here") {
11073
11162
  try {
11074
- const cfg = JSON.parse(readFileSync7(p.configPath, "utf-8"));
11163
+ const cfg = JSON.parse(readFileSync8(p.configPath, "utf-8"));
11075
11164
  if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
11076
11165
  } catch {
11077
11166
  }
@@ -11087,19 +11176,19 @@ async function runGlobalInstall(opts = {}) {
11087
11176
  mkdirSync4(p.hooksDir, { recursive: true });
11088
11177
  mkdirSync4(p.claudeDir, { recursive: true });
11089
11178
  unlockProtected();
11090
- writeFileSync4(join10(p.hooksDir, "guard.mjs"), readGuard());
11091
- writeFileSync4(join10(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11092
- writeFileSync4(join10(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11093
- writeFileSync4(join10(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
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"));
11094
11183
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
11095
11184
  installClaudeShim(join10(p.hooksDir, "shield.mjs"));
11096
- writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11185
+ writeFileSync5(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11097
11186
  console.log(` Wrote ${p.configPath}`);
11098
11187
  let existing = {};
11099
11188
  if (existsSync6(p.settingsPath)) {
11100
- const raw = readFileSync7(p.settingsPath, "utf-8");
11189
+ const raw = readFileSync8(p.settingsPath, "utf-8");
11101
11190
  if (!existsSync6(p.backupPath)) {
11102
- writeFileSync4(p.backupPath, raw);
11191
+ writeFileSync5(p.backupPath, raw);
11103
11192
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
11104
11193
  }
11105
11194
  try {
@@ -11122,7 +11211,7 @@ async function runGlobalInstall(opts = {}) {
11122
11211
  Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
11123
11212
  }
11124
11213
  };
11125
- writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11214
+ writeFileSync5(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11126
11215
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
11127
11216
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
11128
11217
  lockProtected();
@@ -11302,7 +11391,7 @@ import { createServer, request as httpRequest } from "http";
11302
11391
  import { request as httpsRequest } from "https";
11303
11392
  import { spawn as spawn3 } from "child_process";
11304
11393
  import { URL as URL2 } from "url";
11305
- import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
11394
+ import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
11306
11395
  import { resolve as resolve5 } from "path";
11307
11396
  import { homedir as homedir9 } from "os";
11308
11397
  function findCacheFile() {
@@ -11332,7 +11421,7 @@ function loadCfg() {
11332
11421
  try {
11333
11422
  const f = findCacheFile();
11334
11423
  if (f && existsSync7(f)) {
11335
- const c2 = JSON.parse(readFileSync8(f, "utf-8"));
11424
+ const c2 = JSON.parse(readFileSync9(f, "utf-8"));
11336
11425
  const d = c2?.security?.dlpRedact;
11337
11426
  const g = c2?.security?.ghost;
11338
11427
  const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
@@ -11620,7 +11709,7 @@ __export(logs_server_exports, {
11620
11709
  runLogsServer: () => runLogsServer
11621
11710
  });
11622
11711
  import { createServer as createServer2 } from "http";
11623
- import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11712
+ import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
11624
11713
  import { resolve as resolve6, join as join11, isAbsolute } from "path";
11625
11714
  import { homedir as homedir10 } from "os";
11626
11715
  import { readdirSync as readdirSync2 } from "fs";
@@ -11647,7 +11736,7 @@ async function findLogDir() {
11647
11736
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
11648
11737
  for (const f of files) {
11649
11738
  try {
11650
- const c2 = JSON.parse(readFileSync9(join11(base, f), "utf-8"));
11739
+ const c2 = JSON.parse(readFileSync10(join11(base, f), "utf-8"));
11651
11740
  const p = c2?.security?.localLogs?.path;
11652
11741
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
11653
11742
  } catch {
@@ -11656,7 +11745,7 @@ async function findLogDir() {
11656
11745
  } catch {
11657
11746
  }
11658
11747
  try {
11659
- const cfgRaw = readFileSync9(join11(base, "cloud-guard.json"), "utf-8");
11748
+ const cfgRaw = readFileSync10(join11(base, "cloud-guard.json"), "utf-8");
11660
11749
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
11661
11750
  if (apiKey) {
11662
11751
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -11746,7 +11835,7 @@ async function runLogsServer() {
11746
11835
  return;
11747
11836
  }
11748
11837
  try {
11749
- const text = readFileSync9(info.file, "utf-8");
11838
+ const text = readFileSync10(info.file, "utf-8");
11750
11839
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
11751
11840
  res.end(text);
11752
11841
  } catch {
@@ -11798,7 +11887,7 @@ var init_logs_server = __esm({
11798
11887
 
11799
11888
  // src/inject.ts
11800
11889
  var inject_exports = {};
11801
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync8, copyFileSync } from "fs";
11890
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync8, copyFileSync } from "fs";
11802
11891
  import { resolve as resolve7 } from "path";
11803
11892
  import { execSync } from "child_process";
11804
11893
  function parseInjectArgs(argv) {
@@ -11858,7 +11947,7 @@ WHAT IT DOES
11858
11947
  function detectProject() {
11859
11948
  if (!existsSync8(resolve7("package.json"))) return false;
11860
11949
  try {
11861
- const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11950
+ const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
11862
11951
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
11863
11952
  return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
11864
11953
  } catch {
@@ -11867,7 +11956,7 @@ function detectProject() {
11867
11956
  }
11868
11957
  function findTsEntryFile() {
11869
11958
  try {
11870
- const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11959
+ const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
11871
11960
  if (pkg.bin) {
11872
11961
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
11873
11962
  if (typeof binPath === "string") {
@@ -11894,7 +11983,7 @@ function findTsEntryFile() {
11894
11983
  const full = resolve7(c2);
11895
11984
  if (existsSync8(full)) {
11896
11985
  try {
11897
- const content = readFileSync10(full, "utf-8");
11986
+ const content = readFileSync11(full, "utf-8");
11898
11987
  if (content.includes("McpServer") || content.includes("McpServer")) {
11899
11988
  return full;
11900
11989
  }
@@ -11914,7 +12003,7 @@ function detectPackageManager() {
11914
12003
  }
11915
12004
  function installSdk() {
11916
12005
  try {
11917
- const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
12006
+ const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
11918
12007
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
11919
12008
  if (allDeps["@solongate/proxy"]) {
11920
12009
  log3(" @solongate/proxy already installed");
@@ -11935,7 +12024,7 @@ function installSdk() {
11935
12024
  }
11936
12025
  }
11937
12026
  function injectTypeScript(filePath) {
11938
- const original = readFileSync10(filePath, "utf-8");
12027
+ const original = readFileSync11(filePath, "utf-8");
11939
12028
  const changes = [];
11940
12029
  let modified = original;
11941
12030
  if (modified.includes("SecureMcpServer")) {
@@ -12106,7 +12195,7 @@ async function main2() {
12106
12195
  log3("");
12107
12196
  log3(` Backup: ${backupPath}`);
12108
12197
  }
12109
- writeFileSync5(entryFile, result.modified);
12198
+ writeFileSync6(entryFile, result.modified);
12110
12199
  log3("");
12111
12200
  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");
12112
12201
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -12135,7 +12224,7 @@ var init_inject = __esm({
12135
12224
 
12136
12225
  // src/create.ts
12137
12226
  var create_exports = {};
12138
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
12227
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "fs";
12139
12228
  import { resolve as resolve8, join as join12 } from "path";
12140
12229
  import { execSync as execSync2 } from "child_process";
12141
12230
  function withSpinner(message, fn) {
@@ -12215,7 +12304,7 @@ EXAMPLES
12215
12304
  `);
12216
12305
  }
12217
12306
  function createProject(dir, name, _policy) {
12218
- writeFileSync6(
12307
+ writeFileSync7(
12219
12308
  join12(dir, "package.json"),
12220
12309
  JSON.stringify(
12221
12310
  {
@@ -12245,7 +12334,7 @@ function createProject(dir, name, _policy) {
12245
12334
  2
12246
12335
  ) + "\n"
12247
12336
  );
12248
- writeFileSync6(
12337
+ writeFileSync7(
12249
12338
  join12(dir, "tsconfig.json"),
12250
12339
  JSON.stringify(
12251
12340
  {
@@ -12267,7 +12356,7 @@ function createProject(dir, name, _policy) {
12267
12356
  ) + "\n"
12268
12357
  );
12269
12358
  mkdirSync5(join12(dir, "src"), { recursive: true });
12270
- writeFileSync6(
12359
+ writeFileSync7(
12271
12360
  join12(dir, "src", "index.ts"),
12272
12361
  `#!/usr/bin/env node
12273
12362
 
@@ -12309,7 +12398,7 @@ console.log('');
12309
12398
  console.log('Press Ctrl+C to stop.');
12310
12399
  `
12311
12400
  );
12312
- writeFileSync6(
12401
+ writeFileSync7(
12313
12402
  join12(dir, ".mcp.json"),
12314
12403
  JSON.stringify(
12315
12404
  {
@@ -12327,12 +12416,12 @@ console.log('Press Ctrl+C to stop.');
12327
12416
  2
12328
12417
  ) + "\n"
12329
12418
  );
12330
- writeFileSync6(
12419
+ writeFileSync7(
12331
12420
  join12(dir, ".env"),
12332
12421
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
12333
12422
  `
12334
12423
  );
12335
- writeFileSync6(
12424
+ writeFileSync7(
12336
12425
  join12(dir, ".gitignore"),
12337
12426
  `node_modules/
12338
12427
  dist/
@@ -12426,14 +12515,14 @@ var init_create = __esm({
12426
12515
 
12427
12516
  // src/pull-push.ts
12428
12517
  var pull_push_exports = {};
12429
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
12518
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "fs";
12430
12519
  import { resolve as resolve9 } from "path";
12431
12520
  function loadEnv() {
12432
12521
  if (process.env.SOLONGATE_API_KEY) return;
12433
12522
  const envPath = resolve9(".env");
12434
12523
  if (!existsSync10(envPath)) return;
12435
12524
  try {
12436
- const content = readFileSync11(envPath, "utf-8");
12525
+ const content = readFileSync12(envPath, "utf-8");
12437
12526
  for (const line of content.split("\n")) {
12438
12527
  const trimmed = line.trim();
12439
12528
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -12621,7 +12710,7 @@ async function pull(apiKey, file, policyId) {
12621
12710
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
12622
12711
  const { id: _id, ...policyWithoutId } = policy;
12623
12712
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
12624
- writeFileSync7(file, json, "utf-8");
12713
+ writeFileSync8(file, json, "utf-8");
12625
12714
  log5("");
12626
12715
  log5(green2(" Saved to: ") + file);
12627
12716
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -12650,7 +12739,7 @@ async function push(apiKey, file, policyId) {
12650
12739
  log5(" solongate-proxy list");
12651
12740
  process.exit(1);
12652
12741
  }
12653
- const content = readFileSync11(file, "utf-8");
12742
+ const content = readFileSync12(file, "utf-8");
12654
12743
  let policy;
12655
12744
  try {
12656
12745
  policy = JSON.parse(content);
package/dist/tui/index.js CHANGED
@@ -169,12 +169,12 @@ function KeyHints({ hints }) {
169
169
  // src/tui/panels/Live.tsx
170
170
  import { Box as Box2, Text as Text2, useInput } from "ink";
171
171
  import TextInput from "ink-text-input";
172
- import { mkdirSync, writeFileSync } from "fs";
172
+ import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
173
173
  import { homedir as homedir4 } from "os";
174
174
  import { join as join4 } from "path";
175
175
 
176
176
  // src/tui/local-log.ts
177
- import { closeSync, openSync, readSync, statSync } from "fs";
177
+ import { closeSync, openSync, readFileSync as readFileSync2, readSync, statSync, writeFileSync } from "fs";
178
178
  import { homedir as homedir2 } from "os";
179
179
  import { join as join2 } from "path";
180
180
  var LOCAL_LOG = join2(homedir2(), ".solongate", "local-logs", "solongate-audit.jsonl");
@@ -193,6 +193,39 @@ function tailLines(file, maxBytes = 131072) {
193
193
  return [];
194
194
  }
195
195
  }
196
+ function deleteLocalEntry(at, tool, session) {
197
+ try {
198
+ const lines = readFileSync2(LOCAL_LOG, "utf-8").split("\n");
199
+ let removed = 0;
200
+ const kept = lines.filter((line) => {
201
+ if (!line.trim()) return false;
202
+ if (removed) return true;
203
+ try {
204
+ const j = JSON.parse(line);
205
+ const hit = Date.parse(j.ts ?? "") === at && (j.tool ?? "?") === tool && (!session || j.session_id === session);
206
+ if (hit) {
207
+ removed++;
208
+ return false;
209
+ }
210
+ } catch {
211
+ }
212
+ return true;
213
+ });
214
+ if (removed) writeFileSync(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
215
+ return removed;
216
+ } catch {
217
+ return 0;
218
+ }
219
+ }
220
+ function clearLocalLog() {
221
+ try {
222
+ const n = readFileSync2(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
223
+ writeFileSync(LOCAL_LOG, "");
224
+ return n;
225
+ } catch {
226
+ return 0;
227
+ }
228
+ }
196
229
  function parseLocalLines(lines) {
197
230
  const out = [];
198
231
  for (const line of lines) {
@@ -210,7 +243,7 @@ function parseLocalLines(lines) {
210
243
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
211
244
 
212
245
  // src/api-client/client.ts
213
- import { readFileSync as readFileSync2, existsSync } from "fs";
246
+ import { readFileSync as readFileSync3, existsSync } from "fs";
214
247
  import { resolve, join as join3 } from "path";
215
248
  import { homedir as homedir3 } from "os";
216
249
  var DEFAULT_API_URL = "https://api.solongate.com";
@@ -234,7 +267,7 @@ function loginCredentialFile() {
234
267
  try {
235
268
  const p = join3(homedir3(), ".solongate", "cloud-guard.json");
236
269
  if (!existsSync(p)) return {};
237
- const c2 = JSON.parse(readFileSync2(p, "utf-8"));
270
+ const c2 = JSON.parse(readFileSync3(p, "utf-8"));
238
271
  return c2 && typeof c2 === "object" ? c2 : {};
239
272
  } catch {
240
273
  return {};
@@ -244,7 +277,7 @@ function dotenvApiKey() {
244
277
  try {
245
278
  const envPath = resolve(".env");
246
279
  if (!existsSync(envPath)) return void 0;
247
- for (const line of readFileSync2(envPath, "utf-8").split("\n")) {
280
+ for (const line of readFileSync3(envPath, "utf-8").split("\n")) {
248
281
  const trimmed = line.trim();
249
282
  if (!trimmed || trimmed.startsWith("#")) continue;
250
283
  const eq = trimmed.indexOf("=");
@@ -496,11 +529,19 @@ var audit_exports = {};
496
529
  __export(audit_exports, {
497
530
  block: () => block,
498
531
  list: () => list2,
532
+ remove: () => remove2,
533
+ removeAll: () => removeAll,
499
534
  whitelist: () => whitelist
500
535
  });
501
536
  function list2(query = {}) {
502
537
  return request("GET", "/audit-logs", { query });
503
538
  }
539
+ function remove2(ids) {
540
+ return request("DELETE", "/audit-logs", { body: { ids } });
541
+ }
542
+ function removeAll() {
543
+ return request("DELETE", "/audit-logs", { body: { scope: "logs" } });
544
+ }
504
545
  function whitelist(id, scope = "exact") {
505
546
  return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
506
547
  }
@@ -1257,7 +1298,7 @@ function LivePanel({ active: active2 }) {
1257
1298
  const file = join4(homedir4(), ".solongate", "live-export.jsonl");
1258
1299
  try {
1259
1300
  mkdirSync(join4(homedir4(), ".solongate"), { recursive: true });
1260
- writeFileSync(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
1301
+ writeFileSync2(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
1261
1302
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
1262
1303
  } catch (err) {
1263
1304
  setActionMsg({ text: "\u2717 export failed: " + (err instanceof Error ? err.message : String(err)), level: "bad", until: Date.now() + 6e3 });
@@ -2237,6 +2278,8 @@ function AuditPanel({ active: active2, focused }) {
2237
2278
  const [si, setSi] = useState6(0);
2238
2279
  const [sessSearch, setSessSearch] = useState6("");
2239
2280
  const [sessSel, setSessSel] = useState6(0);
2281
+ const [confirm, setConfirm] = useState6(null);
2282
+ const [msg, setMsg] = useState6(null);
2240
2283
  const toTop = () => setSel(0);
2241
2284
  const statsQ = useLoader(() => source === "cloud" ? api.stats.get() : Promise.resolve(null), [source]);
2242
2285
  const tsQ = useLoader(() => source === "cloud" ? api.stats.timeseries({ period: "24h" }) : Promise.resolve(null), [source]);
@@ -2322,6 +2365,31 @@ function AuditPanel({ active: active2, focused }) {
2322
2365
  () => view === "detail" && current?.session && source === "cloud" ? api.audit.list({ session_id: current.session, limit: 40 }) : Promise.resolve(null),
2323
2366
  [view, current?.session, source]
2324
2367
  );
2368
+ const logsLoading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
2369
+ const sessLoading = source === "cloud" ? agentsQ.loading : localQ.loading;
2370
+ const doDelete = (kind) => {
2371
+ setMsg({ text: "deleting\u2026", level: "ok" });
2372
+ const run = async () => {
2373
+ if (source === "cloud") {
2374
+ if (kind === "one") {
2375
+ if (!current) throw new Error("nothing selected");
2376
+ await api.audit.remove([current.id]);
2377
+ } else {
2378
+ await api.audit.removeAll();
2379
+ }
2380
+ cloudQ.reload();
2381
+ statsQ.reloadQuiet();
2382
+ } else {
2383
+ const n = kind === "one" && current ? deleteLocalEntry(current.at, current.tool, current.session) : kind === "all" ? clearLocalLog() : 0;
2384
+ if (!n) throw new Error("entry not found in the local file");
2385
+ localQ.reload();
2386
+ }
2387
+ };
2388
+ run().then(() => {
2389
+ setMsg({ text: kind === "one" ? "\u2713 entry deleted" : `\u2713 ALL ${source} logs deleted`, level: "ok" });
2390
+ toTop();
2391
+ }).catch((e) => setMsg({ text: "\u2717 " + (e instanceof Error ? e.message : String(e)), level: "bad" }));
2392
+ };
2325
2393
  useInput5(
2326
2394
  (input, key) => {
2327
2395
  if (view === "detail") {
@@ -2332,6 +2400,11 @@ function AuditPanel({ active: active2, focused }) {
2332
2400
  else if (key.pageDown) setDetailScroll((n) => n + 10);
2333
2401
  return;
2334
2402
  }
2403
+ if (view === "logs" ? logsLoading : sessLoading) return;
2404
+ if (confirm && input !== "x" && input !== "X") {
2405
+ setConfirm(null);
2406
+ setMsg(null);
2407
+ }
2335
2408
  if (input === "s") {
2336
2409
  setSource((s) => s === "cloud" ? "local" : "cloud");
2337
2410
  setPage(0);
@@ -2393,6 +2466,23 @@ function AuditPanel({ active: active2, focused }) {
2393
2466
  setGi((n) => (n + 1) % SIGNALS.length);
2394
2467
  setPage(0);
2395
2468
  toTop();
2469
+ } else if (input === "x") {
2470
+ if (!current) return;
2471
+ if (confirm?.kind !== "one" || confirm.key !== current.id) {
2472
+ setConfirm({ kind: "one", key: current.id });
2473
+ setMsg({ text: `delete ${current.decision} ${current.tool} (${ago(current.at)} ago)? press x again`, level: "bad" });
2474
+ return;
2475
+ }
2476
+ setConfirm(null);
2477
+ doDelete("one");
2478
+ } else if (input === "X") {
2479
+ if (confirm?.kind !== "all") {
2480
+ setConfirm({ kind: "all", key: "all" });
2481
+ setMsg({ text: `DELETE ALL ${source} logs (${total} entries)? press X again`, level: "bad" });
2482
+ return;
2483
+ }
2484
+ setConfirm(null);
2485
+ doDelete("all");
2396
2486
  } else if (input === "t") setEditing("tool");
2397
2487
  else if (input === "n") setEditing("agent");
2398
2488
  else if (input === "/") setEditing("search");
@@ -2519,8 +2609,7 @@ function AuditPanel({ active: active2, focused }) {
2519
2609
  const selC = Math.min(sessSel, Math.max(0, sessionsFiltered.length - 1));
2520
2610
  const start2 = Math.min(Math.max(0, selC - Math.floor((listRows2 - 1) / 2)), Math.max(0, sessionsFiltered.length - listRows2));
2521
2611
  const win = sessionsFiltered.slice(start2, start2 + listRows2);
2522
- const loading2 = source === "cloud" ? agentsQ.loading : localQ.loading;
2523
- return /* @__PURE__ */ jsx6(DataView, { loading: loading2, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2612
+ return /* @__PURE__ */ jsx6(DataView, { loading: sessLoading, error: source === "cloud" ? agentsQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2524
2613
  strip,
2525
2614
  /* @__PURE__ */ jsxs6(Box6, { children: [
2526
2615
  srcChip,
@@ -2583,15 +2672,14 @@ function AuditPanel({ active: active2, focused }) {
2583
2672
  ] }) : null
2584
2673
  ] }) });
2585
2674
  }
2586
- const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0);
2675
+ const headerRows = 6 + (editing && editing !== "sess-search" ? 1 : 0) + (msg ? 1 : 0);
2587
2676
  const listRows = Math.max(4, rows - headerRows);
2588
2677
  const selClamped = Math.min(sel, Math.max(0, pageRows.length - 1));
2589
2678
  const maxStart = Math.max(0, pageRows.length - listRows);
2590
2679
  const start = Math.min(Math.max(0, selClamped - Math.floor((listRows - 1) / 2)), maxStart);
2591
2680
  const windowed = pageRows.slice(start, start + listRows);
2592
2681
  const reasonW = Math.min(60, Math.max(12, cols - 67));
2593
- const loading = (source === "cloud" ? cloudQ.loading : localQ.loading) && !editing;
2594
- return /* @__PURE__ */ jsx6(DataView, { loading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2682
+ return /* @__PURE__ */ jsx6(DataView, { loading: logsLoading, error: source === "cloud" ? cloudQ.error : localQ.error, children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
2595
2683
  strip,
2596
2684
  /* @__PURE__ */ jsxs6(Box6, { children: [
2597
2685
  srcChip,
@@ -2604,7 +2692,8 @@ function AuditPanel({ active: active2, focused }) {
2604
2692
  chip("search", search || "\xB7", !!search),
2605
2693
  sessFilter ? chip("sess", sessFilter.slice(0, 8), true) : null
2606
2694
  ] }),
2607
- /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
2695
+ /* @__PURE__ */ jsx6(Text6, { color: theme.dim, wrap: "truncate", children: focused ? "\u2191\u2193 select \xB7 enter full entry \xB7 \u2190\u2192 page \xB7 f dec \xB7 g sig \xB7 t tool \xB7 n agent \xB7 / search \xB7 x del \xB7 X del ALL \xB7 s source \xB7 v sessions \xB7 c clear" : "press \u2192 to browse" }),
2696
+ msg ? /* @__PURE__ */ jsx6(Text6, { color: msg.level === "bad" ? theme.bad : theme.ok, children: truncate(msg.text, cols) }) : null,
2608
2697
  editing && editing !== "sess-search" ? /* @__PURE__ */ jsxs6(Box6, { children: [
2609
2698
  /* @__PURE__ */ jsxs6(Text6, { color: theme.warn, children: [
2610
2699
  editing,
@@ -2950,7 +3039,7 @@ var HELP = [
2950
3039
  ["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
2951
3040
  ["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
2952
3041
  ["Rate limit / DLP", [["\u2191\u2193", "field / pattern"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["space", "toggle pattern"], ["m", "mode"], ["a / d", "add / remove custom"], ["g", "ghost mode"], ["P", "self-protection"], ["s", "save"]]],
2953
- ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["c", "clear filters"]]],
3042
+ ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
2954
3043
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]]
2955
3044
  ];
2956
3045
  function HelpOverlay({ cols, rows }) {
@@ -17,6 +17,10 @@ export interface LocalLogLine {
17
17
  matched_rule_id?: string;
18
18
  rate_limit_burst?: boolean;
19
19
  }
20
+ /** Delete ONE matching line (ts + tool [+ session]) from the local log file. */
21
+ export declare function deleteLocalEntry(at: number, tool: string, session?: string | null): number;
22
+ /** Empty the local log file. Returns how many lines were removed. */
23
+ export declare function clearLocalLog(): number;
20
24
  export declare function parseLocalLines(lines: string[]): Array<LocalLogLine & {
21
25
  at: number;
22
26
  }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.25",
3
+ "version": "0.81.26",
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": {