@solongate/proxy 0.79.0 → 0.81.0

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
@@ -6702,6 +6702,34 @@ var init_client = __esm({
6702
6702
  }
6703
6703
  });
6704
6704
 
6705
+ // src/tui/config.ts
6706
+ import { readFileSync as readFileSync5 } from "fs";
6707
+ import { homedir as homedir3 } from "os";
6708
+ import { join as join5 } from "path";
6709
+ function loadConfig() {
6710
+ if (cached2) return cached2;
6711
+ const defaults = { notifications: true };
6712
+ try {
6713
+ const raw = readFileSync5(join5(homedir3(), ".solongate", "tui-config.json"), "utf-8");
6714
+ const j = JSON.parse(raw);
6715
+ cached2 = {
6716
+ notifications: j.notifications !== false,
6717
+ accent: typeof j.accent === "string" ? j.accent : void 0,
6718
+ pollMs: typeof j.pollMs === "number" ? j.pollMs : void 0
6719
+ };
6720
+ } catch {
6721
+ cached2 = defaults;
6722
+ }
6723
+ return cached2;
6724
+ }
6725
+ var cached2;
6726
+ var init_config2 = __esm({
6727
+ "src/tui/config.ts"() {
6728
+ "use strict";
6729
+ cached2 = null;
6730
+ }
6731
+ });
6732
+
6705
6733
  // src/tui/theme.ts
6706
6734
  function decisionColor(decision) {
6707
6735
  const d = (decision || "").toUpperCase();
@@ -6735,13 +6763,15 @@ function ago(ts) {
6735
6763
  if (s < 86400) return `${Math.floor(s / 3600)}h`;
6736
6764
  return `${Math.floor(s / 86400)}d`;
6737
6765
  }
6738
- var theme, BLOCKS;
6766
+ var accent, theme, BLOCKS;
6739
6767
  var init_theme = __esm({
6740
6768
  "src/tui/theme.ts"() {
6741
6769
  "use strict";
6770
+ init_config2();
6771
+ accent = loadConfig().accent;
6742
6772
  theme = {
6743
- accent: "cyan",
6744
- accentBright: "#5a8ce6",
6773
+ accent: accent || "cyan",
6774
+ accentBright: accent || "#5a8ce6",
6745
6775
  ok: "green",
6746
6776
  warn: "yellow",
6747
6777
  bad: "red",
@@ -6875,8 +6905,10 @@ __export(settings_exports, {
6875
6905
  getGuardStatus: () => getGuardStatus,
6876
6906
  getRateLimitHistory: () => getRateLimitHistory,
6877
6907
  getSecurityLayers: () => getSecurityLayers,
6908
+ getSelfProtection: () => getSelfProtection,
6878
6909
  getWebhooks: () => getWebhooks,
6879
- setSecurityLayers: () => setSecurityLayers
6910
+ setSecurityLayers: () => setSecurityLayers,
6911
+ setSelfProtection: () => setSelfProtection
6880
6912
  });
6881
6913
  function getSecurityLayers() {
6882
6914
  return request("GET", "/settings/security-layers");
@@ -6893,6 +6925,12 @@ function clearRateLimitHistory() {
6893
6925
  function getGuardStatus() {
6894
6926
  return request("GET", "/settings/guard-status");
6895
6927
  }
6928
+ function getSelfProtection() {
6929
+ return request("GET", "/settings/self-protection");
6930
+ }
6931
+ function setSelfProtection(enabled) {
6932
+ return request("PUT", "/settings/self-protection", { body: { enabled } });
6933
+ }
6896
6934
  function getAlerts() {
6897
6935
  return request("GET", "/settings/denial-alerts");
6898
6936
  }
@@ -7100,9 +7138,9 @@ var init_hooks = __esm({
7100
7138
  import { Box as Box2, Text as Text2, useInput } from "ink";
7101
7139
  import TextInput from "ink-text-input";
7102
7140
  import { spawn } from "child_process";
7103
- import { closeSync, openSync, readSync, statSync } from "fs";
7104
- import { homedir as homedir3 } from "os";
7105
- import { join as join5 } from "path";
7141
+ import { closeSync, mkdirSync as mkdirSync3, openSync, readSync, statSync, writeFileSync as writeFileSync3 } from "fs";
7142
+ import { homedir as homedir4 } from "os";
7143
+ import { join as join6 } from "path";
7106
7144
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7107
7145
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7108
7146
  function tailLines(file, maxBytes = 131072) {
@@ -7412,6 +7450,7 @@ function LivePanel({ active: active2 }) {
7412
7450
  (id, title, msg, level = "warn") => {
7413
7451
  pushLog(`${level === "bad" ? "\u26D4" : "\u23F8"} ${msg}`, level);
7414
7452
  setAlerts((a) => [...a.filter((x) => x.id !== id), { id, msg, level, until: Date.now() + 15e3 }].slice(-4));
7453
+ if (!CONFIG.notifications) return;
7415
7454
  try {
7416
7455
  process.stdout.write("\x07");
7417
7456
  } catch {
@@ -7639,7 +7678,16 @@ function LivePanel({ active: active2 }) {
7639
7678
  else if (input === "d") toggleSignal("deny");
7640
7679
  else if (input === "x") toggleSignal("dlp");
7641
7680
  else if (input === "r") toggleSignal("ratelimit");
7642
- else if (key.downArrow) setSel((n) => Math.min(visibleDesc.length - 1, n + 1));
7681
+ else if (input === "e") {
7682
+ const file = join6(homedir4(), ".solongate", "live-export.jsonl");
7683
+ try {
7684
+ mkdirSync3(join6(homedir4(), ".solongate"), { recursive: true });
7685
+ writeFileSync3(file, visibleDesc.map((x) => JSON.stringify(x)).join("\n") + "\n");
7686
+ setActionMsg({ text: `\u2713 exported ${visibleDesc.length} lines \u2192 ${file}`, level: "ok", until: Date.now() + 6e3 });
7687
+ } catch (err2) {
7688
+ setActionMsg({ text: "\u2717 export failed: " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 6e3 });
7689
+ }
7690
+ } else if (key.downArrow) setSel((n) => Math.min(visibleDesc.length - 1, n + 1));
7643
7691
  else if (key.upArrow) setSel((n) => Math.max(0, n - 1));
7644
7692
  else if (key.pageDown) setSel((n) => Math.min(visibleDesc.length - 1, n + streamBodyRows));
7645
7693
  else if (key.pageUp) setSel((n) => Math.max(0, n - streamBodyRows));
@@ -7888,18 +7936,20 @@ function LivePanel({ active: active2 }) {
7888
7936
  ] })
7889
7937
  ] });
7890
7938
  }
7891
- var SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE;
7939
+ var CONFIG, SPIN, BG, DIM_FLOOR, LOCAL_LOG, RING, hhmmss, fmtUp, FILTERS, sessStatus, STATUS_STYLE;
7892
7940
  var init_Live = __esm({
7893
7941
  "src/tui/panels/Live.tsx"() {
7894
7942
  "use strict";
7895
7943
  init_api_client();
7944
+ init_config2();
7896
7945
  init_hooks();
7897
7946
  init_theme();
7947
+ CONFIG = loadConfig();
7898
7948
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7899
7949
  BG = "#12234f";
7900
7950
  DIM_FLOOR = "#233457";
7901
- LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
7902
- RING = join5(process.cwd(), ".solongate", ".eval-ring.jsonl");
7951
+ LOCAL_LOG = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
7952
+ RING = join6(process.cwd(), ".solongate", ".eval-ring.jsonl");
7903
7953
  hhmmss = (ts) => {
7904
7954
  const d = new Date(ts);
7905
7955
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -7923,7 +7973,7 @@ var init_Live = __esm({
7923
7973
  import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
7924
7974
  import TextInput2 from "ink-text-input";
7925
7975
  import { useEffect as useEffect3, useState as useState3 } from "react";
7926
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
7976
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
7927
7977
  function ruleSummary(r) {
7928
7978
  const bits = [];
7929
7979
  for (const [g, tag] of [["commandConstraints", "cmd"], ["pathConstraints", "path"], ["filenameConstraints", "file"], ["urlConstraints", "url"]]) {
@@ -7940,6 +7990,7 @@ function PoliciesPanel({ focused }) {
7940
7990
  const [pi, setPi] = useState3(0);
7941
7991
  const [ri, setRi] = useState3(0);
7942
7992
  const [fi, setFi] = useState3(0);
7993
+ const [vi, setVi] = useState3(0);
7943
7994
  const [rules, setRules] = useState3([]);
7944
7995
  const [mode, setMode] = useState3("denylist");
7945
7996
  const [dirty, setDirty] = useState3(false);
@@ -7948,6 +7999,15 @@ function PoliciesPanel({ focused }) {
7948
7999
  const [status, setStatus] = useState3(null);
7949
8000
  const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
7950
8001
  const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
8002
+ const versionsQ = useLoader(
8003
+ () => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
8004
+ [view, selected?.id]
8005
+ );
8006
+ const diffVer = versionsQ.data?.versions[vi]?.version;
8007
+ const diffQ = useLoader(
8008
+ () => view === "versions" && selected && diffVer !== void 0 ? api.policies.get(selected.id, diffVer) : Promise.resolve(null),
8009
+ [view, selected?.id, diffVer]
8010
+ );
7951
8011
  useEffect3(() => {
7952
8012
  if (detail.data) {
7953
8013
  setRules(detail.data.rules);
@@ -8022,10 +8082,41 @@ function PoliciesPanel({ focused }) {
8022
8082
  }
8023
8083
  } else if (input === "m") {
8024
8084
  mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
8085
+ } else if (input === "n") {
8086
+ const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true };
8087
+ mutate([nr, ...rules]);
8088
+ setRi(0);
8089
+ setFi(0);
8090
+ setView("rule");
8091
+ } else if (input === "D") {
8092
+ setStatus("Dry-running\u2026");
8093
+ void api.policies.dryRun({ rules, mode }).then((res) => setStatus(`dry-run ${res.evaluated} calls \xB7 allow ${res.would_allow} / deny ${res.would_deny} \xB7 newly blocked ${res.newly_blocked} \xB7 newly allowed ${res.newly_allowed}`)).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8094
+ } else if (input === "v") {
8095
+ setVi(0);
8096
+ setView("versions");
8025
8097
  } else if (input === "s") void save();
8026
8098
  else if (input === "x") discard();
8027
8099
  return;
8028
8100
  }
8101
+ if (view === "versions") {
8102
+ const vers = versionsQ.data?.versions ?? [];
8103
+ if (key.leftArrow) return void setView("rules");
8104
+ if (key.upArrow) setVi((n) => Math.max(0, n - 1));
8105
+ else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
8106
+ else if (key.return) {
8107
+ const v = vers[vi];
8108
+ if (v && selected) {
8109
+ setStatus("Rolling back\u2026");
8110
+ void api.policies.rollback(selected.id, v.version).then((r) => {
8111
+ setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
8112
+ detail.reload();
8113
+ list6.reload();
8114
+ setView("rules");
8115
+ }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8116
+ }
8117
+ }
8118
+ return;
8119
+ }
8029
8120
  const rule2 = rules[ri];
8030
8121
  if (!rule2) return setView("rules");
8031
8122
  const field = FIELDS[fi];
@@ -8070,7 +8161,7 @@ function PoliciesPanel({ focused }) {
8070
8161
  /* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
8071
8162
  dirtyTag
8072
8163
  ] }),
8073
- /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 rule \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 d delete \xB7 m mode \xB7 s save \xB7 \u2190 back" }),
8164
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 \xB7 enter edit \xB7 space on/off \xB7 e effect \xB7 n new \xB7 d del \xB7 m mode \xB7 D dry-run \xB7 v versions \xB7 s save \xB7 \u2190 back" }),
8074
8165
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8075
8166
  Table,
8076
8167
  {
@@ -8096,6 +8187,50 @@ function PoliciesPanel({ focused }) {
8096
8187
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8097
8188
  ] }) });
8098
8189
  }
8190
+ if (view === "versions") {
8191
+ const vers = versionsQ.data?.versions ?? [];
8192
+ return /* @__PURE__ */ jsx3(DataView, { loading: versionsQ.loading && !versionsQ.data, error: versionsQ.error, empty: !!versionsQ.data && vers.length === 0, emptyText: "No versions.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8193
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
8194
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
8195
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8196
+ Table,
8197
+ {
8198
+ columns: [
8199
+ { header: "", width: 2 },
8200
+ { header: "VER", width: 5 },
8201
+ { header: "RULES", width: 5 },
8202
+ { header: "REASON", width: 34 },
8203
+ { header: "WHEN", width: 16 }
8204
+ ],
8205
+ rows: vers.map((v, i) => [
8206
+ { value: i === vi ? "\u25B8" : "", color: theme.accentBright },
8207
+ { value: `v${v.version}`, bold: i === vi },
8208
+ { value: String(v.rules_count), dim: true },
8209
+ { value: truncate2(v.reason || "\u2014", 34) },
8210
+ { value: truncate2(v.created_at, 16), dim: true }
8211
+ ])
8212
+ }
8213
+ ) }),
8214
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
8215
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: `Diff v${diffVer ?? "?"} vs current draft` }),
8216
+ (() => {
8217
+ const old = diffQ.data?.rules ?? [];
8218
+ const cur = rules;
8219
+ const oldIds = new Set(old.map((r) => r.id));
8220
+ const curIds = new Set(cur.map((r) => r.id));
8221
+ const added = cur.filter((r) => !oldIds.has(r.id));
8222
+ const removed = old.filter((r) => !curIds.has(r.id));
8223
+ if (diffQ.loading && !diffQ.data) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " loading\u2026" });
8224
+ if (!added.length && !removed.length) return /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: " identical rule set" });
8225
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
8226
+ added.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.ok, children: ` + ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "a" + r.id)),
8227
+ removed.slice(0, 4).map((r) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", color: theme.bad, children: ` - ${r.effect} ${truncate2(r.toolPattern, 18)} ${truncate2(r.description || r.id, 30)}` }, "r" + r.id))
8228
+ ] });
8229
+ })()
8230
+ ] }),
8231
+ status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8232
+ ] }) });
8233
+ }
8099
8234
  const rule = rules[ri];
8100
8235
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8101
8236
  /* @__PURE__ */ jsxs3(Box3, { children: [
@@ -8237,6 +8372,20 @@ function RateLimitPanel({ focused }) {
8237
8372
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
8238
8373
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
8239
8374
  ] }),
8375
+ draft.perMinute > 0 ? (() => {
8376
+ const now = minuteSeries.slice(-1)[0] ?? 0;
8377
+ const pct = Math.min(100, Math.round(now / draft.perMinute * 100));
8378
+ const width2 = 30;
8379
+ const filled = Math.min(width2, Math.round(now / draft.perMinute * width2));
8380
+ const col = pct >= 100 ? theme.bad : pct >= 80 ? theme.warn : theme.ok;
8381
+ return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
8382
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "load " }),
8383
+ /* @__PURE__ */ jsx4(Text4, { color: col, children: "\u2588".repeat(filled) }),
8384
+ /* @__PURE__ */ jsx4(Text4, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
8385
+ /* @__PURE__ */ jsx4(Text4, { color: col, children: ` ${now}/${draft.perMinute} ` }),
8386
+ /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: `(${pct}% this min)` })
8387
+ ] });
8388
+ })() : null,
8240
8389
  dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
8241
8390
  dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
8242
8391
  status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
@@ -8319,12 +8468,19 @@ function DlpPanel({ focused }) {
8319
8468
  const [adding, setAdding] = useState5(null);
8320
8469
  const [newName, setNewName] = useState5("");
8321
8470
  const [newRe, setNewRe] = useState5("");
8471
+ const [ghostOn, setGhostOn] = useState5(false);
8472
+ const spQ = useLoader(() => api.settings.getSelfProtection());
8473
+ const [selfProt, setSelfProt] = useState5(null);
8322
8474
  useEffect5(() => {
8323
8475
  if (q.data && !dlp) {
8324
8476
  setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
8325
8477
  setAvailable(q.data.availablePatterns);
8478
+ setGhostOn(q.data.layers.ghost.mode === "on");
8326
8479
  }
8327
8480
  }, [q.data, dlp]);
8481
+ useEffect5(() => {
8482
+ if (spQ.data && selfProt === null) setSelfProt(spQ.data.enabled);
8483
+ }, [spQ.data, selfProt]);
8328
8484
  const mutate = (next) => {
8329
8485
  setDlp(next);
8330
8486
  setDirty(true);
@@ -8334,14 +8490,27 @@ function DlpPanel({ focused }) {
8334
8490
  if (!dlp || !q.data) return;
8335
8491
  setStatus("Saving\u2026");
8336
8492
  try {
8337
- const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp });
8493
+ const ghost = { ...q.data.layers.ghost, mode: ghostOn ? "on" : "off" };
8494
+ const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp, ghost });
8338
8495
  setDlp({ ...res.layers.dlp, patterns: [...res.layers.dlp.patterns], custom: [...res.layers.dlp.custom] });
8496
+ setGhostOn(res.layers.ghost.mode === "on");
8339
8497
  setDirty(false);
8340
8498
  setStatus("\u2713 Saved");
8341
8499
  } catch (e) {
8342
8500
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
8343
8501
  }
8344
8502
  };
8503
+ const toggleSelfProt = async () => {
8504
+ const next = !(selfProt ?? false);
8505
+ setSelfProt(next);
8506
+ try {
8507
+ await api.settings.setSelfProtection(next);
8508
+ setStatus(`\u2713 self-protection ${next ? "on" : "off"}`);
8509
+ } catch (e) {
8510
+ setSelfProt(!next);
8511
+ setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
8512
+ }
8513
+ };
8345
8514
  const discard = () => {
8346
8515
  if (q.data) setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
8347
8516
  setDirty(false);
@@ -8371,7 +8540,12 @@ function DlpPanel({ focused }) {
8371
8540
  const ci = sel - available.length;
8372
8541
  mutate({ ...dlp, custom: dlp.custom.filter((_, i) => i !== ci) });
8373
8542
  setSel((n) => Math.max(0, n - 1));
8374
- } else if (input === "s") void save();
8543
+ } else if (input === "g") {
8544
+ setGhostOn((v) => !v);
8545
+ setDirty(true);
8546
+ setStatus(null);
8547
+ } else if (input === "P") void toggleSelfProt();
8548
+ else if (input === "s") void save();
8375
8549
  else if (input === "x") discard();
8376
8550
  },
8377
8551
  { isActive: focused && !adding }
@@ -8383,7 +8557,13 @@ function DlpPanel({ focused }) {
8383
8557
  /* @__PURE__ */ jsx5(Text5, { color: modeColor(dlp.mode), children: dlp.mode }),
8384
8558
  dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
8385
8559
  ] }),
8386
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193 move \xB7 space toggle \xB7 m mode \xB7 a add \xB7 d remove \xB7 s save" : "press \u2192 to edit" }),
8560
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: focused ? "\u2191\u2193 \xB7 space toggle \xB7 m mode \xB7 a add \xB7 d remove \xB7 g ghost \xB7 P self-protect \xB7 s save" : "press \u2192 to edit" }),
8561
+ /* @__PURE__ */ jsxs5(Box5, { children: [
8562
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "ghost: " }),
8563
+ /* @__PURE__ */ jsx5(Text5, { color: ghostOn ? theme.ok : theme.dim, children: ghostOn ? "on" : "off" }),
8564
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " self-protection: " }),
8565
+ /* @__PURE__ */ jsx5(Text5, { color: selfProt ? theme.ok : theme.dim, children: selfProt === null ? "\u2026" : selfProt ? "on" : "off" })
8566
+ ] }),
8387
8567
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: available.map((p, i) => {
8388
8568
  const on = enabled.has(p);
8389
8569
  const active2 = focused && sel === i;
@@ -8430,10 +8610,27 @@ function DlpPanel({ focused }) {
8430
8610
  }
8431
8611
  )
8432
8612
  ] }) : null,
8613
+ adding === "re" ? /* @__PURE__ */ jsx5(RegexTest, { re: newRe }) : null,
8433
8614
  status ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
8434
8615
  ] }) : null });
8435
8616
  }
8436
- var MODES2;
8617
+ function RegexTest({ re }) {
8618
+ if (!re.trim()) return /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " regex: type a pattern\u2026" });
8619
+ let rx = null;
8620
+ let err2 = "";
8621
+ try {
8622
+ rx = new RegExp(re);
8623
+ } catch (e) {
8624
+ err2 = e instanceof Error ? e.message : String(e);
8625
+ }
8626
+ if (!rx) return /* @__PURE__ */ jsx5(Text5, { color: theme.bad, children: ` \u2717 invalid: ${err2.slice(0, 50)}` });
8627
+ const hits = SAMPLES.filter((s) => rx.test(s));
8628
+ return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
8629
+ /* @__PURE__ */ jsx5(Text5, { color: theme.ok, children: " \u2713 valid" }),
8630
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: hits.length ? ` matches ${hits.length}/${SAMPLES.length} samples: ${hits.map((h) => h.slice(0, 8)).join(", ")}` : " (no sample match \u2014 will only catch your custom secrets)" })
8631
+ ] });
8632
+ }
8633
+ var MODES2, SAMPLES;
8437
8634
  var init_Dlp = __esm({
8438
8635
  "src/tui/panels/Dlp.tsx"() {
8439
8636
  "use strict";
@@ -8442,6 +8639,7 @@ var init_Dlp = __esm({
8442
8639
  init_hooks();
8443
8640
  init_theme();
8444
8641
  MODES2 = ["off", "detect", "block"];
8642
+ SAMPLES = ["AKIA1234567890ABCD00", "sk-ant-api03-xxxxxxxx", "ghp_16chars0000000000000000000000000000", "password=hunter2", "Bearer eyJhbGciOi"];
8445
8643
  }
8446
8644
  });
8447
8645
 
@@ -8451,12 +8649,15 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
8451
8649
  function StatsPanel({ active: active2 }) {
8452
8650
  const stats = useLoader(() => api.stats.get());
8453
8651
  const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
8652
+ const drift2 = useLoader(() => api.stats.drift(7));
8454
8653
  usePoll(() => {
8455
8654
  stats.reload();
8456
8655
  ts.reload();
8457
8656
  }, 5e3, active2);
8657
+ usePoll(drift2.reload, 3e4, active2);
8458
8658
  const s = stats.data;
8459
8659
  const points = ts.data?.timeseries ?? [];
8660
+ const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
8460
8661
  return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
8461
8662
  /* @__PURE__ */ jsxs6(Box6, { children: [
8462
8663
  /* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
@@ -8509,7 +8710,7 @@ function StatsPanel({ active: active2 }) {
8509
8710
  { header: "MS", width: 5 },
8510
8711
  { header: "WHEN", width: 6 }
8511
8712
  ],
8512
- rows: (s.recent_activity ?? []).slice(0, 8).map((a) => [
8713
+ rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
8513
8714
  { value: a.decision, color: decisionColor(a.decision) },
8514
8715
  { value: truncate2(a.tool_name, 20), color: theme.accent },
8515
8716
  { value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
@@ -8517,6 +8718,31 @@ function StatsPanel({ active: active2 }) {
8517
8718
  ])
8518
8719
  }
8519
8720
  )
8721
+ ] }),
8722
+ /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
8723
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
8724
+ "Denial drift ",
8725
+ drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
8726
+ ] }),
8727
+ driftRules.length ? /* @__PURE__ */ jsx6(
8728
+ Table,
8729
+ {
8730
+ columns: [
8731
+ { header: "NOW", width: 5 },
8732
+ { header: "PREV", width: 5 },
8733
+ { header: "\u0394", width: 6 },
8734
+ { header: "RULE", width: 22 },
8735
+ { header: "REASON", width: 26 }
8736
+ ],
8737
+ rows: driftRules.map((r) => [
8738
+ { value: String(r.current), bold: true },
8739
+ { value: String(r.previous), dim: true },
8740
+ { value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
8741
+ { value: truncate2(r.rule_id ?? "\u2014", 22), color: theme.accent },
8742
+ { value: truncate2(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
8743
+ ])
8744
+ }
8745
+ ) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
8520
8746
  ] })
8521
8747
  ] }) : null });
8522
8748
  }
@@ -8709,18 +8935,114 @@ var init_Audit = __esm({
8709
8935
  }
8710
8936
  });
8711
8937
 
8712
- // src/tui/App.tsx
8713
- import { Box as Box8, Text as Text8, useApp, useInput as useInput6 } from "ink";
8938
+ // src/tui/panels/Agents.tsx
8939
+ import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
8714
8940
  import { useState as useState7 } from "react";
8715
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
8941
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
8942
+ function Bar({ label, value, max = 100, width: width2 = 16, color = theme.accent }) {
8943
+ const filled = max > 0 ? Math.round(Math.min(value, max) / max * width2) : 0;
8944
+ return /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
8945
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: label.padEnd(9) }),
8946
+ /* @__PURE__ */ jsx8(Text8, { color, children: "\u2588".repeat(filled) }),
8947
+ /* @__PURE__ */ jsx8(Text8, { color: "#233457", children: "\u2591".repeat(Math.max(0, width2 - filled)) }),
8948
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " " + value })
8949
+ ] });
8950
+ }
8951
+ function AgentsPanel({ focused }) {
8952
+ const live2 = useLoader(() => api.agents.live({ limit: 30, includeDeactivated: true }));
8953
+ usePoll(live2.reload, 6e3, focused ? false : true);
8954
+ const [sel, setSel] = useState7(0);
8955
+ const agents = (live2.data?.agents ?? []).filter((a) => a.agent_id);
8956
+ const selected = agents[Math.min(sel, Math.max(0, agents.length - 1))];
8957
+ const detail = useLoader(() => selected?.agent_id ? api.agents.get(selected.agent_id) : Promise.resolve(null), [selected?.agent_id]);
8958
+ useInput6(
8959
+ (_input, key) => {
8960
+ if (key.upArrow) setSel((n) => Math.max(0, n - 1));
8961
+ else if (key.downArrow) setSel((n) => Math.min(agents.length - 1, n + 1));
8962
+ },
8963
+ { isActive: focused }
8964
+ );
8965
+ const d = detail.data;
8966
+ const base = d?.baseline;
8967
+ const radar = base?.radar;
8968
+ const anomalies2 = d?.anomalies ?? [];
8969
+ const tmap = d?.trust_map;
8970
+ const tools = tmap?.tools ?? [];
8971
+ const resources = tmap?.resources ?? [];
8972
+ const listW = 30;
8973
+ return /* @__PURE__ */ jsx8(DataView, { loading: live2.loading && !live2.data, error: live2.error, empty: !!live2.data && agents.length === 0, emptyText: "No identified agents yet.", children: /* @__PURE__ */ jsxs8(Box8, { children: [
8974
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: listW, marginRight: 2, overflow: "hidden", children: [
8975
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: focused ? "\u2191\u2193 select agent" : "press \u2192 to browse" }),
8976
+ agents.slice(0, 16).map((a, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", backgroundColor: focused && i === sel ? "#1c2f63" : void 0, bold: i === sel, children: [
8977
+ /* @__PURE__ */ jsxs8(Text8, { color: statusColor(a.status), children: [
8978
+ a.status === "active" ? "\u25CF" : a.status === "idle" ? "\u25D0" : "\u25CB",
8979
+ " "
8980
+ ] }),
8981
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: truncate2(a.agent_name ?? a.agent_id ?? "?", 16).padEnd(17) }),
8982
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `t${a.trust_score}` })
8983
+ ] }, a.session_id))
8984
+ ] }),
8985
+ /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: !selected ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "select an agent" }) : detail.loading && !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "loading profile\u2026" }) : !d ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "no profile (agent has no calls yet)" }) : /* @__PURE__ */ jsxs8(Fragment4, { children: [
8986
+ /* @__PURE__ */ jsxs8(Box8, { children: [
8987
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: truncate2(selected.agent_name ?? selected.agent_id ?? "?", 22) }),
8988
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: ` trust ${base?.trustScore ?? "\u2014"}/100 \xB7 ${base?.character ?? ""}` })
8989
+ ] }),
8990
+ base?.characterBlurb ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(String(base.characterBlurb), 70) }) : null,
8991
+ radar ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
8992
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Behaviour radar (0\u2013100)" }),
8993
+ /* @__PURE__ */ jsx8(Bar, { label: "read", value: Math.round((radar.read ?? 0) * 100) }),
8994
+ /* @__PURE__ */ jsx8(Bar, { label: "write", value: Math.round((radar.write ?? 0) * 100), color: theme.warn }),
8995
+ /* @__PURE__ */ jsx8(Bar, { label: "execute", value: Math.round((radar.execute ?? 0) * 100), color: theme.warn }),
8996
+ /* @__PURE__ */ jsx8(Bar, { label: "network", value: Math.round((radar.network ?? 0) * 100) }),
8997
+ /* @__PURE__ */ jsx8(Bar, { label: "complian.", value: Math.round((radar.compliance ?? 0) * 100), color: theme.ok })
8998
+ ] }) : null,
8999
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9000
+ /* @__PURE__ */ jsxs8(Text8, { color: theme.dim, children: [
9001
+ "Anomalies ",
9002
+ anomalies2.length ? `(${anomalies2.length})` : ""
9003
+ ] }),
9004
+ anomalies2.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " none" }) : anomalies2.slice(0, 4).map((an, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9005
+ /* @__PURE__ */ jsx8(Text8, { color: sevColor(String(an.severity)), children: ` ${String(an.severity).toUpperCase().slice(0, 4).padEnd(5)}` }),
9006
+ /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: String(an.kind).padEnd(12) }),
9007
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: truncate2(String(an.description ?? ""), 44) })
9008
+ ] }, i))
9009
+ ] }),
9010
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
9011
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Trust map \xB7 top tools" }),
9012
+ tools.slice(0, 4).map((t, i) => /* @__PURE__ */ jsxs8(Text8, { wrap: "truncate", children: [
9013
+ /* @__PURE__ */ jsx8(Text8, { color: t.anomalous ? theme.bad : theme.accent, children: ` ${truncate2(String(t.name), 16).padEnd(17)}` }),
9014
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: `${t.count}\xD7 ${t.permission ?? ""}` })
9015
+ ] }, i)),
9016
+ resources.slice(0, 3).map((r, i) => /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: r.anomalous ? theme.bad : theme.dim, children: ` ${r.type === "domain" ? "\u{1F310}" : "\u{1F4C1}"} ${truncate2(String(r.value), 40)} ${r.count}\xD7` }, "r" + i))
9017
+ ] })
9018
+ ] }) })
9019
+ ] }) });
9020
+ }
9021
+ var statusColor, sevColor;
9022
+ var init_Agents = __esm({
9023
+ "src/tui/panels/Agents.tsx"() {
9024
+ "use strict";
9025
+ init_api_client();
9026
+ init_components();
9027
+ init_hooks();
9028
+ init_theme();
9029
+ statusColor = (s) => s === "active" ? theme.ok : s === "idle" ? theme.warn : theme.dim;
9030
+ sevColor = (s) => s === "high" ? theme.bad : s === "medium" ? theme.warn : theme.dim;
9031
+ }
9032
+ });
9033
+
9034
+ // src/tui/App.tsx
9035
+ import { Box as Box9, Text as Text9, useApp, useInput as useInput7 } from "ink";
9036
+ import { useState as useState8 } from "react";
9037
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
8716
9038
  function LiveHint() {
8717
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
8718
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
8719
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
8720
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
8721
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
9039
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9040
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
9041
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
9042
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
9043
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
8722
9044
  "press ",
8723
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
9045
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
8724
9046
  " to go live"
8725
9047
  ] }) })
8726
9048
  ] });
@@ -8728,21 +9050,30 @@ function LiveHint() {
8728
9050
  function Banner() {
8729
9051
  const wide = (process.stdout.columns ?? 80) >= 82;
8730
9052
  if (!wide) {
8731
- return /* @__PURE__ */ jsxs8(Box8, { children: [
8732
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
8733
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " \u2014 security control center" })
9053
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
9054
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate" }),
9055
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " \u2014 security control center" })
8734
9056
  ] });
8735
9057
  }
8736
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
8737
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
8738
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
9058
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
9059
+ BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx9(Text9, { bold: true, color: BANNER_HEX[i], children: line }, i)),
9060
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: " security control center \xB7 manage policies, rate limits, DLP & more" })
8739
9061
  ] });
8740
9062
  }
8741
9063
  function App() {
8742
9064
  const { exit } = useApp();
8743
- const [section, setSection] = useState7(0);
8744
- const [focus, setFocus] = useState7("nav");
8745
- useInput6((input, key) => {
9065
+ const [section, setSection] = useState8(0);
9066
+ const [focus, setFocus] = useState8("nav");
9067
+ const [help, setHelp] = useState8(false);
9068
+ useInput7((input, key) => {
9069
+ if (help) {
9070
+ setHelp(false);
9071
+ return;
9072
+ }
9073
+ if (input === "?" && focus === "nav") {
9074
+ setHelp(true);
9075
+ return;
9076
+ }
8746
9077
  if (focus === "nav") {
8747
9078
  if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
8748
9079
  else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
@@ -8756,20 +9087,34 @@ function App() {
8756
9087
  const cols = process.stdout.columns ?? 100;
8757
9088
  const rows = process.stdout.rows ?? 30;
8758
9089
  const current = SECTIONS[section];
9090
+ if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
8759
9091
  if (current.label === "Live" && focus === "panel") {
8760
- return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
9092
+ return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx9(LivePanel, { active: true, focused: true }) });
8761
9093
  }
8762
9094
  const Panel = current.Panel;
8763
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
8764
- /* @__PURE__ */ jsx8(Banner, {}),
8765
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
8766
- /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
8767
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx8(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
9095
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
9096
+ /* @__PURE__ */ jsx9(Banner, {}),
9097
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexGrow: 1, children: [
9098
+ /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", width: 16, borderStyle: "round", borderColor: focus === "nav" ? theme.accent : "gray", paddingX: 1, children: SECTIONS.map((s, i) => /* @__PURE__ */ jsx9(Text9, { color: i === section ? theme.accentBright : void 0, bold: i === section, children: (i === section ? "\u25B8 " : " ") + s.label }, s.label)) }),
9099
+ /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, borderStyle: "round", borderColor: focus === "panel" ? theme.accent : "gray", paddingX: 1, paddingY: 0, children: /* @__PURE__ */ jsx9(Panel, { active: focus === "panel" || current.label !== "Live", focused: focus === "panel" }) })
8768
9100
  ] }),
8769
- /* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["q", "quit"]] }) : /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9101
+ /* @__PURE__ */ jsx9(Box9, { children: focus === "nav" ? /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["q", "quit"]] }) : /* @__PURE__ */ jsx9(KeyHints, { hints: [["\u2190/esc", "back"], ["\u2191\u2193", "in-panel"], ["space/s", "act"]] }) })
9102
+ ] });
9103
+ }
9104
+ function HelpOverlay({ cols, rows }) {
9105
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
9106
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
9107
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
9108
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.accent, children: group }),
9109
+ keys.map(([k, desc]) => /* @__PURE__ */ jsxs9(Text9, { children: [
9110
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
9111
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: desc })
9112
+ ] }, k))
9113
+ ] }, group)) }),
9114
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
8770
9115
  ] });
8771
9116
  }
8772
- var SECTIONS, BANNER_HEX;
9117
+ var SECTIONS, BANNER_HEX, HELP;
8773
9118
  var init_App = __esm({
8774
9119
  "src/tui/App.tsx"() {
8775
9120
  "use strict";
@@ -8782,8 +9127,10 @@ var init_App = __esm({
8782
9127
  init_Dlp();
8783
9128
  init_Stats();
8784
9129
  init_Audit();
9130
+ init_Agents();
8785
9131
  SECTIONS = [
8786
9132
  { label: "Live", Panel: LiveHint },
9133
+ { label: "Agents", Panel: AgentsPanel },
8787
9134
  { label: "Policies", Panel: PoliciesPanel },
8788
9135
  { label: "Rate Limit", Panel: RateLimitPanel },
8789
9136
  { label: "DLP", Panel: DlpPanel },
@@ -8791,6 +9138,12 @@ var init_App = __esm({
8791
9138
  { label: "Audit", Panel: AuditPanel }
8792
9139
  ];
8793
9140
  BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
9141
+ HELP = [
9142
+ ["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
9143
+ ["Live", [["\u2191\u2193", "select a stream row"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
9144
+ ["Policies", [["\u2191\u2193", "browse / select"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["v", "versions / rollback"], ["s", "save"], ["x", "discard"]]],
9145
+ ["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"]]]
9146
+ ];
8794
9147
  }
8795
9148
  });
8796
9149
 
@@ -8800,7 +9153,7 @@ __export(tui_exports, {
8800
9153
  launchTui: () => launchTui
8801
9154
  });
8802
9155
  import { render } from "ink";
8803
- import { jsx as jsx9 } from "react/jsx-runtime";
9156
+ import { jsx as jsx10 } from "react/jsx-runtime";
8804
9157
  async function launchTui() {
8805
9158
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
8806
9159
  process.stderr.write(
@@ -8814,7 +9167,7 @@ async function launchTui() {
8814
9167
  }
8815
9168
  process.stdout.write("\x1B[?1049h\x1B[H");
8816
9169
  try {
8817
- const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
9170
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
8818
9171
  await waitUntilExit();
8819
9172
  } finally {
8820
9173
  process.stdout.write("\x1B[?1049l");
@@ -8926,7 +9279,7 @@ var init_args = __esm({
8926
9279
  });
8927
9280
 
8928
9281
  // src/commands/policy.ts
8929
- import { readFileSync as readFileSync5 } from "fs";
9282
+ import { readFileSync as readFileSync6 } from "fs";
8930
9283
  async function run(argv) {
8931
9284
  const { positionals, flags } = parse(argv);
8932
9285
  const sub = positionals[0];
@@ -9101,7 +9454,7 @@ function printRules(rules) {
9101
9454
  }
9102
9455
  async function resolveRules(target) {
9103
9456
  if (target.endsWith(".json")) {
9104
- const parsed = JSON.parse(readFileSync5(target, "utf-8"));
9457
+ const parsed = JSON.parse(readFileSync6(target, "utf-8"));
9105
9458
  return parsed.rules ?? [];
9106
9459
  }
9107
9460
  const p = await api.policies.get(target);
@@ -9465,7 +9818,7 @@ async function runAgents(argv) {
9465
9818
  table(
9466
9819
  ["STATUS", "AGENT", "CALLS", "DENY", "DLP", "TRUST", "CHARACTER"],
9467
9820
  res.agents.map((a) => [
9468
- statusColor(a.status),
9821
+ statusColor2(a.status),
9469
9822
  cyan(truncate3(a.agent_name ?? a.agent_id ?? a.session_id, 20)),
9470
9823
  String(a.total_calls),
9471
9824
  a.denied_calls ? red(String(a.denied_calls)) : dim("0"),
@@ -9484,7 +9837,7 @@ async function runAgent(argv) {
9484
9837
  const a = await api.agents.get(id);
9485
9838
  if (json) return printJson(a), 0;
9486
9839
  err("");
9487
- err(` ${bold(a.agent_id)} status: ${statusColor(String(a.status))}`);
9840
+ err(` ${bold(a.agent_id)} status: ${statusColor2(String(a.status))}`);
9488
9841
  const base = a.baseline;
9489
9842
  if (base) {
9490
9843
  err(` ${dim(base.character ?? "")} trust ${bold(String(base.trustScore ?? "\u2014"))}/100 deny-rate ${(Number(base.denyRate ?? 0) * 100).toFixed(0)}%`);
@@ -9504,21 +9857,21 @@ async function runAgent(argv) {
9504
9857
  }
9505
9858
  return 0;
9506
9859
  }
9507
- var statusColor;
9860
+ var statusColor2;
9508
9861
  var init_agents2 = __esm({
9509
9862
  "src/commands/agents.ts"() {
9510
9863
  "use strict";
9511
9864
  init_api_client();
9512
9865
  init_args();
9513
9866
  init_format();
9514
- statusColor = (s) => s === "active" ? green(s) : s === "idle" ? yellow(s) : dim(s);
9867
+ statusColor2 = (s) => s === "active" ? green(s) : s === "idle" ? yellow(s) : dim(s);
9515
9868
  }
9516
9869
  });
9517
9870
 
9518
9871
  // src/commands/doctor.ts
9519
9872
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
9520
- import { homedir as homedir4 } from "os";
9521
- import { join as join6 } from "path";
9873
+ import { homedir as homedir5 } from "os";
9874
+ import { join as join7 } from "path";
9522
9875
  async function run6(argv) {
9523
9876
  const { flags } = parse(argv);
9524
9877
  const json = flagBool(flags, "json");
@@ -9578,14 +9931,14 @@ var init_doctor = __esm({
9578
9931
  init_api_client();
9579
9932
  init_format();
9580
9933
  init_args();
9581
- LOCAL_LOG2 = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
9934
+ LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
9582
9935
  }
9583
9936
  });
9584
9937
 
9585
9938
  // src/commands/watch.ts
9586
9939
  import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
9587
- import { homedir as homedir5 } from "os";
9588
- import { join as join7 } from "path";
9940
+ import { homedir as homedir6 } from "os";
9941
+ import { join as join8 } from "path";
9589
9942
  function tailLocal(file, maxBytes = 131072) {
9590
9943
  try {
9591
9944
  const size = statSync3(file).size;
@@ -9692,7 +10045,7 @@ var init_watch = __esm({
9692
10045
  init_cli_utils();
9693
10046
  init_args();
9694
10047
  init_format();
9695
- LOCAL_LOG3 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10048
+ LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
9696
10049
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
9697
10050
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
9698
10051
  }
@@ -9994,9 +10347,9 @@ __export(global_install_exports, {
9994
10347
  runGlobalRestore: () => runGlobalRestore,
9995
10348
  unlockProtected: () => unlockProtected
9996
10349
  });
9997
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
9998
- import { resolve as resolve4, join as join8, dirname } from "path";
9999
- import { homedir as homedir6 } from "os";
10350
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
10351
+ import { resolve as resolve4, join as join9, dirname } from "path";
10352
+ import { homedir as homedir7 } from "os";
10000
10353
  import { fileURLToPath } from "url";
10001
10354
  import { createInterface } from "readline";
10002
10355
  import { execFileSync as execFileSync2 } from "child_process";
@@ -10059,10 +10412,10 @@ function unlockFile(file) {
10059
10412
  function protectedTargets() {
10060
10413
  const p = globalPaths();
10061
10414
  return [
10062
- join8(p.hooksDir, "guard.mjs"),
10063
- join8(p.hooksDir, "audit.mjs"),
10064
- join8(p.hooksDir, "stop.mjs"),
10065
- join8(p.hooksDir, "shield.mjs"),
10415
+ join9(p.hooksDir, "guard.mjs"),
10416
+ join9(p.hooksDir, "audit.mjs"),
10417
+ join9(p.hooksDir, "stop.mjs"),
10418
+ join9(p.hooksDir, "shield.mjs"),
10066
10419
  p.configPath,
10067
10420
  p.settingsPath
10068
10421
  ];
@@ -10074,26 +10427,26 @@ function unlockProtected() {
10074
10427
  for (const f of protectedTargets()) unlockFile(f);
10075
10428
  }
10076
10429
  function globalPaths() {
10077
- const home = homedir6();
10078
- const sgDir = join8(home, ".solongate");
10079
- const hooksDir = join8(sgDir, "hooks");
10080
- const claudeDir = join8(home, ".claude");
10430
+ const home = homedir7();
10431
+ const sgDir = join9(home, ".solongate");
10432
+ const hooksDir = join9(sgDir, "hooks");
10433
+ const claudeDir = join9(home, ".claude");
10081
10434
  return {
10082
10435
  home,
10083
10436
  sgDir,
10084
10437
  hooksDir,
10085
10438
  claudeDir,
10086
- settingsPath: join8(claudeDir, "settings.json"),
10087
- backupPath: join8(claudeDir, "settings.solongate.bak"),
10088
- configPath: join8(sgDir, "cloud-guard.json")
10439
+ settingsPath: join9(claudeDir, "settings.json"),
10440
+ backupPath: join9(claudeDir, "settings.solongate.bak"),
10441
+ configPath: join9(sgDir, "cloud-guard.json")
10089
10442
  };
10090
10443
  }
10091
10444
  function readHook(filename) {
10092
- return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
10445
+ return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
10093
10446
  }
10094
10447
  function readGuard() {
10095
- const bundled = join8(HOOKS_DIR, "guard.bundled.mjs");
10096
- return existsSync6(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
10448
+ const bundled = join9(HOOKS_DIR, "guard.bundled.mjs");
10449
+ return existsSync6(bundled) ? readFileSync7(bundled, "utf-8") : readHook("guard.mjs");
10097
10450
  }
10098
10451
  function ask(question) {
10099
10452
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -10107,13 +10460,13 @@ function runGlobalRestore() {
10107
10460
  unlockProtected();
10108
10461
  removeClaudeShim();
10109
10462
  if (existsSync6(p.backupPath)) {
10110
- writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
10463
+ writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10111
10464
  console.log(` Restored ${p.settingsPath} from backup.`);
10112
10465
  } else if (existsSync6(p.settingsPath)) {
10113
10466
  try {
10114
- const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
10467
+ const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
10115
10468
  delete s.hooks;
10116
- writeFileSync3(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10469
+ writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10117
10470
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
10118
10471
  } catch {
10119
10472
  }
@@ -10147,18 +10500,18 @@ function shimTargets() {
10147
10500
  return [];
10148
10501
  }
10149
10502
  }
10150
- return [".bashrc", ".zshrc", ".profile"].map((f) => join8(homedir6(), f)).filter((f) => existsSync6(f));
10503
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join9(homedir7(), f)).filter((f) => existsSync6(f));
10151
10504
  }
10152
10505
  function writeShimBlock(file, block2) {
10153
10506
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10154
- let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
10507
+ let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
10155
10508
  content = content.replace(re, "");
10156
10509
  if (block2) {
10157
10510
  if (content.length && !content.endsWith("\n")) content += "\n";
10158
10511
  content += block2 + "\n";
10159
10512
  }
10160
- mkdirSync3(dirname(file), { recursive: true });
10161
- writeFileSync3(file, content);
10513
+ mkdirSync4(dirname(file), { recursive: true });
10514
+ writeFileSync4(file, content);
10162
10515
  }
10163
10516
  function installClaudeShim(shieldPath) {
10164
10517
  const real = resolveRealClaude();
@@ -10202,7 +10555,7 @@ async function runGlobalInstall(opts = {}) {
10202
10555
  let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
10203
10556
  if (!apiKey || apiKey === "sg_live_your_key_here") {
10204
10557
  try {
10205
- const cfg = JSON.parse(readFileSync6(p.configPath, "utf-8"));
10558
+ const cfg = JSON.parse(readFileSync7(p.configPath, "utf-8"));
10206
10559
  if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
10207
10560
  } catch {
10208
10561
  }
@@ -10215,22 +10568,22 @@ async function runGlobalInstall(opts = {}) {
10215
10568
  process.exit(1);
10216
10569
  }
10217
10570
  const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
10218
- mkdirSync3(p.hooksDir, { recursive: true });
10219
- mkdirSync3(p.claudeDir, { recursive: true });
10571
+ mkdirSync4(p.hooksDir, { recursive: true });
10572
+ mkdirSync4(p.claudeDir, { recursive: true });
10220
10573
  unlockProtected();
10221
- writeFileSync3(join8(p.hooksDir, "guard.mjs"), readGuard());
10222
- writeFileSync3(join8(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10223
- writeFileSync3(join8(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10224
- writeFileSync3(join8(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10574
+ writeFileSync4(join9(p.hooksDir, "guard.mjs"), readGuard());
10575
+ writeFileSync4(join9(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10576
+ writeFileSync4(join9(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10577
+ writeFileSync4(join9(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10225
10578
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
10226
10579
  removeClaudeShim();
10227
- writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10580
+ writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10228
10581
  console.log(` Wrote ${p.configPath}`);
10229
10582
  let existing = {};
10230
10583
  if (existsSync6(p.settingsPath)) {
10231
- const raw = readFileSync6(p.settingsPath, "utf-8");
10584
+ const raw = readFileSync7(p.settingsPath, "utf-8");
10232
10585
  if (!existsSync6(p.backupPath)) {
10233
- writeFileSync3(p.backupPath, raw);
10586
+ writeFileSync4(p.backupPath, raw);
10234
10587
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
10235
10588
  }
10236
10589
  try {
@@ -10239,9 +10592,9 @@ async function runGlobalInstall(opts = {}) {
10239
10592
  existing = {};
10240
10593
  }
10241
10594
  }
10242
- const guardAbs = join8(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10243
- const auditAbs = join8(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10244
- const stopAbs = join8(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10595
+ const guardAbs = join9(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10596
+ const auditAbs = join9(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10597
+ const stopAbs = join9(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10245
10598
  const nodeBin = process.execPath.replace(/\\/g, "/");
10246
10599
  const merged = {
10247
10600
  ...existing,
@@ -10251,7 +10604,7 @@ async function runGlobalInstall(opts = {}) {
10251
10604
  Stop: [{ matcher: "", hooks: [{ type: "command", command: `"${nodeBin}" "${stopAbs}" claude-code "Claude Code"` }] }]
10252
10605
  }
10253
10606
  };
10254
- writeFileSync3(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10607
+ writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10255
10608
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
10256
10609
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
10257
10610
  lockProtected();
@@ -10431,11 +10784,11 @@ import { createServer, request as httpRequest } from "http";
10431
10784
  import { request as httpsRequest } from "https";
10432
10785
  import { spawn as spawn3 } from "child_process";
10433
10786
  import { URL as URL2 } from "url";
10434
- import { readFileSync as readFileSync7, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
10787
+ import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
10435
10788
  import { resolve as resolve5 } from "path";
10436
- import { homedir as homedir7 } from "os";
10789
+ import { homedir as homedir8 } from "os";
10437
10790
  function findCacheFile() {
10438
- const dir = resolve5(homedir7(), ".solongate");
10791
+ const dir = resolve5(homedir8(), ".solongate");
10439
10792
  const envSel = process.env.SOLONGATE_AGENT_ID;
10440
10793
  if (envSel) {
10441
10794
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
@@ -10461,7 +10814,7 @@ function loadCfg() {
10461
10814
  try {
10462
10815
  const f = findCacheFile();
10463
10816
  if (f && existsSync7(f)) {
10464
- const c2 = JSON.parse(readFileSync7(f, "utf-8"));
10817
+ const c2 = JSON.parse(readFileSync8(f, "utf-8"));
10465
10818
  const d = c2?.security?.dlpRedact;
10466
10819
  const g = c2?.security?.ghost;
10467
10820
  const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
@@ -10737,9 +11090,9 @@ __export(logs_server_exports, {
10737
11090
  runLogsServer: () => runLogsServer
10738
11091
  });
10739
11092
  import { createServer as createServer2 } from "http";
10740
- import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
10741
- import { resolve as resolve6, join as join9, isAbsolute } from "path";
10742
- import { homedir as homedir8 } from "os";
11093
+ import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11094
+ import { resolve as resolve6, join as join10, isAbsolute } from "path";
11095
+ import { homedir as homedir9 } from "os";
10743
11096
  import { readdirSync as readdirSync2 } from "fs";
10744
11097
  function allowedOrigins() {
10745
11098
  const base = [
@@ -10756,15 +11109,15 @@ function resolveLocalLogDir(rawPath) {
10756
11109
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
10757
11110
  if (!dir) return null;
10758
11111
  if (isAbsolute(dir)) return dir;
10759
- return resolve6(homedir8(), ".solongate", "local-logs");
11112
+ return resolve6(homedir9(), ".solongate", "local-logs");
10760
11113
  }
10761
11114
  async function findLogDir() {
10762
- const base = resolve6(homedir8(), ".solongate");
11115
+ const base = resolve6(homedir9(), ".solongate");
10763
11116
  try {
10764
11117
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
10765
11118
  for (const f of files) {
10766
11119
  try {
10767
- const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
11120
+ const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
10768
11121
  const p = c2?.security?.localLogs?.path;
10769
11122
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
10770
11123
  } catch {
@@ -10773,7 +11126,7 @@ async function findLogDir() {
10773
11126
  } catch {
10774
11127
  }
10775
11128
  try {
10776
- const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
11129
+ const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
10777
11130
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
10778
11131
  if (apiKey) {
10779
11132
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -10802,7 +11155,7 @@ function setCors(req, res) {
10802
11155
  }
10803
11156
  function fileInfo(dir) {
10804
11157
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
10805
- const file = join9(dir, LOG_FILENAME);
11158
+ const file = join10(dir, LOG_FILENAME);
10806
11159
  try {
10807
11160
  const st = statSync5(file);
10808
11161
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -10863,7 +11216,7 @@ async function runLogsServer() {
10863
11216
  return;
10864
11217
  }
10865
11218
  try {
10866
- const text = readFileSync8(info.file, "utf-8");
11219
+ const text = readFileSync9(info.file, "utf-8");
10867
11220
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
10868
11221
  res.end(text);
10869
11222
  } catch {
@@ -10915,7 +11268,7 @@ var init_logs_server = __esm({
10915
11268
 
10916
11269
  // src/inject.ts
10917
11270
  var inject_exports = {};
10918
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync } from "fs";
11271
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync8, copyFileSync } from "fs";
10919
11272
  import { resolve as resolve7 } from "path";
10920
11273
  import { execSync } from "child_process";
10921
11274
  function parseInjectArgs(argv) {
@@ -10975,7 +11328,7 @@ WHAT IT DOES
10975
11328
  function detectProject() {
10976
11329
  if (!existsSync8(resolve7("package.json"))) return false;
10977
11330
  try {
10978
- const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
11331
+ const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
10979
11332
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
10980
11333
  return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
10981
11334
  } catch {
@@ -10984,7 +11337,7 @@ function detectProject() {
10984
11337
  }
10985
11338
  function findTsEntryFile() {
10986
11339
  try {
10987
- const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
11340
+ const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
10988
11341
  if (pkg.bin) {
10989
11342
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
10990
11343
  if (typeof binPath === "string") {
@@ -11011,7 +11364,7 @@ function findTsEntryFile() {
11011
11364
  const full = resolve7(c2);
11012
11365
  if (existsSync8(full)) {
11013
11366
  try {
11014
- const content = readFileSync9(full, "utf-8");
11367
+ const content = readFileSync10(full, "utf-8");
11015
11368
  if (content.includes("McpServer") || content.includes("McpServer")) {
11016
11369
  return full;
11017
11370
  }
@@ -11031,7 +11384,7 @@ function detectPackageManager() {
11031
11384
  }
11032
11385
  function installSdk() {
11033
11386
  try {
11034
- const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
11387
+ const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11035
11388
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
11036
11389
  if (allDeps["@solongate/proxy"]) {
11037
11390
  log3(" @solongate/proxy already installed");
@@ -11052,7 +11405,7 @@ function installSdk() {
11052
11405
  }
11053
11406
  }
11054
11407
  function injectTypeScript(filePath) {
11055
- const original = readFileSync9(filePath, "utf-8");
11408
+ const original = readFileSync10(filePath, "utf-8");
11056
11409
  const changes = [];
11057
11410
  let modified = original;
11058
11411
  if (modified.includes("SecureMcpServer")) {
@@ -11223,7 +11576,7 @@ async function main2() {
11223
11576
  log3("");
11224
11577
  log3(` Backup: ${backupPath}`);
11225
11578
  }
11226
- writeFileSync4(entryFile, result.modified);
11579
+ writeFileSync5(entryFile, result.modified);
11227
11580
  log3("");
11228
11581
  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");
11229
11582
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -11252,8 +11605,8 @@ var init_inject = __esm({
11252
11605
 
11253
11606
  // src/create.ts
11254
11607
  var create_exports = {};
11255
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
11256
- import { resolve as resolve8, join as join10 } from "path";
11608
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
11609
+ import { resolve as resolve8, join as join11 } from "path";
11257
11610
  import { execSync as execSync2 } from "child_process";
11258
11611
  function withSpinner(message, fn) {
11259
11612
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -11332,8 +11685,8 @@ EXAMPLES
11332
11685
  `);
11333
11686
  }
11334
11687
  function createProject(dir, name, _policy) {
11335
- writeFileSync5(
11336
- join10(dir, "package.json"),
11688
+ writeFileSync6(
11689
+ join11(dir, "package.json"),
11337
11690
  JSON.stringify(
11338
11691
  {
11339
11692
  name,
@@ -11362,8 +11715,8 @@ function createProject(dir, name, _policy) {
11362
11715
  2
11363
11716
  ) + "\n"
11364
11717
  );
11365
- writeFileSync5(
11366
- join10(dir, "tsconfig.json"),
11718
+ writeFileSync6(
11719
+ join11(dir, "tsconfig.json"),
11367
11720
  JSON.stringify(
11368
11721
  {
11369
11722
  compilerOptions: {
@@ -11383,9 +11736,9 @@ function createProject(dir, name, _policy) {
11383
11736
  2
11384
11737
  ) + "\n"
11385
11738
  );
11386
- mkdirSync4(join10(dir, "src"), { recursive: true });
11387
- writeFileSync5(
11388
- join10(dir, "src", "index.ts"),
11739
+ mkdirSync5(join11(dir, "src"), { recursive: true });
11740
+ writeFileSync6(
11741
+ join11(dir, "src", "index.ts"),
11389
11742
  `#!/usr/bin/env node
11390
11743
 
11391
11744
  console.log = (...args: unknown[]) => {
@@ -11426,8 +11779,8 @@ console.log('');
11426
11779
  console.log('Press Ctrl+C to stop.');
11427
11780
  `
11428
11781
  );
11429
- writeFileSync5(
11430
- join10(dir, ".mcp.json"),
11782
+ writeFileSync6(
11783
+ join11(dir, ".mcp.json"),
11431
11784
  JSON.stringify(
11432
11785
  {
11433
11786
  mcpServers: {
@@ -11444,13 +11797,13 @@ console.log('Press Ctrl+C to stop.');
11444
11797
  2
11445
11798
  ) + "\n"
11446
11799
  );
11447
- writeFileSync5(
11448
- join10(dir, ".env"),
11800
+ writeFileSync6(
11801
+ join11(dir, ".env"),
11449
11802
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
11450
11803
  `
11451
11804
  );
11452
- writeFileSync5(
11453
- join10(dir, ".gitignore"),
11805
+ writeFileSync6(
11806
+ join11(dir, ".gitignore"),
11454
11807
  `node_modules/
11455
11808
  dist/
11456
11809
  *.solongate-backup
@@ -11469,7 +11822,7 @@ async function main3() {
11469
11822
  process.exit(1);
11470
11823
  }
11471
11824
  withSpinner(`Setting up ${opts.name}...`, () => {
11472
- mkdirSync4(dir, { recursive: true });
11825
+ mkdirSync5(dir, { recursive: true });
11473
11826
  createProject(dir, opts.name, opts.policy);
11474
11827
  });
11475
11828
  if (!opts.noInstall) {
@@ -11543,14 +11896,14 @@ var init_create = __esm({
11543
11896
 
11544
11897
  // src/pull-push.ts
11545
11898
  var pull_push_exports = {};
11546
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
11899
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
11547
11900
  import { resolve as resolve9 } from "path";
11548
11901
  function loadEnv() {
11549
11902
  if (process.env.SOLONGATE_API_KEY) return;
11550
11903
  const envPath = resolve9(".env");
11551
11904
  if (!existsSync10(envPath)) return;
11552
11905
  try {
11553
- const content = readFileSync10(envPath, "utf-8");
11906
+ const content = readFileSync11(envPath, "utf-8");
11554
11907
  for (const line of content.split("\n")) {
11555
11908
  const trimmed = line.trim();
11556
11909
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -11738,7 +12091,7 @@ async function pull(apiKey, file, policyId) {
11738
12091
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
11739
12092
  const { id: _id, ...policyWithoutId } = policy;
11740
12093
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
11741
- writeFileSync6(file, json, "utf-8");
12094
+ writeFileSync7(file, json, "utf-8");
11742
12095
  log5("");
11743
12096
  log5(green2(" Saved to: ") + file);
11744
12097
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -11767,7 +12120,7 @@ async function push(apiKey, file, policyId) {
11767
12120
  log5(" solongate-proxy list");
11768
12121
  process.exit(1);
11769
12122
  }
11770
- const content = readFileSync10(file, "utf-8");
12123
+ const content = readFileSync11(file, "utf-8");
11771
12124
  let policy;
11772
12125
  try {
11773
12126
  policy = JSON.parse(content);