@solongate/proxy 0.81.44 → 0.81.46

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
@@ -6554,15 +6554,151 @@ var init_cli_utils = __esm({
6554
6554
  }
6555
6555
  });
6556
6556
 
6557
- // src/tui/config.ts
6558
- import { readFileSync as readFileSync4 } from "fs";
6557
+ // src/self-update.ts
6558
+ var self_update_exports = {};
6559
+ __export(self_update_exports, {
6560
+ currentVersion: () => currentVersion,
6561
+ maybeSelfUpdate: () => maybeSelfUpdate
6562
+ });
6563
+ import { execFile, spawn } from "child_process";
6564
+ import { mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync4, realpathSync, writeFileSync as writeFileSync3 } from "fs";
6559
6565
  import { homedir as homedir2 } from "os";
6560
- import { join as join4 } from "path";
6566
+ import { dirname, join as join4 } from "path";
6567
+ import { fileURLToPath } from "url";
6568
+ function readState() {
6569
+ try {
6570
+ const s = JSON.parse(readFileSync4(STATE_FILE, "utf-8"));
6571
+ return s && typeof s === "object" ? s : {};
6572
+ } catch {
6573
+ return {};
6574
+ }
6575
+ }
6576
+ function writeState(s) {
6577
+ try {
6578
+ mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
6579
+ writeFileSync3(STATE_FILE, JSON.stringify(s));
6580
+ } catch {
6581
+ }
6582
+ }
6583
+ function currentVersion() {
6584
+ try {
6585
+ const pkg = JSON.parse(readFileSync4(join4(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8"));
6586
+ return pkg.version ?? "0.0.0";
6587
+ } catch {
6588
+ return "0.0.0";
6589
+ }
6590
+ }
6591
+ function newerThan(b, a) {
6592
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
6593
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
6594
+ for (let i = 0; i < 3; i++) {
6595
+ if ((pb[i] ?? 0) > (pa[i] ?? 0)) return true;
6596
+ if ((pb[i] ?? 0) < (pa[i] ?? 0)) return false;
6597
+ }
6598
+ return false;
6599
+ }
6600
+ async function fetchLatest() {
6601
+ try {
6602
+ const res = await fetch(`https://registry.npmjs.org/${PKG}/latest`, { signal: AbortSignal.timeout(2e3) });
6603
+ if (!res.ok) return null;
6604
+ const j = await res.json();
6605
+ return typeof j.version === "string" ? j.version : null;
6606
+ } catch {
6607
+ return null;
6608
+ }
6609
+ }
6610
+ function npmGlobalRoot() {
6611
+ return new Promise((resolve10) => {
6612
+ try {
6613
+ execFile("npm", ["root", "-g"], { timeout: 5e3, windowsHide: true, shell: process.platform === "win32" }, (err2, stdout) => {
6614
+ resolve10(err2 ? null : stdout.trim() || null);
6615
+ });
6616
+ } catch {
6617
+ resolve10(null);
6618
+ }
6619
+ });
6620
+ }
6621
+ async function isGlobalInstall() {
6622
+ try {
6623
+ const script = realpathSync(process.argv[1] ?? "");
6624
+ if (!script) return false;
6625
+ const root = await npmGlobalRoot();
6626
+ if (!root) return false;
6627
+ return script.startsWith(realpathSync(root));
6628
+ } catch {
6629
+ return false;
6630
+ }
6631
+ }
6632
+ function spawnGlobalInstall(version) {
6633
+ try {
6634
+ mkdirSync3(join4(homedir2(), ".solongate"), { recursive: true });
6635
+ const log6 = openSync(LOG_FILE, "a");
6636
+ const p = spawn("npm", ["install", "-g", `${PKG}@${version}`], {
6637
+ stdio: ["ignore", log6, log6],
6638
+ detached: true,
6639
+ windowsHide: true,
6640
+ shell: process.platform === "win32"
6641
+ });
6642
+ p.on("error", () => {
6643
+ });
6644
+ p.unref();
6645
+ return true;
6646
+ } catch {
6647
+ return false;
6648
+ }
6649
+ }
6650
+ function maybeSelfUpdate(notify = (l) => process.stderr.write(l + "\n")) {
6651
+ void (async () => {
6652
+ try {
6653
+ const state = readState();
6654
+ const now = Date.now();
6655
+ const current = currentVersion();
6656
+ let latest = state.latestSeen ?? null;
6657
+ if (now - (state.lastCheckAt ?? 0) >= CHECK_EVERY_MS) {
6658
+ latest = await fetchLatest();
6659
+ if (latest) writeState({ ...state, lastCheckAt: now, latestSeen: latest });
6660
+ else writeState({ ...state, lastCheckAt: now });
6661
+ }
6662
+ if (!latest || !newerThan(latest, current)) return;
6663
+ const attempts = readState().attempts ?? {};
6664
+ if (await isGlobalInstall()) {
6665
+ if (now - (attempts[latest] ?? 0) >= ATTEMPT_EVERY_MS) {
6666
+ writeState({ ...readState(), attempts: { [latest]: now } });
6667
+ if (spawnGlobalInstall(latest)) {
6668
+ notify(`${c.dim}\u2191 solongate v${latest} available (running v${current}) \u2014 updating in the background, next run uses it${c.reset}`);
6669
+ return;
6670
+ }
6671
+ } else {
6672
+ return;
6673
+ }
6674
+ }
6675
+ notify(`${c.dim}\u2191 solongate v${latest} available (running v${current}) \u2014 update: ${c.reset}${c.cyan}npm i -g ${PKG}@latest${c.reset}`);
6676
+ } catch {
6677
+ }
6678
+ })();
6679
+ }
6680
+ var PKG, CHECK_EVERY_MS, ATTEMPT_EVERY_MS, STATE_FILE, LOG_FILE;
6681
+ var init_self_update = __esm({
6682
+ "src/self-update.ts"() {
6683
+ "use strict";
6684
+ init_cli_utils();
6685
+ PKG = "@solongate/proxy";
6686
+ CHECK_EVERY_MS = 30 * 60 * 1e3;
6687
+ ATTEMPT_EVERY_MS = 6 * 60 * 60 * 1e3;
6688
+ STATE_FILE = join4(homedir2(), ".solongate", ".self-update.json");
6689
+ LOG_FILE = join4(homedir2(), ".solongate", "self-update.log");
6690
+ }
6691
+ });
6692
+
6693
+ // src/tui/config.ts
6694
+ import { readFileSync as readFileSync5 } from "fs";
6695
+ import { homedir as homedir3 } from "os";
6696
+ import { join as join5 } from "path";
6561
6697
  function loadConfig() {
6562
6698
  if (cached) return cached;
6563
6699
  const defaults = { notifications: true };
6564
6700
  try {
6565
- const raw = readFileSync4(join4(homedir2(), ".solongate", "tui-config.json"), "utf-8");
6701
+ const raw = readFileSync5(join5(homedir3(), ".solongate", "tui-config.json"), "utf-8");
6566
6702
  const j = JSON.parse(raw);
6567
6703
  cached = {
6568
6704
  notifications: j.notifications !== false,
@@ -6688,14 +6824,14 @@ var init_components = __esm({
6688
6824
  });
6689
6825
 
6690
6826
  // src/tui/local-log.ts
6691
- import { closeSync, openSync, readFileSync as readFileSync5, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
6692
- import { homedir as homedir3 } from "os";
6693
- import { join as join5 } from "path";
6827
+ import { closeSync, openSync as openSync2, readFileSync as readFileSync6, readSync, statSync, writeFileSync as writeFileSync4 } from "fs";
6828
+ import { homedir as homedir4 } from "os";
6829
+ import { join as join6 } from "path";
6694
6830
  function tailLines(file, maxBytes = 131072) {
6695
6831
  try {
6696
6832
  const size = statSync(file).size;
6697
6833
  const start = Math.max(0, size - maxBytes);
6698
- const fd = openSync(file, "r");
6834
+ const fd = openSync2(file, "r");
6699
6835
  const buf = Buffer.alloc(size - start);
6700
6836
  readSync(fd, buf, 0, buf.length, start);
6701
6837
  closeSync(fd);
@@ -6708,7 +6844,7 @@ function tailLines(file, maxBytes = 131072) {
6708
6844
  }
6709
6845
  function deleteLocalEntry(at, tool, session) {
6710
6846
  try {
6711
- const lines = readFileSync5(LOCAL_LOG, "utf-8").split("\n");
6847
+ const lines = readFileSync6(LOCAL_LOG, "utf-8").split("\n");
6712
6848
  let removed = 0;
6713
6849
  const kept = lines.filter((line) => {
6714
6850
  if (!line.trim()) return false;
@@ -6724,7 +6860,7 @@ function deleteLocalEntry(at, tool, session) {
6724
6860
  }
6725
6861
  return true;
6726
6862
  });
6727
- if (removed) writeFileSync3(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
6863
+ if (removed) writeFileSync4(LOCAL_LOG, kept.length ? kept.join("\n") + "\n" : "");
6728
6864
  return removed;
6729
6865
  } catch {
6730
6866
  return 0;
@@ -6732,8 +6868,8 @@ function deleteLocalEntry(at, tool, session) {
6732
6868
  }
6733
6869
  function clearLocalLog() {
6734
6870
  try {
6735
- const n = readFileSync5(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
6736
- writeFileSync3(LOCAL_LOG, "");
6871
+ const n = readFileSync6(LOCAL_LOG, "utf-8").split("\n").filter(Boolean).length;
6872
+ writeFileSync4(LOCAL_LOG, "");
6737
6873
  return n;
6738
6874
  } catch {
6739
6875
  return 0;
@@ -6764,20 +6900,20 @@ var LOCAL_LOG, DLP_REASON, RL_REASON;
6764
6900
  var init_local_log = __esm({
6765
6901
  "src/tui/local-log.ts"() {
6766
6902
  "use strict";
6767
- LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
6903
+ LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
6768
6904
  DLP_REASON = /security layer \(dlp\)/i;
6769
6905
  RL_REASON = /security layer \(rate limit\)|rate[- ]?limit(?:ed)?\b.*exceed|exceeded \d+ calls/i;
6770
6906
  }
6771
6907
  });
6772
6908
 
6773
6909
  // src/api-client/client.ts
6774
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
6775
- import { resolve as resolve3, join as join6 } from "path";
6776
- import { homedir as homedir4 } from "os";
6910
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "fs";
6911
+ import { resolve as resolve3, join as join7 } from "path";
6912
+ import { homedir as homedir5 } from "os";
6777
6913
  function listAccounts() {
6778
6914
  let list6 = [];
6779
6915
  try {
6780
- const raw = JSON.parse(readFileSync6(accountsFile(), "utf-8"));
6916
+ const raw = JSON.parse(readFileSync7(accountsFile(), "utf-8"));
6781
6917
  if (Array.isArray(raw)) list6 = raw.filter((a) => a && typeof a.apiKey === "string");
6782
6918
  } catch {
6783
6919
  }
@@ -6791,7 +6927,7 @@ function saveAccount(acc) {
6791
6927
  try {
6792
6928
  const list6 = (() => {
6793
6929
  try {
6794
- const raw = JSON.parse(readFileSync6(accountsFile(), "utf-8"));
6930
+ const raw = JSON.parse(readFileSync7(accountsFile(), "utf-8"));
6795
6931
  return Array.isArray(raw) ? raw.filter((a) => a && a.apiKey) : [];
6796
6932
  } catch {
6797
6933
  return [];
@@ -6799,8 +6935,8 @@ function saveAccount(acc) {
6799
6935
  })();
6800
6936
  const next = list6.filter((a) => a.apiKey !== acc.apiKey);
6801
6937
  next.unshift({ ...acc, addedAt: acc.addedAt ?? Date.now() });
6802
- mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
6803
- writeFileSync4(accountsFile(), JSON.stringify(next, null, 2));
6938
+ mkdirSync4(join7(homedir5(), ".solongate"), { recursive: true });
6939
+ writeFileSync5(accountsFile(), JSON.stringify(next, null, 2));
6804
6940
  } catch {
6805
6941
  }
6806
6942
  }
@@ -6813,15 +6949,15 @@ function isActiveAccount(apiKey) {
6813
6949
  }
6814
6950
  function setActiveAccount(creds) {
6815
6951
  try {
6816
- const dir = join6(homedir4(), ".solongate");
6817
- mkdirSync3(dir, { recursive: true });
6818
- const p = join6(dir, ["cloud", "guard.json"].join("-"));
6952
+ const dir = join7(homedir5(), ".solongate");
6953
+ mkdirSync4(dir, { recursive: true });
6954
+ const p = join7(dir, ["cloud", "guard.json"].join("-"));
6819
6955
  let existing = {};
6820
6956
  try {
6821
- existing = JSON.parse(readFileSync6(p, "utf-8"));
6957
+ existing = JSON.parse(readFileSync7(p, "utf-8"));
6822
6958
  } catch {
6823
6959
  }
6824
- writeFileSync4(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
6960
+ writeFileSync5(p, JSON.stringify({ ...existing, apiKey: creds.apiKey, apiUrl: creds.apiUrl }, null, 2));
6825
6961
  cached2 = null;
6826
6962
  return true;
6827
6963
  } catch {
@@ -6830,9 +6966,9 @@ function setActiveAccount(creds) {
6830
6966
  }
6831
6967
  function loginCredentialFile() {
6832
6968
  try {
6833
- const p = join6(homedir4(), ".solongate", "cloud-guard.json");
6969
+ const p = join7(homedir5(), ".solongate", "cloud-guard.json");
6834
6970
  if (!existsSync3(p)) return {};
6835
- const c2 = JSON.parse(readFileSync6(p, "utf-8"));
6971
+ const c2 = JSON.parse(readFileSync7(p, "utf-8"));
6836
6972
  return c2 && typeof c2 === "object" ? c2 : {};
6837
6973
  } catch {
6838
6974
  return {};
@@ -6842,7 +6978,7 @@ function dotenvApiKey() {
6842
6978
  try {
6843
6979
  const envPath = resolve3(".env");
6844
6980
  if (!existsSync3(envPath)) return void 0;
6845
- for (const line of readFileSync6(envPath, "utf-8").split("\n")) {
6981
+ for (const line of readFileSync7(envPath, "utf-8").split("\n")) {
6846
6982
  const trimmed = line.trim();
6847
6983
  if (!trimmed || trimmed.startsWith("#")) continue;
6848
6984
  const eq = trimmed.indexOf("=");
@@ -6944,7 +7080,7 @@ var init_client = __esm({
6944
7080
  "src/api-client/client.ts"() {
6945
7081
  "use strict";
6946
7082
  DEFAULT_API_URL2 = "https://api.solongate.com";
6947
- accountsFile = () => join6(homedir4(), ".solongate", "accounts.json");
7083
+ accountsFile = () => join7(homedir5(), ".solongate", "accounts.json");
6948
7084
  viewOverride = null;
6949
7085
  ApiError = class extends Error {
6950
7086
  status;
@@ -6974,12 +7110,12 @@ var init_types = __esm({
6974
7110
  });
6975
7111
 
6976
7112
  // src/api-client/device-login.ts
6977
- import { spawn } from "child_process";
7113
+ import { spawn as spawn2 } from "child_process";
6978
7114
  function openBrowser(url) {
6979
7115
  try {
6980
7116
  const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
6981
7117
  const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url];
6982
- const child = spawn(cmd, args, { stdio: "ignore", detached: true });
7118
+ const child = spawn2(cmd, args, { stdio: "ignore", detached: true });
6983
7119
  child.on("error", () => {
6984
7120
  });
6985
7121
  child.unref();
@@ -7304,24 +7440,24 @@ var init_api_client = __esm({
7304
7440
  });
7305
7441
 
7306
7442
  // src/tui/notify.ts
7307
- import { spawn as spawn2 } from "child_process";
7443
+ import { spawn as spawn3 } from "child_process";
7308
7444
  function desktopNotify(title, msg) {
7309
7445
  try {
7310
7446
  if (process.platform === "linux") {
7311
- const p = spawn2("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
7447
+ const p = spawn3("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
7312
7448
  p.on("error", () => {
7313
7449
  });
7314
7450
  p.unref();
7315
7451
  } else if (process.platform === "win32") {
7316
7452
  const q = (s) => s.replace(/'/g, "''");
7317
7453
  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()`;
7318
- const p = spawn2("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "ignore", detached: true, windowsHide: true });
7454
+ const p = spawn3("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "ignore", detached: true, windowsHide: true });
7319
7455
  p.on("error", () => {
7320
7456
  });
7321
7457
  p.unref();
7322
7458
  } else if (process.platform === "darwin") {
7323
7459
  const esc = (s) => s.replace(/"/g, '\\"');
7324
- const p = spawn2("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
7460
+ const p = spawn3("osascript", ["-e", `display notification "${esc(msg)}" with title "${esc(title)}"`], { stdio: "ignore", detached: true });
7325
7461
  p.on("error", () => {
7326
7462
  });
7327
7463
  p.unref();
@@ -7407,9 +7543,9 @@ var init_hooks = __esm({
7407
7543
  // src/tui/panels/Live.tsx
7408
7544
  import { Box as Box2, Text as Text2, useInput } from "ink";
7409
7545
  import TextInput from "ink-text-input";
7410
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
7411
- import { homedir as homedir5 } from "os";
7412
- import { join as join7 } from "path";
7546
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
7547
+ import { homedir as homedir6 } from "os";
7548
+ import { join as join8 } from "path";
7413
7549
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7414
7550
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7415
7551
  function extractTarget(detail) {
@@ -7534,6 +7670,7 @@ function LivePanel({ active: active2 }) {
7534
7670
  const [detailScroll, setDetailScroll] = useState2(0);
7535
7671
  const [inspect, setInspect] = useState2(null);
7536
7672
  const [inspectScroll, setInspectScroll] = useState2(0);
7673
+ const [layersScroll, setLayersScroll] = useState2(0);
7537
7674
  const inspectFromRef = useRef2("stream");
7538
7675
  const seenRef = useRef2(/* @__PURE__ */ new Set());
7539
7676
  const lastLocalTs = useRef2(0);
@@ -7933,6 +8070,14 @@ function LivePanel({ active: active2 }) {
7933
8070
  else if (key.pageDown) setInspectScroll((n) => n + 10);
7934
8071
  return;
7935
8072
  }
8073
+ if (mode === "layers") {
8074
+ if (key.leftArrow || input === "l") setMode("stream");
8075
+ else if (key.upArrow) setLayersScroll((n) => Math.max(0, n - 1));
8076
+ else if (key.downArrow) setLayersScroll((n) => n + 1);
8077
+ else if (key.pageUp) setLayersScroll((n) => Math.max(0, n - 10));
8078
+ else if (key.pageDown) setLayersScroll((n) => n + 10);
8079
+ return;
8080
+ }
7936
8081
  if (mode === "detail") {
7937
8082
  const maxD = Math.max(0, detailEntries.length - 1);
7938
8083
  if (key.leftArrow) {
@@ -7982,16 +8127,19 @@ function LivePanel({ active: active2 }) {
7982
8127
  } else if (input === "s") {
7983
8128
  setPickIdx(0);
7984
8129
  setMode("pick");
8130
+ } else if (input === "l") {
8131
+ setLayersScroll(0);
8132
+ setMode("layers");
7985
8133
  } else if (input === "w") void doAction("whitelist");
7986
8134
  else if (input === "b") void doAction("block");
7987
8135
  else if (input === "d") toggleSignal("deny");
7988
8136
  else if (input === "x") toggleSignal("dlp");
7989
8137
  else if (input === "r") toggleSignal("ratelimit");
7990
8138
  else if (input === "e") {
7991
- const file = join7(homedir5(), ".solongate", "live-export.jsonl");
8139
+ const file = join8(homedir6(), ".solongate", "live-export.jsonl");
7992
8140
  try {
7993
- mkdirSync4(join7(homedir5(), ".solongate"), { recursive: true });
7994
- writeFileSync5(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
8141
+ mkdirSync5(join8(homedir6(), ".solongate"), { recursive: true });
8142
+ writeFileSync6(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7995
8143
  setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7996
8144
  } catch (err2) {
7997
8145
  setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
@@ -8101,6 +8249,87 @@ function LivePanel({ active: active2 }) {
8101
8249
  ] })
8102
8250
  ] });
8103
8251
  }
8252
+ if (mode === "layers") {
8253
+ const modeColor4 = (m) => m === "block" || m === "on" ? theme.ok : m === "detect" ? theme.warn : theme.dim;
8254
+ const barW = Math.max(10, Math.min(40, innerW - 30));
8255
+ const burstsInBuf = mergedAll.filter((e) => e.burst).length;
8256
+ const dlpInBuf = mergedAll.filter((e) => e.dlp).length;
8257
+ const allHits = ins.dlpByPattern ?? [];
8258
+ const maxHit = allHits[0]?.count ?? 1;
8259
+ const L = [];
8260
+ const push2 = (el) => L.push(/* @__PURE__ */ jsx2(Box2, { children: el }, L.length));
8261
+ const blank = () => push2(/* @__PURE__ */ jsx2(Text2, { children: " " }));
8262
+ const section = (label, m, note) => push2(
8263
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8264
+ /* @__PURE__ */ jsxs2(Text2, { bold: true, color: theme.accentBright, children: [
8265
+ "\u258E",
8266
+ label.padEnd(10)
8267
+ ] }),
8268
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: modeColor4(m), children: (m ?? "?").padEnd(8) }),
8269
+ note ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: note }) : null
8270
+ ] })
8271
+ );
8272
+ const kv = (k, v) => push2(
8273
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8274
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (" " + k).padEnd(16) }),
8275
+ v
8276
+ ] })
8277
+ );
8278
+ section("RATELIMIT", rl?.mode, rl?.mode === "block" ? "over-limit calls are DENIED" : rl?.mode === "detect" ? "over-limit calls are observed & flagged rl:yes" : "no limit enforcement");
8279
+ kv("limits", /* @__PURE__ */ jsx2(Text2, { children: rl?.perMinute || rl?.perHour || rl?.perDay ? `${rl?.perMinute || "\u2014"}/min \xB7 ${rl?.perHour || "\u2014"}/hour \xB7 ${rl?.perDay || "\u2014"}/day` : "none configured" }));
8280
+ kv("load now", /* @__PURE__ */ jsx2(Text2, { children: `${minuteNow} calls in the last 60s` }));
8281
+ if (rl?.perMinute) push2(/* @__PURE__ */ jsx2(HBar, { label: " load/min", value: minuteNow, max: rl.perMinute, width: barW, color: minuteNow > rl.perMinute ? theme.bad : theme.accent }));
8282
+ kv("bursts", /* @__PURE__ */ jsx2(Text2, { color: burstsInBuf ? theme.warn : theme.dim, children: `${burstsInBuf} flagged in the live buffer` }));
8283
+ blank();
8284
+ section("DLP", dl?.mode, dl?.mode === "block" ? "secrets in arguments are DENIED + redacted" : dl?.mode === "detect" ? "secrets observed & flagged dlp:yes, output redacted" : "no secret scanning");
8285
+ kv("hits", /* @__PURE__ */ jsx2(Text2, { color: dlpInBuf ? theme.bad : theme.dim, children: `${dlpInBuf} flagged in the live buffer` }));
8286
+ const builtin = dl?.patterns ?? [];
8287
+ kv("builtin", /* @__PURE__ */ jsx2(Text2, { children: builtin.length ? `${builtin.length} patterns enabled` : "none enabled" }));
8288
+ for (const line of wrapLines(builtin.join(" \xB7 "), Math.max(20, innerW - 18))) push2(/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + line }));
8289
+ const custom = dl?.custom ?? [];
8290
+ kv("custom", /* @__PURE__ */ jsx2(Text2, { children: custom.length ? `${custom.length} patterns` : "none" }));
8291
+ for (const c2 of custom)
8292
+ push2(
8293
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8294
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " }),
8295
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate2(c2.name ?? "custom", 24).padEnd(25) }),
8296
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate2(c2.re ?? "", Math.max(10, innerW - 44)) })
8297
+ ] })
8298
+ );
8299
+ kv("pattern hits", /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: allHits.length ? "last 7 days" : "none in the last 7 days" }));
8300
+ for (const h of allHits) push2(/* @__PURE__ */ jsx2(HBar, { label: " " + truncate2(h.pattern, 10), value: h.count, max: maxHit, width: barW, color: theme.bad }));
8301
+ blank();
8302
+ section("GHOST", gh?.mode, gh?.mode === "on" ? "matching paths are hidden from agents" : "no hidden paths");
8303
+ const ghostPats = gh?.patterns ?? [];
8304
+ kv("patterns", /* @__PURE__ */ jsx2(Text2, { children: ghostPats.length ? `${ghostPats.length}` : "none" }));
8305
+ for (const line of wrapLines(ghostPats.join(" \xB7 "), Math.max(20, innerW - 18))) push2(/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + line }));
8306
+ blank();
8307
+ section("GUARD", guard.data ? guard.data.up_to_date ? "ok" : "stale" : "?", "the PreToolUse hook enforcing all of the above");
8308
+ kv(
8309
+ "hooks",
8310
+ guard.data ? /* @__PURE__ */ jsx2(Text2, { color: guard.data.up_to_date ? theme.ok : theme.warn, children: `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? " (latest)" : ` \u2192 v${guard.data.latest} available`} \xB7 ${guard.data.device_count} device${guard.data.device_count === 1 ? "" : "s"}` }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "loading\u2026" })
8311
+ );
8312
+ kv("local eval", ring ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: `avg ${ring.avgMs}ms over ${ring.count} recent calls` }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no local ring in cwd" }));
8313
+ const bodyRows = Math.max(4, rows - 5);
8314
+ const maxLScroll = Math.max(0, L.length - bodyRows);
8315
+ const lOff = Math.min(layersScroll, maxLScroll);
8316
+ const win = L.slice(lOff, lOff + bodyRows);
8317
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
8318
+ titleBar,
8319
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8320
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LAYERS " }),
8321
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " security layers, limits and patterns \xB7 insights window: last 7 days" }),
8322
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
8323
+ ] }),
8324
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: `${insights.data ? "" : spin + " loading \xB7 "}${maxLScroll ? `\u25BC${maxLScroll - lOff} more \xB7 \u2191\u2193 scroll \xB7 ` : ""}l or \u2190 back`, width: innerW }),
8325
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win }),
8326
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8327
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LAYERS " }),
8328
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "white", children: " rate limit \xB7 dlp \xB7 ghost \xB7 guard " }),
8329
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", children: " \u2191\u2193 scroll \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
8330
+ ] })
8331
+ ] });
8332
+ }
8104
8333
  if (mode === "detail" && detail) {
8105
8334
  const d = detailEntries;
8106
8335
  const allow = d.filter((e) => e.decision === "ALLOW").length;
@@ -8220,7 +8449,7 @@ function LivePanel({ active: active2 }) {
8220
8449
  ] }),
8221
8450
  /* @__PURE__ */ jsxs2(Box2, { height: colH, children: [
8222
8451
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
8223
- /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", width: colW }),
8452
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: "l = inspect", width: colW }),
8224
8453
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
8225
8454
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
8226
8455
  /* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: (rl?.mode ?? "?").padEnd(7) }),
@@ -8253,7 +8482,8 @@ function LivePanel({ active: active2 }) {
8253
8482
  " "
8254
8483
  ] }),
8255
8484
  /* @__PURE__ */ jsx2(Text2, { color: st.color, children: st.label.padEnd(7) }),
8256
- /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate2(r.isMe ? "this machine" : r.agent, 12).padEnd(13) }),
8485
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate2(r.agent, r.isMe ? 9 : 12).padEnd(r.isMe ? 10 : 13) }),
8486
+ r.isMe ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "me " }) : null,
8257
8487
  /* @__PURE__ */ jsxs2(Text2, { children: [
8258
8488
  String(r.calls).padStart(4),
8259
8489
  "c "
@@ -8319,7 +8549,7 @@ var init_Live = __esm({
8319
8549
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8320
8550
  BG = "#12234f";
8321
8551
  DIM_FLOOR = "#233457";
8322
- RING = join7(process.cwd(), ".solongate", ".eval-ring.jsonl");
8552
+ RING = join8(process.cwd(), ".solongate", ".eval-ring.jsonl");
8323
8553
  hhmmss = (ts) => {
8324
8554
  const d = new Date(ts);
8325
8555
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -8348,6 +8578,7 @@ var init_Live = __esm({
8348
8578
  ["f", "source filter: all \u2192 local \u2192 cloud"],
8349
8579
  ["/", "live search (tool, agent, command\u2026) \xB7 enter done"],
8350
8580
  ["s", "session picker"],
8581
+ ["l", "layers detail (rate limit \xB7 dlp \xB7 ghost \xB7 guard)"],
8351
8582
  ["e", "export visible rows \u2192 ~/.solongate/live-export.jsonl"],
8352
8583
  ["space", "copy mode: freeze screen for mouse selection"],
8353
8584
  ["esc", "back to menu"],
@@ -9008,9 +9239,9 @@ var init_Dlp = __esm({
9008
9239
  // src/tui/panels/Audit.tsx
9009
9240
  import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
9010
9241
  import TextInput4 from "ink-text-input";
9011
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
9012
- import { homedir as homedir6 } from "os";
9013
- import { join as join8 } from "path";
9242
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
9243
+ import { homedir as homedir7 } from "os";
9244
+ import { join as join9 } from "path";
9014
9245
  import { useState as useState6 } from "react";
9015
9246
  import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
9016
9247
  function loadLocalRows() {
@@ -9164,16 +9395,16 @@ function AuditPanel({ active: active2, focused }) {
9164
9395
  const doExport = (kind) => {
9165
9396
  setMsg({ text: "exporting\u2026", level: "ok" });
9166
9397
  const run12 = async () => {
9167
- const dir = join8(homedir6(), ".solongate");
9168
- const file = join8(dir, `audit-export-${source}.jsonl`);
9398
+ const dir = join9(homedir7(), ".solongate");
9399
+ const file = join9(dir, `audit-export-${source}.jsonl`);
9169
9400
  let rows2;
9170
9401
  if (kind === "page") rows2 = pageRows;
9171
9402
  else if (source === "cloud") {
9172
9403
  const r = await api.audit.list({ ...query, limit: 1e4, offset: 0 });
9173
9404
  rows2 = r.entries.map(cloudRow).sort((a, b) => b.at - a.at);
9174
9405
  } else rows2 = localFiltered;
9175
- mkdirSync5(dir, { recursive: true });
9176
- writeFileSync6(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
9406
+ mkdirSync6(dir, { recursive: true });
9407
+ writeFileSync7(file, rows2.map((x) => JSON.stringify(x)).join("\n") + (rows2.length ? "\n" : ""));
9177
9408
  return { n: rows2.length, file };
9178
9409
  };
9179
9410
  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" }));
@@ -10176,9 +10407,9 @@ var tui_exports = {};
10176
10407
  __export(tui_exports, {
10177
10408
  launchTui: () => launchTui
10178
10409
  });
10179
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "fs";
10180
- import { homedir as homedir7 } from "os";
10181
- import { join as join9 } from "path";
10410
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "fs";
10411
+ import { homedir as homedir8 } from "os";
10412
+ import { join as join10 } from "path";
10182
10413
  import { render } from "ink";
10183
10414
  import { jsx as jsx10 } from "react/jsx-runtime";
10184
10415
  async function launchTui() {
@@ -10189,11 +10420,11 @@ async function launchTui() {
10189
10420
  return;
10190
10421
  }
10191
10422
  process.stdout.write("\x1B[?1049h\x1B[H");
10192
- const debugLog = join9(homedir7(), ".solongate", "dataroom-debug.log");
10423
+ const debugLog = join10(homedir8(), ".solongate", "dataroom-debug.log");
10193
10424
  const saved = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
10194
10425
  const toFile = (level) => (...args) => {
10195
10426
  try {
10196
- mkdirSync6(join9(homedir7(), ".solongate"), { recursive: true });
10427
+ mkdirSync7(join10(homedir8(), ".solongate"), { recursive: true });
10197
10428
  appendFileSync2(debugLog, `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
10198
10429
  `);
10199
10430
  } catch {
@@ -10317,7 +10548,7 @@ var init_args = __esm({
10317
10548
  });
10318
10549
 
10319
10550
  // src/commands/policy.ts
10320
- import { readFileSync as readFileSync7 } from "fs";
10551
+ import { readFileSync as readFileSync8 } from "fs";
10321
10552
  async function run(argv) {
10322
10553
  const { positionals, flags } = parse(argv);
10323
10554
  const sub = positionals[0];
@@ -10492,7 +10723,7 @@ function printRules(rules) {
10492
10723
  }
10493
10724
  async function resolveRules(target) {
10494
10725
  if (target.endsWith(".json")) {
10495
- const parsed = JSON.parse(readFileSync7(target, "utf-8"));
10726
+ const parsed = JSON.parse(readFileSync8(target, "utf-8"));
10496
10727
  return parsed.rules ?? [];
10497
10728
  }
10498
10729
  const p = await api.policies.get(target);
@@ -10908,8 +11139,8 @@ var init_agents2 = __esm({
10908
11139
 
10909
11140
  // src/commands/doctor.ts
10910
11141
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
10911
- import { homedir as homedir8 } from "os";
10912
- import { join as join10 } from "path";
11142
+ import { homedir as homedir9 } from "os";
11143
+ import { join as join11 } from "path";
10913
11144
  async function run6(argv) {
10914
11145
  const { flags } = parse(argv);
10915
11146
  const json = flagBool(flags, "json");
@@ -10969,19 +11200,19 @@ var init_doctor = __esm({
10969
11200
  init_api_client();
10970
11201
  init_format();
10971
11202
  init_args();
10972
- LOCAL_LOG2 = join10(homedir8(), ".solongate", "local-logs", "solongate-audit.jsonl");
11203
+ LOCAL_LOG2 = join11(homedir9(), ".solongate", "local-logs", "solongate-audit.jsonl");
10973
11204
  }
10974
11205
  });
10975
11206
 
10976
11207
  // src/commands/watch.ts
10977
- import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
10978
- import { homedir as homedir9 } from "os";
10979
- import { join as join11 } from "path";
11208
+ import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync3, readSync as readSync2, statSync as statSync3 } from "fs";
11209
+ import { homedir as homedir10 } from "os";
11210
+ import { join as join12 } from "path";
10980
11211
  function tailLocal(file, maxBytes = 131072) {
10981
11212
  try {
10982
11213
  const size = statSync3(file).size;
10983
11214
  const start = Math.max(0, size - maxBytes);
10984
- const fd = openSync2(file, "r");
11215
+ const fd = openSync3(file, "r");
10985
11216
  const buf = Buffer.alloc(size - start);
10986
11217
  readSync2(fd, buf, 0, buf.length, start);
10987
11218
  closeSync2(fd);
@@ -11083,7 +11314,7 @@ var init_watch = __esm({
11083
11314
  init_cli_utils();
11084
11315
  init_args();
11085
11316
  init_format();
11086
- LOCAL_LOG3 = join11(homedir9(), ".solongate", "local-logs", "solongate-audit.jsonl");
11317
+ LOCAL_LOG3 = join12(homedir10(), ".solongate", "local-logs", "solongate-audit.jsonl");
11087
11318
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
11088
11319
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
11089
11320
  }
@@ -11374,10 +11605,10 @@ var init_commands = __esm({
11374
11605
  });
11375
11606
 
11376
11607
  // src/global-install.ts
11377
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync6, mkdirSync as mkdirSync7 } from "fs";
11378
- import { resolve as resolve4, join as join12, dirname } from "path";
11379
- import { homedir as homedir10 } from "os";
11380
- import { fileURLToPath } from "url";
11608
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, existsSync as existsSync6, mkdirSync as mkdirSync8 } from "fs";
11609
+ import { resolve as resolve4, join as join13, dirname as dirname2 } from "path";
11610
+ import { homedir as homedir11 } from "os";
11611
+ import { fileURLToPath as fileURLToPath2 } from "url";
11381
11612
  import { createInterface } from "readline";
11382
11613
  import { execFileSync as execFileSync2 } from "child_process";
11383
11614
  function lockFile(file) {
@@ -11439,10 +11670,10 @@ function unlockFile(file) {
11439
11670
  function protectedTargets() {
11440
11671
  const p = globalPaths();
11441
11672
  return [
11442
- join12(p.hooksDir, "guard.mjs"),
11443
- join12(p.hooksDir, "audit.mjs"),
11444
- join12(p.hooksDir, "stop.mjs"),
11445
- join12(p.hooksDir, "shield.mjs"),
11673
+ join13(p.hooksDir, "guard.mjs"),
11674
+ join13(p.hooksDir, "audit.mjs"),
11675
+ join13(p.hooksDir, "stop.mjs"),
11676
+ join13(p.hooksDir, "shield.mjs"),
11446
11677
  p.configPath,
11447
11678
  p.settingsPath
11448
11679
  ];
@@ -11454,26 +11685,26 @@ function unlockProtected() {
11454
11685
  for (const f of protectedTargets()) unlockFile(f);
11455
11686
  }
11456
11687
  function globalPaths() {
11457
- const home = homedir10();
11458
- const sgDir = join12(home, ".solongate");
11459
- const hooksDir = join12(sgDir, "hooks");
11460
- const claudeDir = join12(home, ".claude");
11688
+ const home = homedir11();
11689
+ const sgDir = join13(home, ".solongate");
11690
+ const hooksDir = join13(sgDir, "hooks");
11691
+ const claudeDir = join13(home, ".claude");
11461
11692
  return {
11462
11693
  home,
11463
11694
  sgDir,
11464
11695
  hooksDir,
11465
11696
  claudeDir,
11466
- settingsPath: join12(claudeDir, "settings.json"),
11467
- backupPath: join12(claudeDir, "settings.solongate.bak"),
11468
- configPath: join12(sgDir, "cloud-guard.json")
11697
+ settingsPath: join13(claudeDir, "settings.json"),
11698
+ backupPath: join13(claudeDir, "settings.solongate.bak"),
11699
+ configPath: join13(sgDir, "cloud-guard.json")
11469
11700
  };
11470
11701
  }
11471
11702
  function readHook(filename) {
11472
- return readFileSync8(join12(HOOKS_DIR, filename), "utf-8");
11703
+ return readFileSync9(join13(HOOKS_DIR, filename), "utf-8");
11473
11704
  }
11474
11705
  function readGuard() {
11475
- const bundled = join12(HOOKS_DIR, "guard.bundled.mjs");
11476
- return existsSync6(bundled) ? readFileSync8(bundled, "utf-8") : readHook("guard.mjs");
11706
+ const bundled = join13(HOOKS_DIR, "guard.bundled.mjs");
11707
+ return existsSync6(bundled) ? readFileSync9(bundled, "utf-8") : readHook("guard.mjs");
11477
11708
  }
11478
11709
  function ask(question) {
11479
11710
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -11507,18 +11738,18 @@ function shimTargets() {
11507
11738
  return [];
11508
11739
  }
11509
11740
  }
11510
- return [".bashrc", ".zshrc", ".profile"].map((f) => join12(homedir10(), f)).filter((f) => existsSync6(f));
11741
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join13(homedir11(), f)).filter((f) => existsSync6(f));
11511
11742
  }
11512
11743
  function writeShimBlock(file, block2) {
11513
11744
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
11514
- let content = existsSync6(file) ? readFileSync8(file, "utf-8") : "";
11745
+ let content = existsSync6(file) ? readFileSync9(file, "utf-8") : "";
11515
11746
  content = content.replace(re, "");
11516
11747
  if (block2) {
11517
11748
  if (content.length && !content.endsWith("\n")) content += "\n";
11518
11749
  content += block2 + "\n";
11519
11750
  }
11520
- mkdirSync7(dirname(file), { recursive: true });
11521
- writeFileSync7(file, content);
11751
+ mkdirSync8(dirname2(file), { recursive: true });
11752
+ writeFileSync8(file, content);
11522
11753
  }
11523
11754
  function installClaudeShim(shieldPath) {
11524
11755
  const real = resolveRealClaude();
@@ -11548,7 +11779,7 @@ async function runGlobalInstall(opts = {}) {
11548
11779
  let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
11549
11780
  if (!apiKey || apiKey === "sg_live_your_key_here") {
11550
11781
  try {
11551
- const cfg = JSON.parse(readFileSync8(p.configPath, "utf-8"));
11782
+ const cfg = JSON.parse(readFileSync9(p.configPath, "utf-8"));
11552
11783
  if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
11553
11784
  } catch {
11554
11785
  }
@@ -11561,22 +11792,22 @@ async function runGlobalInstall(opts = {}) {
11561
11792
  process.exit(1);
11562
11793
  }
11563
11794
  const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
11564
- mkdirSync7(p.hooksDir, { recursive: true });
11565
- mkdirSync7(p.claudeDir, { recursive: true });
11795
+ mkdirSync8(p.hooksDir, { recursive: true });
11796
+ mkdirSync8(p.claudeDir, { recursive: true });
11566
11797
  unlockProtected();
11567
- writeFileSync7(join12(p.hooksDir, "guard.mjs"), readGuard());
11568
- writeFileSync7(join12(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11569
- writeFileSync7(join12(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11570
- writeFileSync7(join12(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
11798
+ writeFileSync8(join13(p.hooksDir, "guard.mjs"), readGuard());
11799
+ writeFileSync8(join13(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
11800
+ writeFileSync8(join13(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
11801
+ writeFileSync8(join13(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
11571
11802
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
11572
- installClaudeShim(join12(p.hooksDir, "shield.mjs"));
11573
- writeFileSync7(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11803
+ installClaudeShim(join13(p.hooksDir, "shield.mjs"));
11804
+ writeFileSync8(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
11574
11805
  console.log(` Wrote ${p.configPath}`);
11575
11806
  let existing = {};
11576
11807
  if (existsSync6(p.settingsPath)) {
11577
- const raw = readFileSync8(p.settingsPath, "utf-8");
11808
+ const raw = readFileSync9(p.settingsPath, "utf-8");
11578
11809
  if (!existsSync6(p.backupPath)) {
11579
- writeFileSync7(p.backupPath, raw);
11810
+ writeFileSync8(p.backupPath, raw);
11580
11811
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
11581
11812
  }
11582
11813
  try {
@@ -11585,9 +11816,9 @@ async function runGlobalInstall(opts = {}) {
11585
11816
  existing = {};
11586
11817
  }
11587
11818
  }
11588
- const guardAbs = join12(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
11589
- const auditAbs = join12(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
11590
- const stopAbs = join12(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
11819
+ const guardAbs = join13(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
11820
+ const auditAbs = join13(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
11821
+ const stopAbs = join13(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
11591
11822
  const nodeBin = process.execPath.replace(/\\/g, "/");
11592
11823
  const call = process.platform === "win32" ? "& " : "";
11593
11824
  const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
@@ -11599,7 +11830,7 @@ async function runGlobalInstall(opts = {}) {
11599
11830
  Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
11600
11831
  }
11601
11832
  };
11602
- writeFileSync7(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11833
+ writeFileSync8(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
11603
11834
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
11604
11835
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
11605
11836
  lockProtected();
@@ -11610,7 +11841,7 @@ var __dirname, HOOKS_DIR, SHIM_BEGIN, SHIM_END;
11610
11841
  var init_global_install = __esm({
11611
11842
  "src/global-install.ts"() {
11612
11843
  "use strict";
11613
- __dirname = dirname(fileURLToPath(import.meta.url));
11844
+ __dirname = dirname2(fileURLToPath2(import.meta.url));
11614
11845
  HOOKS_DIR = resolve4(__dirname, "..", "hooks");
11615
11846
  SHIM_BEGIN = "# >>> SolonGate shield (auto secret redaction) >>>";
11616
11847
  SHIM_END = "# <<< SolonGate shield <<<";
@@ -11619,7 +11850,7 @@ var init_global_install = __esm({
11619
11850
 
11620
11851
  // src/login.ts
11621
11852
  var login_exports = {};
11622
- import { spawn as spawn3 } from "child_process";
11853
+ import { spawn as spawn4 } from "child_process";
11623
11854
  function startSpinner(text) {
11624
11855
  if (!process.stderr.isTTY) {
11625
11856
  process.stderr.write(` ${text}
@@ -11655,7 +11886,7 @@ function openBrowser2(url) {
11655
11886
  try {
11656
11887
  const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
11657
11888
  const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url];
11658
- const child = spawn3(cmd, args, { stdio: "ignore", detached: true });
11889
+ const child = spawn4(cmd, args, { stdio: "ignore", detached: true });
11659
11890
  child.on("error", () => {
11660
11891
  });
11661
11892
  child.unref();
@@ -11775,13 +12006,13 @@ __export(shield_exports, {
11775
12006
  });
11776
12007
  import { createServer, request as httpRequest } from "http";
11777
12008
  import { request as httpsRequest } from "https";
11778
- import { spawn as spawn4 } from "child_process";
12009
+ import { spawn as spawn5 } from "child_process";
11779
12010
  import { URL as URL2 } from "url";
11780
- import { readFileSync as readFileSync9, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
12011
+ import { readFileSync as readFileSync10, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
11781
12012
  import { resolve as resolve5 } from "path";
11782
- import { homedir as homedir11 } from "os";
12013
+ import { homedir as homedir12 } from "os";
11783
12014
  function findCacheFile() {
11784
- const dir = resolve5(homedir11(), ".solongate");
12015
+ const dir = resolve5(homedir12(), ".solongate");
11785
12016
  const envSel = process.env.SOLONGATE_AGENT_ID;
11786
12017
  if (envSel) {
11787
12018
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
@@ -11807,7 +12038,7 @@ function loadCfg() {
11807
12038
  try {
11808
12039
  const f = findCacheFile();
11809
12040
  if (f && existsSync7(f)) {
11810
- const c2 = JSON.parse(readFileSync9(f, "utf-8"));
12041
+ const c2 = JSON.parse(readFileSync10(f, "utf-8"));
11811
12042
  const d = c2?.security?.dlpRedact;
11812
12043
  const g = c2?.security?.ghost;
11813
12044
  const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
@@ -12035,7 +12266,7 @@ async function runShield() {
12035
12266
  const upstream = pickUpstream();
12036
12267
  const { port, close } = await startProxy(upstream);
12037
12268
  log4(`redacting secrets on the LLM path \u2192 masking before ${upstream.host} (127.0.0.1:${port})`);
12038
- const child = spawn4(cmd[0], cmd.slice(1), {
12269
+ const child = spawn5(cmd[0], cmd.slice(1), {
12039
12270
  stdio: "inherit",
12040
12271
  env: { ...process.env, ANTHROPIC_BASE_URL: `http://127.0.0.1:${port}` },
12041
12272
  shell: process.platform === "win32"
@@ -12095,9 +12326,9 @@ __export(logs_server_exports, {
12095
12326
  runLogsServer: () => runLogsServer
12096
12327
  });
12097
12328
  import { createServer as createServer2 } from "http";
12098
- import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
12099
- import { resolve as resolve6, join as join13, isAbsolute } from "path";
12100
- import { homedir as homedir12 } from "os";
12329
+ import { readFileSync as readFileSync11, statSync as statSync5 } from "fs";
12330
+ import { resolve as resolve6, join as join14, isAbsolute } from "path";
12331
+ import { homedir as homedir13 } from "os";
12101
12332
  import { readdirSync as readdirSync2 } from "fs";
12102
12333
  function allowedOrigins() {
12103
12334
  const base = [
@@ -12114,15 +12345,15 @@ function resolveLocalLogDir(rawPath) {
12114
12345
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
12115
12346
  if (!dir) return null;
12116
12347
  if (isAbsolute(dir)) return dir;
12117
- return resolve6(homedir12(), ".solongate", "local-logs");
12348
+ return resolve6(homedir13(), ".solongate", "local-logs");
12118
12349
  }
12119
12350
  async function findLogDir() {
12120
- const base = resolve6(homedir12(), ".solongate");
12351
+ const base = resolve6(homedir13(), ".solongate");
12121
12352
  try {
12122
12353
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
12123
12354
  for (const f of files) {
12124
12355
  try {
12125
- const c2 = JSON.parse(readFileSync10(join13(base, f), "utf-8"));
12356
+ const c2 = JSON.parse(readFileSync11(join14(base, f), "utf-8"));
12126
12357
  const p = c2?.security?.localLogs?.path;
12127
12358
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
12128
12359
  } catch {
@@ -12131,7 +12362,7 @@ async function findLogDir() {
12131
12362
  } catch {
12132
12363
  }
12133
12364
  try {
12134
- const cfgRaw = readFileSync10(join13(base, "cloud-guard.json"), "utf-8");
12365
+ const cfgRaw = readFileSync11(join14(base, "cloud-guard.json"), "utf-8");
12135
12366
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
12136
12367
  if (apiKey) {
12137
12368
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -12160,7 +12391,7 @@ function setCors(req, res) {
12160
12391
  }
12161
12392
  function fileInfo(dir) {
12162
12393
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
12163
- const file = join13(dir, LOG_FILENAME);
12394
+ const file = join14(dir, LOG_FILENAME);
12164
12395
  try {
12165
12396
  const st = statSync5(file);
12166
12397
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -12221,7 +12452,7 @@ async function runLogsServer() {
12221
12452
  return;
12222
12453
  }
12223
12454
  try {
12224
- const text = readFileSync10(info.file, "utf-8");
12455
+ const text = readFileSync11(info.file, "utf-8");
12225
12456
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
12226
12457
  res.end(text);
12227
12458
  } catch {
@@ -12273,7 +12504,7 @@ var init_logs_server = __esm({
12273
12504
 
12274
12505
  // src/inject.ts
12275
12506
  var inject_exports = {};
12276
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync8, copyFileSync } from "fs";
12507
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, existsSync as existsSync8, copyFileSync } from "fs";
12277
12508
  import { resolve as resolve7 } from "path";
12278
12509
  import { execSync } from "child_process";
12279
12510
  function parseInjectArgs(argv) {
@@ -12333,7 +12564,7 @@ WHAT IT DOES
12333
12564
  function detectProject() {
12334
12565
  if (!existsSync8(resolve7("package.json"))) return false;
12335
12566
  try {
12336
- const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
12567
+ const pkg = JSON.parse(readFileSync12(resolve7("package.json"), "utf-8"));
12337
12568
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
12338
12569
  return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
12339
12570
  } catch {
@@ -12342,7 +12573,7 @@ function detectProject() {
12342
12573
  }
12343
12574
  function findTsEntryFile() {
12344
12575
  try {
12345
- const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
12576
+ const pkg = JSON.parse(readFileSync12(resolve7("package.json"), "utf-8"));
12346
12577
  if (pkg.bin) {
12347
12578
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
12348
12579
  if (typeof binPath === "string") {
@@ -12369,7 +12600,7 @@ function findTsEntryFile() {
12369
12600
  const full = resolve7(c2);
12370
12601
  if (existsSync8(full)) {
12371
12602
  try {
12372
- const content = readFileSync11(full, "utf-8");
12603
+ const content = readFileSync12(full, "utf-8");
12373
12604
  if (content.includes("McpServer") || content.includes("McpServer")) {
12374
12605
  return full;
12375
12606
  }
@@ -12389,7 +12620,7 @@ function detectPackageManager() {
12389
12620
  }
12390
12621
  function installSdk() {
12391
12622
  try {
12392
- const pkg = JSON.parse(readFileSync11(resolve7("package.json"), "utf-8"));
12623
+ const pkg = JSON.parse(readFileSync12(resolve7("package.json"), "utf-8"));
12393
12624
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
12394
12625
  if (allDeps["@solongate/proxy"]) {
12395
12626
  log3(" @solongate/proxy already installed");
@@ -12410,7 +12641,7 @@ function installSdk() {
12410
12641
  }
12411
12642
  }
12412
12643
  function injectTypeScript(filePath) {
12413
- const original = readFileSync11(filePath, "utf-8");
12644
+ const original = readFileSync12(filePath, "utf-8");
12414
12645
  const changes = [];
12415
12646
  let modified = original;
12416
12647
  if (modified.includes("SecureMcpServer")) {
@@ -12581,7 +12812,7 @@ async function main2() {
12581
12812
  log3("");
12582
12813
  log3(` Backup: ${backupPath}`);
12583
12814
  }
12584
- writeFileSync8(entryFile, result.modified);
12815
+ writeFileSync9(entryFile, result.modified);
12585
12816
  log3("");
12586
12817
  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");
12587
12818
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -12610,8 +12841,8 @@ var init_inject = __esm({
12610
12841
 
12611
12842
  // src/create.ts
12612
12843
  var create_exports = {};
12613
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
12614
- import { resolve as resolve8, join as join14 } from "path";
12844
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, existsSync as existsSync9 } from "fs";
12845
+ import { resolve as resolve8, join as join15 } from "path";
12615
12846
  import { execSync as execSync2 } from "child_process";
12616
12847
  function withSpinner(message, fn) {
12617
12848
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -12690,8 +12921,8 @@ EXAMPLES
12690
12921
  `);
12691
12922
  }
12692
12923
  function createProject(dir, name, _policy) {
12693
- writeFileSync9(
12694
- join14(dir, "package.json"),
12924
+ writeFileSync10(
12925
+ join15(dir, "package.json"),
12695
12926
  JSON.stringify(
12696
12927
  {
12697
12928
  name,
@@ -12720,8 +12951,8 @@ function createProject(dir, name, _policy) {
12720
12951
  2
12721
12952
  ) + "\n"
12722
12953
  );
12723
- writeFileSync9(
12724
- join14(dir, "tsconfig.json"),
12954
+ writeFileSync10(
12955
+ join15(dir, "tsconfig.json"),
12725
12956
  JSON.stringify(
12726
12957
  {
12727
12958
  compilerOptions: {
@@ -12741,9 +12972,9 @@ function createProject(dir, name, _policy) {
12741
12972
  2
12742
12973
  ) + "\n"
12743
12974
  );
12744
- mkdirSync8(join14(dir, "src"), { recursive: true });
12745
- writeFileSync9(
12746
- join14(dir, "src", "index.ts"),
12975
+ mkdirSync9(join15(dir, "src"), { recursive: true });
12976
+ writeFileSync10(
12977
+ join15(dir, "src", "index.ts"),
12747
12978
  `#!/usr/bin/env node
12748
12979
 
12749
12980
  console.log = (...args: unknown[]) => {
@@ -12784,8 +13015,8 @@ console.log('');
12784
13015
  console.log('Press Ctrl+C to stop.');
12785
13016
  `
12786
13017
  );
12787
- writeFileSync9(
12788
- join14(dir, ".mcp.json"),
13018
+ writeFileSync10(
13019
+ join15(dir, ".mcp.json"),
12789
13020
  JSON.stringify(
12790
13021
  {
12791
13022
  mcpServers: {
@@ -12802,13 +13033,13 @@ console.log('Press Ctrl+C to stop.');
12802
13033
  2
12803
13034
  ) + "\n"
12804
13035
  );
12805
- writeFileSync9(
12806
- join14(dir, ".env"),
13036
+ writeFileSync10(
13037
+ join15(dir, ".env"),
12807
13038
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
12808
13039
  `
12809
13040
  );
12810
- writeFileSync9(
12811
- join14(dir, ".gitignore"),
13041
+ writeFileSync10(
13042
+ join15(dir, ".gitignore"),
12812
13043
  `node_modules/
12813
13044
  dist/
12814
13045
  *.solongate-backup
@@ -12827,7 +13058,7 @@ async function main3() {
12827
13058
  process.exit(1);
12828
13059
  }
12829
13060
  withSpinner(`Setting up ${opts.name}...`, () => {
12830
- mkdirSync8(dir, { recursive: true });
13061
+ mkdirSync9(dir, { recursive: true });
12831
13062
  createProject(dir, opts.name, opts.policy);
12832
13063
  });
12833
13064
  if (!opts.noInstall) {
@@ -12901,14 +13132,14 @@ var init_create = __esm({
12901
13132
 
12902
13133
  // src/pull-push.ts
12903
13134
  var pull_push_exports = {};
12904
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, existsSync as existsSync10 } from "fs";
13135
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, existsSync as existsSync10 } from "fs";
12905
13136
  import { resolve as resolve9 } from "path";
12906
13137
  function loadEnv() {
12907
13138
  if (process.env.SOLONGATE_API_KEY) return;
12908
13139
  const envPath = resolve9(".env");
12909
13140
  if (!existsSync10(envPath)) return;
12910
13141
  try {
12911
- const content = readFileSync12(envPath, "utf-8");
13142
+ const content = readFileSync13(envPath, "utf-8");
12912
13143
  for (const line of content.split("\n")) {
12913
13144
  const trimmed = line.trim();
12914
13145
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -13096,7 +13327,7 @@ async function pull(apiKey, file, policyId) {
13096
13327
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
13097
13328
  const { id: _id, ...policyWithoutId } = policy;
13098
13329
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
13099
- writeFileSync10(file, json, "utf-8");
13330
+ writeFileSync11(file, json, "utf-8");
13100
13331
  log5("");
13101
13332
  log5(green2(" Saved to: ") + file);
13102
13333
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -13125,7 +13356,7 @@ async function push(apiKey, file, policyId) {
13125
13356
  log5(" solongate-proxy list");
13126
13357
  process.exit(1);
13127
13358
  }
13128
- const content = readFileSync12(file, "utf-8");
13359
+ const content = readFileSync13(file, "utf-8");
13129
13360
  let policy;
13130
13361
  try {
13131
13362
  policy = JSON.parse(content);
@@ -15285,7 +15516,7 @@ var SolonGate = class {
15285
15516
  */
15286
15517
  startPolicyPolling() {
15287
15518
  const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
15288
- let currentVersion = 0;
15519
+ let currentVersion2 = 0;
15289
15520
  const timer = setInterval(async () => {
15290
15521
  try {
15291
15522
  const res = await fetch(`${apiUrl}/api/v1/policies/default`, {
@@ -15295,7 +15526,7 @@ var SolonGate = class {
15295
15526
  if (!res.ok) return;
15296
15527
  const data = await res.json();
15297
15528
  const version = Number(data._version ?? 0);
15298
- if (version !== currentVersion && version > 0) {
15529
+ if (version !== currentVersion2 && version > 0) {
15299
15530
  const policySet = {
15300
15531
  id: String(data.id ?? "cloud"),
15301
15532
  name: String(data.name ?? "Cloud Policy"),
@@ -15307,7 +15538,7 @@ var SolonGate = class {
15307
15538
  };
15308
15539
  this.policyEngine.loadPolicySet(policySet);
15309
15540
  await this.loadCloudWasm(apiUrl, policySet.id);
15310
- currentVersion = version;
15541
+ currentVersion2 = version;
15311
15542
  }
15312
15543
  } catch {
15313
15544
  }
@@ -16275,6 +16506,12 @@ function printWelcome() {
16275
16506
  }
16276
16507
  async function main5() {
16277
16508
  const subcommand = process.argv[2];
16509
+ if (IS_HUMAN_CLI) {
16510
+ const tuiBound = subcommand === "dataroom" || process.argv.length <= 2 && process.stdout.isTTY && process.stdin.isTTY;
16511
+ const { maybeSelfUpdate: maybeSelfUpdate2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
16512
+ maybeSelfUpdate2(tuiBound ? () => {
16513
+ } : void 0);
16514
+ }
16278
16515
  if (process.argv.length <= 2) {
16279
16516
  if (process.stdout.isTTY && process.stdin.isTTY) {
16280
16517
  const { launchTui: launchTui2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
@@ -0,0 +1,6 @@
1
+ export declare function currentVersion(): string;
2
+ /**
3
+ * Fire-and-forget startup hook. `notify` writes the human-facing one-liner —
4
+ * callers pass a stderr writer so the notice never lands in piped stdout.
5
+ */
6
+ export declare function maybeSelfUpdate(notify?: (line: string) => void): void;
package/dist/tui/index.js CHANGED
@@ -920,6 +920,7 @@ var LIVE_HELP = [
920
920
  ["f", "source filter: all \u2192 local \u2192 cloud"],
921
921
  ["/", "live search (tool, agent, command\u2026) \xB7 enter done"],
922
922
  ["s", "session picker"],
923
+ ["l", "layers detail (rate limit \xB7 dlp \xB7 ghost \xB7 guard)"],
923
924
  ["e", "export visible rows \u2192 ~/.solongate/live-export.jsonl"],
924
925
  ["space", "copy mode: freeze screen for mouse selection"],
925
926
  ["esc", "back to menu"],
@@ -970,6 +971,7 @@ function LivePanel({ active: active2 }) {
970
971
  const [detailScroll, setDetailScroll] = useState2(0);
971
972
  const [inspect, setInspect] = useState2(null);
972
973
  const [inspectScroll, setInspectScroll] = useState2(0);
974
+ const [layersScroll, setLayersScroll] = useState2(0);
973
975
  const inspectFromRef = useRef2("stream");
974
976
  const seenRef = useRef2(/* @__PURE__ */ new Set());
975
977
  const lastLocalTs = useRef2(0);
@@ -1369,6 +1371,14 @@ function LivePanel({ active: active2 }) {
1369
1371
  else if (key.pageDown) setInspectScroll((n) => n + 10);
1370
1372
  return;
1371
1373
  }
1374
+ if (mode === "layers") {
1375
+ if (key.leftArrow || input === "l") setMode("stream");
1376
+ else if (key.upArrow) setLayersScroll((n) => Math.max(0, n - 1));
1377
+ else if (key.downArrow) setLayersScroll((n) => n + 1);
1378
+ else if (key.pageUp) setLayersScroll((n) => Math.max(0, n - 10));
1379
+ else if (key.pageDown) setLayersScroll((n) => n + 10);
1380
+ return;
1381
+ }
1372
1382
  if (mode === "detail") {
1373
1383
  const maxD = Math.max(0, detailEntries.length - 1);
1374
1384
  if (key.leftArrow) {
@@ -1418,6 +1428,9 @@ function LivePanel({ active: active2 }) {
1418
1428
  } else if (input === "s") {
1419
1429
  setPickIdx(0);
1420
1430
  setMode("pick");
1431
+ } else if (input === "l") {
1432
+ setLayersScroll(0);
1433
+ setMode("layers");
1421
1434
  } else if (input === "w") void doAction("whitelist");
1422
1435
  else if (input === "b") void doAction("block");
1423
1436
  else if (input === "d") toggleSignal("deny");
@@ -1537,6 +1550,87 @@ function LivePanel({ active: active2 }) {
1537
1550
  ] })
1538
1551
  ] });
1539
1552
  }
1553
+ if (mode === "layers") {
1554
+ const modeColor2 = (m) => m === "block" || m === "on" ? theme.ok : m === "detect" ? theme.warn : theme.dim;
1555
+ const barW = Math.max(10, Math.min(40, innerW - 30));
1556
+ const burstsInBuf = mergedAll.filter((e) => e.burst).length;
1557
+ const dlpInBuf = mergedAll.filter((e) => e.dlp).length;
1558
+ const allHits = ins.dlpByPattern ?? [];
1559
+ const maxHit = allHits[0]?.count ?? 1;
1560
+ const L = [];
1561
+ const push = (el) => L.push(/* @__PURE__ */ jsx2(Box2, { children: el }, L.length));
1562
+ const blank = () => push(/* @__PURE__ */ jsx2(Text2, { children: " " }));
1563
+ const section = (label, m, note) => push(
1564
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1565
+ /* @__PURE__ */ jsxs2(Text2, { bold: true, color: theme.accentBright, children: [
1566
+ "\u258E",
1567
+ label.padEnd(10)
1568
+ ] }),
1569
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: modeColor2(m), children: (m ?? "?").padEnd(8) }),
1570
+ note ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: note }) : null
1571
+ ] })
1572
+ );
1573
+ const kv = (k, v) => push(
1574
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1575
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: (" " + k).padEnd(16) }),
1576
+ v
1577
+ ] })
1578
+ );
1579
+ section("RATELIMIT", rl?.mode, rl?.mode === "block" ? "over-limit calls are DENIED" : rl?.mode === "detect" ? "over-limit calls are observed & flagged rl:yes" : "no limit enforcement");
1580
+ kv("limits", /* @__PURE__ */ jsx2(Text2, { children: rl?.perMinute || rl?.perHour || rl?.perDay ? `${rl?.perMinute || "\u2014"}/min \xB7 ${rl?.perHour || "\u2014"}/hour \xB7 ${rl?.perDay || "\u2014"}/day` : "none configured" }));
1581
+ kv("load now", /* @__PURE__ */ jsx2(Text2, { children: `${minuteNow} calls in the last 60s` }));
1582
+ if (rl?.perMinute) push(/* @__PURE__ */ jsx2(HBar, { label: " load/min", value: minuteNow, max: rl.perMinute, width: barW, color: minuteNow > rl.perMinute ? theme.bad : theme.accent }));
1583
+ kv("bursts", /* @__PURE__ */ jsx2(Text2, { color: burstsInBuf ? theme.warn : theme.dim, children: `${burstsInBuf} flagged in the live buffer` }));
1584
+ blank();
1585
+ section("DLP", dl?.mode, dl?.mode === "block" ? "secrets in arguments are DENIED + redacted" : dl?.mode === "detect" ? "secrets observed & flagged dlp:yes, output redacted" : "no secret scanning");
1586
+ kv("hits", /* @__PURE__ */ jsx2(Text2, { color: dlpInBuf ? theme.bad : theme.dim, children: `${dlpInBuf} flagged in the live buffer` }));
1587
+ const builtin = dl?.patterns ?? [];
1588
+ kv("builtin", /* @__PURE__ */ jsx2(Text2, { children: builtin.length ? `${builtin.length} patterns enabled` : "none enabled" }));
1589
+ for (const line of wrapLines(builtin.join(" \xB7 "), Math.max(20, innerW - 18))) push(/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + line }));
1590
+ const custom = dl?.custom ?? [];
1591
+ kv("custom", /* @__PURE__ */ jsx2(Text2, { children: custom.length ? `${custom.length} patterns` : "none" }));
1592
+ for (const c2 of custom)
1593
+ push(
1594
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1595
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " " }),
1596
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: truncate(c2.name ?? "custom", 24).padEnd(25) }),
1597
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: truncate(c2.re ?? "", Math.max(10, innerW - 44)) })
1598
+ ] })
1599
+ );
1600
+ kv("pattern hits", /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: allHits.length ? "last 7 days" : "none in the last 7 days" }));
1601
+ for (const h of allHits) push(/* @__PURE__ */ jsx2(HBar, { label: " " + truncate(h.pattern, 10), value: h.count, max: maxHit, width: barW, color: theme.bad }));
1602
+ blank();
1603
+ section("GHOST", gh?.mode, gh?.mode === "on" ? "matching paths are hidden from agents" : "no hidden paths");
1604
+ const ghostPats = gh?.patterns ?? [];
1605
+ kv("patterns", /* @__PURE__ */ jsx2(Text2, { children: ghostPats.length ? `${ghostPats.length}` : "none" }));
1606
+ for (const line of wrapLines(ghostPats.join(" \xB7 "), Math.max(20, innerW - 18))) push(/* @__PURE__ */ jsx2(Text2, { wrap: "truncate", color: theme.dim, children: " " + line }));
1607
+ blank();
1608
+ section("GUARD", guard.data ? guard.data.up_to_date ? "ok" : "stale" : "?", "the PreToolUse hook enforcing all of the above");
1609
+ kv(
1610
+ "hooks",
1611
+ guard.data ? /* @__PURE__ */ jsx2(Text2, { color: guard.data.up_to_date ? theme.ok : theme.warn, children: `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? " (latest)" : ` \u2192 v${guard.data.latest} available`} \xB7 ${guard.data.device_count} device${guard.data.device_count === 1 ? "" : "s"}` }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "loading\u2026" })
1612
+ );
1613
+ kv("local eval", ring ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: `avg ${ring.avgMs}ms over ${ring.count} recent calls` }) : /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "no local ring in cwd" }));
1614
+ const bodyRows = Math.max(4, rows - 5);
1615
+ const maxLScroll = Math.max(0, L.length - bodyRows);
1616
+ const lOff = Math.min(layersScroll, maxLScroll);
1617
+ const win = L.slice(lOff, lOff + bodyRows);
1618
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
1619
+ titleBar,
1620
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1621
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LAYERS " }),
1622
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " security layers, limits and patterns \xB7 insights window: last 7 days" }),
1623
+ /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2190 back" })
1624
+ ] }),
1625
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: `${insights.data ? "" : spin + " loading \xB7 "}${maxLScroll ? `\u25BC${maxLScroll - lOff} more \xB7 \u2191\u2193 scroll \xB7 ` : ""}l or \u2190 back`, width: innerW }),
1626
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", height: bodyRows, overflow: "hidden", children: win }),
1627
+ /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1628
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", bold: true, children: " LAYERS " }),
1629
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "white", children: " rate limit \xB7 dlp \xB7 ghost \xB7 guard " }),
1630
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "white", children: " \u2191\u2193 scroll \xB7 ? all keys \xB7 \u2190 back \xB7 esc menu " })
1631
+ ] })
1632
+ ] });
1633
+ }
1540
1634
  if (mode === "detail" && detail) {
1541
1635
  const d = detailEntries;
1542
1636
  const allow = d.filter((e) => e.decision === "ALLOW").length;
@@ -1656,7 +1750,7 @@ function LivePanel({ active: active2 }) {
1656
1750
  ] }),
1657
1751
  /* @__PURE__ */ jsxs2(Box2, { height: colH, children: [
1658
1752
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: colW, marginRight: 2, overflow: "hidden", children: [
1659
- /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", width: colW }),
1753
+ /* @__PURE__ */ jsx2(PaneTitle, { label: "LAYERS", extra: "l = inspect", width: colW }),
1660
1754
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
1661
1755
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "RATELIMIT ".padEnd(10) }),
1662
1756
  /* @__PURE__ */ jsx2(Text2, { color: rl?.mode === "block" ? theme.ok : rl?.mode === "detect" ? theme.warn : theme.dim, children: (rl?.mode ?? "?").padEnd(7) }),
@@ -1689,7 +1783,8 @@ function LivePanel({ active: active2 }) {
1689
1783
  " "
1690
1784
  ] }),
1691
1785
  /* @__PURE__ */ jsx2(Text2, { color: st.color, children: st.label.padEnd(7) }),
1692
- /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate(r.isMe ? "this machine" : r.agent, 12).padEnd(13) }),
1786
+ /* @__PURE__ */ jsx2(Text2, { color: theme.accentBright, children: truncate(r.agent, r.isMe ? 9 : 12).padEnd(r.isMe ? 10 : 13) }),
1787
+ r.isMe ? /* @__PURE__ */ jsx2(Text2, { color: theme.ok, children: "me " }) : null,
1693
1788
  /* @__PURE__ */ jsxs2(Text2, { children: [
1694
1789
  String(r.calls).padStart(4),
1695
1790
  "c "
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.44",
3
+ "version": "0.81.46",
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": {