@solongate/proxy 0.80.0 → 0.81.1

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",
@@ -7108,9 +7138,9 @@ var init_hooks = __esm({
7108
7138
  import { Box as Box2, Text as Text2, useInput } from "ink";
7109
7139
  import TextInput from "ink-text-input";
7110
7140
  import { spawn } from "child_process";
7111
- import { closeSync, openSync, readSync, statSync } from "fs";
7112
- import { homedir as homedir3 } from "os";
7113
- 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";
7114
7144
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
7115
7145
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7116
7146
  function tailLines(file, maxBytes = 131072) {
@@ -7420,6 +7450,7 @@ function LivePanel({ active: active2 }) {
7420
7450
  (id, title, msg, level = "warn") => {
7421
7451
  pushLog(`${level === "bad" ? "\u26D4" : "\u23F8"} ${msg}`, level);
7422
7452
  setAlerts((a) => [...a.filter((x) => x.id !== id), { id, msg, level, until: Date.now() + 15e3 }].slice(-4));
7453
+ if (!CONFIG.notifications) return;
7423
7454
  try {
7424
7455
  process.stdout.write("\x07");
7425
7456
  } catch {
@@ -7647,7 +7678,16 @@ function LivePanel({ active: active2 }) {
7647
7678
  else if (input === "d") toggleSignal("deny");
7648
7679
  else if (input === "x") toggleSignal("dlp");
7649
7680
  else if (input === "r") toggleSignal("ratelimit");
7650
- 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));
7651
7691
  else if (key.upArrow) setSel((n) => Math.max(0, n - 1));
7652
7692
  else if (key.pageDown) setSel((n) => Math.min(visibleDesc.length - 1, n + streamBodyRows));
7653
7693
  else if (key.pageUp) setSel((n) => Math.max(0, n - streamBodyRows));
@@ -7896,18 +7936,20 @@ function LivePanel({ active: active2 }) {
7896
7936
  ] })
7897
7937
  ] });
7898
7938
  }
7899
- 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;
7900
7940
  var init_Live = __esm({
7901
7941
  "src/tui/panels/Live.tsx"() {
7902
7942
  "use strict";
7903
7943
  init_api_client();
7944
+ init_config2();
7904
7945
  init_hooks();
7905
7946
  init_theme();
7947
+ CONFIG = loadConfig();
7906
7948
  SPIN = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7907
7949
  BG = "#12234f";
7908
7950
  DIM_FLOOR = "#233457";
7909
- LOCAL_LOG = join5(homedir3(), ".solongate", "local-logs", "solongate-audit.jsonl");
7910
- 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");
7911
7953
  hhmmss = (ts) => {
7912
7954
  const d = new Date(ts);
7913
7955
  return Number.isNaN(d.getTime()) ? "--:--:--" : d.toTimeString().slice(0, 8);
@@ -7931,7 +7973,7 @@ var init_Live = __esm({
7931
7973
  import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
7932
7974
  import TextInput2 from "ink-text-input";
7933
7975
  import { useEffect as useEffect3, useState as useState3 } from "react";
7934
- 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";
7935
7977
  function ruleSummary(r) {
7936
7978
  const bits = [];
7937
7979
  for (const [g, tag] of [["commandConstraints", "cmd"], ["pathConstraints", "path"], ["filenameConstraints", "file"], ["urlConstraints", "url"]]) {
@@ -7961,6 +8003,11 @@ function PoliciesPanel({ focused }) {
7961
8003
  () => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
7962
8004
  [view, selected?.id]
7963
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
+ );
7964
8011
  useEffect3(() => {
7965
8012
  if (detail.data) {
7966
8013
  setRules(detail.data.rules);
@@ -8164,6 +8211,23 @@ function PoliciesPanel({ focused }) {
8164
8211
  ])
8165
8212
  }
8166
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
+ ] }),
8167
8231
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8168
8232
  ] }) });
8169
8233
  }
@@ -8308,6 +8372,20 @@ function RateLimitPanel({ focused }) {
8308
8372
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per hour", active: focused && fi === 2, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perHour === 0 ? "off" : draft.perHour }) }),
8309
8373
  /* @__PURE__ */ jsx4(FieldRow, { label: "Per day", active: focused && fi === 3, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: draft.perDay === 0 ? "off" : draft.perDay }) })
8310
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,
8311
8389
  dirty || status ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
8312
8390
  dirty ? /* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "\u25CF unsaved \u2014 press s to apply " }) : null,
8313
8391
  status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
@@ -8532,10 +8610,27 @@ function DlpPanel({ focused }) {
8532
8610
  }
8533
8611
  )
8534
8612
  ] }) : null,
8613
+ adding === "re" ? /* @__PURE__ */ jsx5(RegexTest, { re: newRe }) : null,
8535
8614
  status ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) }) : null
8536
8615
  ] }) : null });
8537
8616
  }
8538
- 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;
8539
8634
  var init_Dlp = __esm({
8540
8635
  "src/tui/panels/Dlp.tsx"() {
8541
8636
  "use strict";
@@ -8544,6 +8639,7 @@ var init_Dlp = __esm({
8544
8639
  init_hooks();
8545
8640
  init_theme();
8546
8641
  MODES2 = ["off", "detect", "block"];
8642
+ SAMPLES = ["AKIA1234567890ABCD00", "sk-ant-api03-xxxxxxxx", "ghp_16chars0000000000000000000000000000", "password=hunter2", "Bearer eyJhbGciOi"];
8547
8643
  }
8548
8644
  });
8549
8645
 
@@ -8839,18 +8935,114 @@ var init_Audit = __esm({
8839
8935
  }
8840
8936
  });
8841
8937
 
8842
- // src/tui/App.tsx
8843
- 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";
8844
8940
  import { useState as useState7 } from "react";
8845
- 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";
8846
9038
  function LiveHint() {
8847
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
8848
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, bold: true, children: "\u25B6 REALTIME CONSOLE" }),
8849
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "Fullscreen live monitor \u2014 streaming tool calls, active charts," }),
8850
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "counters, DLP hits and denial alerts. Updates every 2s." }),
8851
- /* @__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: [
8852
9044
  "press ",
8853
- /* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: "enter" }),
9045
+ /* @__PURE__ */ jsx9(Text9, { color: theme.accent, children: "enter" }),
8854
9046
  " to go live"
8855
9047
  ] }) })
8856
9048
  ] });
@@ -8858,22 +9050,22 @@ function LiveHint() {
8858
9050
  function Banner() {
8859
9051
  const wide = (process.stdout.columns ?? 80) >= 82;
8860
9052
  if (!wide) {
8861
- return /* @__PURE__ */ jsxs8(Box8, { children: [
8862
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate" }),
8863
- /* @__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" })
8864
9056
  ] });
8865
9057
  }
8866
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
8867
- BANNER_FULL.map((line, i) => /* @__PURE__ */ jsx8(Text8, { bold: true, color: BANNER_HEX[i], children: line }, i)),
8868
- /* @__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" })
8869
9061
  ] });
8870
9062
  }
8871
9063
  function App() {
8872
9064
  const { exit } = useApp();
8873
- const [section, setSection] = useState7(0);
8874
- const [focus, setFocus] = useState7("nav");
8875
- const [help, setHelp] = useState7(false);
8876
- 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) => {
8877
9069
  if (help) {
8878
9070
  setHelp(false);
8879
9071
  return;
@@ -8895,31 +9087,31 @@ function App() {
8895
9087
  const cols = process.stdout.columns ?? 100;
8896
9088
  const rows = process.stdout.rows ?? 30;
8897
9089
  const current = SECTIONS[section];
8898
- if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
9090
+ if (help) return /* @__PURE__ */ jsx9(HelpOverlay, { cols, rows });
8899
9091
  if (current.label === "Live" && focus === "panel") {
8900
- 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 }) });
8901
9093
  }
8902
9094
  const Panel = current.Panel;
8903
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 1, paddingTop: 1, children: [
8904
- /* @__PURE__ */ jsx8(Banner, {}),
8905
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexGrow: 1, children: [
8906
- /* @__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)) }),
8907
- /* @__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" }) })
8908
9100
  ] }),
8909
- /* @__PURE__ */ jsx8(Box8, { children: focus === "nav" ? /* @__PURE__ */ jsx8(KeyHints, { hints: [["\u2191\u2193", "section"], ["\u2192/enter", "open"], ["?", "help"], ["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"]] }) })
8910
9102
  ] });
8911
9103
  }
8912
9104
  function HelpOverlay({ cols, rows }) {
8913
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: cols, height: rows, paddingX: 2, paddingTop: 1, children: [
8914
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accentBright, children: "SolonGate \u2014 keyboard shortcuts" }),
8915
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: HELP.map(([group, keys]) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
8916
- /* @__PURE__ */ jsx8(Text8, { bold: true, color: theme.accent, children: group }),
8917
- keys.map(([k, desc]) => /* @__PURE__ */ jsxs8(Text8, { children: [
8918
- /* @__PURE__ */ jsx8(Text8, { color: theme.accentBright, children: (" " + k).padEnd(16) }),
8919
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: desc })
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 })
8920
9112
  ] }, k))
8921
9113
  ] }, group)) }),
8922
- /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
9114
+ /* @__PURE__ */ jsx9(Text9, { color: theme.dim, children: "press any key to close" })
8923
9115
  ] });
8924
9116
  }
8925
9117
  var SECTIONS, BANNER_HEX, HELP;
@@ -8935,8 +9127,10 @@ var init_App = __esm({
8935
9127
  init_Dlp();
8936
9128
  init_Stats();
8937
9129
  init_Audit();
9130
+ init_Agents();
8938
9131
  SECTIONS = [
8939
9132
  { label: "Live", Panel: LiveHint },
9133
+ { label: "Agents", Panel: AgentsPanel },
8940
9134
  { label: "Policies", Panel: PoliciesPanel },
8941
9135
  { label: "Rate Limit", Panel: RateLimitPanel },
8942
9136
  { label: "DLP", Panel: DlpPanel },
@@ -8959,7 +9153,7 @@ __export(tui_exports, {
8959
9153
  launchTui: () => launchTui
8960
9154
  });
8961
9155
  import { render } from "ink";
8962
- import { jsx as jsx9 } from "react/jsx-runtime";
9156
+ import { jsx as jsx10 } from "react/jsx-runtime";
8963
9157
  async function launchTui() {
8964
9158
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
8965
9159
  process.stderr.write(
@@ -8973,7 +9167,7 @@ async function launchTui() {
8973
9167
  }
8974
9168
  process.stdout.write("\x1B[?1049h\x1B[H");
8975
9169
  try {
8976
- const { waitUntilExit } = render(/* @__PURE__ */ jsx9(App, {}));
9170
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx10(App, {}));
8977
9171
  await waitUntilExit();
8978
9172
  } finally {
8979
9173
  process.stdout.write("\x1B[?1049l");
@@ -9085,7 +9279,7 @@ var init_args = __esm({
9085
9279
  });
9086
9280
 
9087
9281
  // src/commands/policy.ts
9088
- import { readFileSync as readFileSync5 } from "fs";
9282
+ import { readFileSync as readFileSync6 } from "fs";
9089
9283
  async function run(argv) {
9090
9284
  const { positionals, flags } = parse(argv);
9091
9285
  const sub = positionals[0];
@@ -9260,7 +9454,7 @@ function printRules(rules) {
9260
9454
  }
9261
9455
  async function resolveRules(target) {
9262
9456
  if (target.endsWith(".json")) {
9263
- const parsed = JSON.parse(readFileSync5(target, "utf-8"));
9457
+ const parsed = JSON.parse(readFileSync6(target, "utf-8"));
9264
9458
  return parsed.rules ?? [];
9265
9459
  }
9266
9460
  const p = await api.policies.get(target);
@@ -9624,7 +9818,7 @@ async function runAgents(argv) {
9624
9818
  table(
9625
9819
  ["STATUS", "AGENT", "CALLS", "DENY", "DLP", "TRUST", "CHARACTER"],
9626
9820
  res.agents.map((a) => [
9627
- statusColor(a.status),
9821
+ statusColor2(a.status),
9628
9822
  cyan(truncate3(a.agent_name ?? a.agent_id ?? a.session_id, 20)),
9629
9823
  String(a.total_calls),
9630
9824
  a.denied_calls ? red(String(a.denied_calls)) : dim("0"),
@@ -9643,7 +9837,7 @@ async function runAgent(argv) {
9643
9837
  const a = await api.agents.get(id);
9644
9838
  if (json) return printJson(a), 0;
9645
9839
  err("");
9646
- err(` ${bold(a.agent_id)} status: ${statusColor(String(a.status))}`);
9840
+ err(` ${bold(a.agent_id)} status: ${statusColor2(String(a.status))}`);
9647
9841
  const base = a.baseline;
9648
9842
  if (base) {
9649
9843
  err(` ${dim(base.character ?? "")} trust ${bold(String(base.trustScore ?? "\u2014"))}/100 deny-rate ${(Number(base.denyRate ?? 0) * 100).toFixed(0)}%`);
@@ -9663,21 +9857,21 @@ async function runAgent(argv) {
9663
9857
  }
9664
9858
  return 0;
9665
9859
  }
9666
- var statusColor;
9860
+ var statusColor2;
9667
9861
  var init_agents2 = __esm({
9668
9862
  "src/commands/agents.ts"() {
9669
9863
  "use strict";
9670
9864
  init_api_client();
9671
9865
  init_args();
9672
9866
  init_format();
9673
- 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);
9674
9868
  }
9675
9869
  });
9676
9870
 
9677
9871
  // src/commands/doctor.ts
9678
9872
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
9679
- import { homedir as homedir4 } from "os";
9680
- import { join as join6 } from "path";
9873
+ import { homedir as homedir5 } from "os";
9874
+ import { join as join7 } from "path";
9681
9875
  async function run6(argv) {
9682
9876
  const { flags } = parse(argv);
9683
9877
  const json = flagBool(flags, "json");
@@ -9737,14 +9931,14 @@ var init_doctor = __esm({
9737
9931
  init_api_client();
9738
9932
  init_format();
9739
9933
  init_args();
9740
- LOCAL_LOG2 = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
9934
+ LOCAL_LOG2 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
9741
9935
  }
9742
9936
  });
9743
9937
 
9744
9938
  // src/commands/watch.ts
9745
9939
  import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
9746
- import { homedir as homedir5 } from "os";
9747
- import { join as join7 } from "path";
9940
+ import { homedir as homedir6 } from "os";
9941
+ import { join as join8 } from "path";
9748
9942
  function tailLocal(file, maxBytes = 131072) {
9749
9943
  try {
9750
9944
  const size = statSync3(file).size;
@@ -9851,7 +10045,7 @@ var init_watch = __esm({
9851
10045
  init_cli_utils();
9852
10046
  init_args();
9853
10047
  init_format();
9854
- LOCAL_LOG3 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
10048
+ LOCAL_LOG3 = join8(homedir6(), ".solongate", "local-logs", "solongate-audit.jsonl");
9855
10049
  trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
9856
10050
  time = (ms) => new Date(ms).toTimeString().slice(0, 8);
9857
10051
  }
@@ -10153,9 +10347,9 @@ __export(global_install_exports, {
10153
10347
  runGlobalRestore: () => runGlobalRestore,
10154
10348
  unlockProtected: () => unlockProtected
10155
10349
  });
10156
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
10157
- import { resolve as resolve4, join as join8, dirname } from "path";
10158
- 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";
10159
10353
  import { fileURLToPath } from "url";
10160
10354
  import { createInterface } from "readline";
10161
10355
  import { execFileSync as execFileSync2 } from "child_process";
@@ -10218,10 +10412,10 @@ function unlockFile(file) {
10218
10412
  function protectedTargets() {
10219
10413
  const p = globalPaths();
10220
10414
  return [
10221
- join8(p.hooksDir, "guard.mjs"),
10222
- join8(p.hooksDir, "audit.mjs"),
10223
- join8(p.hooksDir, "stop.mjs"),
10224
- 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"),
10225
10419
  p.configPath,
10226
10420
  p.settingsPath
10227
10421
  ];
@@ -10233,26 +10427,26 @@ function unlockProtected() {
10233
10427
  for (const f of protectedTargets()) unlockFile(f);
10234
10428
  }
10235
10429
  function globalPaths() {
10236
- const home = homedir6();
10237
- const sgDir = join8(home, ".solongate");
10238
- const hooksDir = join8(sgDir, "hooks");
10239
- 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");
10240
10434
  return {
10241
10435
  home,
10242
10436
  sgDir,
10243
10437
  hooksDir,
10244
10438
  claudeDir,
10245
- settingsPath: join8(claudeDir, "settings.json"),
10246
- backupPath: join8(claudeDir, "settings.solongate.bak"),
10247
- 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")
10248
10442
  };
10249
10443
  }
10250
10444
  function readHook(filename) {
10251
- return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
10445
+ return readFileSync7(join9(HOOKS_DIR, filename), "utf-8");
10252
10446
  }
10253
10447
  function readGuard() {
10254
- const bundled = join8(HOOKS_DIR, "guard.bundled.mjs");
10255
- 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");
10256
10450
  }
10257
10451
  function ask(question) {
10258
10452
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -10266,13 +10460,13 @@ function runGlobalRestore() {
10266
10460
  unlockProtected();
10267
10461
  removeClaudeShim();
10268
10462
  if (existsSync6(p.backupPath)) {
10269
- writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
10463
+ writeFileSync4(p.settingsPath, readFileSync7(p.backupPath, "utf-8"));
10270
10464
  console.log(` Restored ${p.settingsPath} from backup.`);
10271
10465
  } else if (existsSync6(p.settingsPath)) {
10272
10466
  try {
10273
- const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
10467
+ const s = JSON.parse(readFileSync7(p.settingsPath, "utf-8"));
10274
10468
  delete s.hooks;
10275
- writeFileSync3(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10469
+ writeFileSync4(p.settingsPath, JSON.stringify(s, null, 2) + "\n");
10276
10470
  console.log(` Removed SolonGate hooks from ${p.settingsPath}.`);
10277
10471
  } catch {
10278
10472
  }
@@ -10306,18 +10500,18 @@ function shimTargets() {
10306
10500
  return [];
10307
10501
  }
10308
10502
  }
10309
- 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));
10310
10504
  }
10311
10505
  function writeShimBlock(file, block2) {
10312
10506
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
10313
- let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
10507
+ let content = existsSync6(file) ? readFileSync7(file, "utf-8") : "";
10314
10508
  content = content.replace(re, "");
10315
10509
  if (block2) {
10316
10510
  if (content.length && !content.endsWith("\n")) content += "\n";
10317
10511
  content += block2 + "\n";
10318
10512
  }
10319
- mkdirSync3(dirname(file), { recursive: true });
10320
- writeFileSync3(file, content);
10513
+ mkdirSync4(dirname(file), { recursive: true });
10514
+ writeFileSync4(file, content);
10321
10515
  }
10322
10516
  function installClaudeShim(shieldPath) {
10323
10517
  const real = resolveRealClaude();
@@ -10361,7 +10555,7 @@ async function runGlobalInstall(opts = {}) {
10361
10555
  let apiKey = opts.apiKey || process.env["SOLONGATE_API_KEY"] || "";
10362
10556
  if (!apiKey || apiKey === "sg_live_your_key_here") {
10363
10557
  try {
10364
- const cfg = JSON.parse(readFileSync6(p.configPath, "utf-8"));
10558
+ const cfg = JSON.parse(readFileSync7(p.configPath, "utf-8"));
10365
10559
  if (cfg && typeof cfg.apiKey === "string") apiKey = cfg.apiKey;
10366
10560
  } catch {
10367
10561
  }
@@ -10374,22 +10568,22 @@ async function runGlobalInstall(opts = {}) {
10374
10568
  process.exit(1);
10375
10569
  }
10376
10570
  const apiUrl = opts.apiUrl || process.env["SOLONGATE_API_URL"] || "https://api.solongate.com";
10377
- mkdirSync3(p.hooksDir, { recursive: true });
10378
- mkdirSync3(p.claudeDir, { recursive: true });
10571
+ mkdirSync4(p.hooksDir, { recursive: true });
10572
+ mkdirSync4(p.claudeDir, { recursive: true });
10379
10573
  unlockProtected();
10380
- writeFileSync3(join8(p.hooksDir, "guard.mjs"), readGuard());
10381
- writeFileSync3(join8(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10382
- writeFileSync3(join8(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10383
- 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"));
10384
10578
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
10385
10579
  removeClaudeShim();
10386
- writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10580
+ writeFileSync4(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
10387
10581
  console.log(` Wrote ${p.configPath}`);
10388
10582
  let existing = {};
10389
10583
  if (existsSync6(p.settingsPath)) {
10390
- const raw = readFileSync6(p.settingsPath, "utf-8");
10584
+ const raw = readFileSync7(p.settingsPath, "utf-8");
10391
10585
  if (!existsSync6(p.backupPath)) {
10392
- writeFileSync3(p.backupPath, raw);
10586
+ writeFileSync4(p.backupPath, raw);
10393
10587
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
10394
10588
  }
10395
10589
  try {
@@ -10398,19 +10592,21 @@ async function runGlobalInstall(opts = {}) {
10398
10592
  existing = {};
10399
10593
  }
10400
10594
  }
10401
- const guardAbs = join8(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10402
- const auditAbs = join8(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10403
- 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, "/");
10404
10598
  const nodeBin = process.execPath.replace(/\\/g, "/");
10599
+ const call = process.platform === "win32" ? "& " : "";
10600
+ const hookCmd = (script) => `${call}"${nodeBin}" "${script}" claude-code "Claude Code"`;
10405
10601
  const merged = {
10406
10602
  ...existing,
10407
10603
  hooks: {
10408
- PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: `"${nodeBin}" "${guardAbs}" claude-code "Claude Code"` }] }],
10409
- PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: `"${nodeBin}" "${auditAbs}" claude-code "Claude Code"` }] }],
10410
- Stop: [{ matcher: "", hooks: [{ type: "command", command: `"${nodeBin}" "${stopAbs}" claude-code "Claude Code"` }] }]
10604
+ PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(guardAbs) }] }],
10605
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(auditAbs) }] }],
10606
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: hookCmd(stopAbs) }] }]
10411
10607
  }
10412
10608
  };
10413
- writeFileSync3(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10609
+ writeFileSync4(p.settingsPath, JSON.stringify(merged, null, 2) + "\n");
10414
10610
  console.log(` Registered global hooks \u2192 ${p.settingsPath}`);
10415
10611
  if (process.env["SOLONGATE_OS_LOCK"] === "1") {
10416
10612
  lockProtected();
@@ -10590,11 +10786,11 @@ import { createServer, request as httpRequest } from "http";
10590
10786
  import { request as httpsRequest } from "https";
10591
10787
  import { spawn as spawn3 } from "child_process";
10592
10788
  import { URL as URL2 } from "url";
10593
- import { readFileSync as readFileSync7, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
10789
+ import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
10594
10790
  import { resolve as resolve5 } from "path";
10595
- import { homedir as homedir7 } from "os";
10791
+ import { homedir as homedir8 } from "os";
10596
10792
  function findCacheFile() {
10597
- const dir = resolve5(homedir7(), ".solongate");
10793
+ const dir = resolve5(homedir8(), ".solongate");
10598
10794
  const envSel = process.env.SOLONGATE_AGENT_ID;
10599
10795
  if (envSel) {
10600
10796
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
@@ -10620,7 +10816,7 @@ function loadCfg() {
10620
10816
  try {
10621
10817
  const f = findCacheFile();
10622
10818
  if (f && existsSync7(f)) {
10623
- const c2 = JSON.parse(readFileSync7(f, "utf-8"));
10819
+ const c2 = JSON.parse(readFileSync8(f, "utf-8"));
10624
10820
  const d = c2?.security?.dlpRedact;
10625
10821
  const g = c2?.security?.ghost;
10626
10822
  const ghost = g && Array.isArray(g.patterns) ? g.patterns : [];
@@ -10896,9 +11092,9 @@ __export(logs_server_exports, {
10896
11092
  runLogsServer: () => runLogsServer
10897
11093
  });
10898
11094
  import { createServer as createServer2 } from "http";
10899
- import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
10900
- import { resolve as resolve6, join as join9, isAbsolute } from "path";
10901
- import { homedir as homedir8 } from "os";
11095
+ import { readFileSync as readFileSync9, statSync as statSync5 } from "fs";
11096
+ import { resolve as resolve6, join as join10, isAbsolute } from "path";
11097
+ import { homedir as homedir9 } from "os";
10902
11098
  import { readdirSync as readdirSync2 } from "fs";
10903
11099
  function allowedOrigins() {
10904
11100
  const base = [
@@ -10915,15 +11111,15 @@ function resolveLocalLogDir(rawPath) {
10915
11111
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
10916
11112
  if (!dir) return null;
10917
11113
  if (isAbsolute(dir)) return dir;
10918
- return resolve6(homedir8(), ".solongate", "local-logs");
11114
+ return resolve6(homedir9(), ".solongate", "local-logs");
10919
11115
  }
10920
11116
  async function findLogDir() {
10921
- const base = resolve6(homedir8(), ".solongate");
11117
+ const base = resolve6(homedir9(), ".solongate");
10922
11118
  try {
10923
11119
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
10924
11120
  for (const f of files) {
10925
11121
  try {
10926
- const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
11122
+ const c2 = JSON.parse(readFileSync9(join10(base, f), "utf-8"));
10927
11123
  const p = c2?.security?.localLogs?.path;
10928
11124
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
10929
11125
  } catch {
@@ -10932,7 +11128,7 @@ async function findLogDir() {
10932
11128
  } catch {
10933
11129
  }
10934
11130
  try {
10935
- const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
11131
+ const cfgRaw = readFileSync9(join10(base, "cloud-guard.json"), "utf-8");
10936
11132
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
10937
11133
  if (apiKey) {
10938
11134
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -10961,7 +11157,7 @@ function setCors(req, res) {
10961
11157
  }
10962
11158
  function fileInfo(dir) {
10963
11159
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
10964
- const file = join9(dir, LOG_FILENAME);
11160
+ const file = join10(dir, LOG_FILENAME);
10965
11161
  try {
10966
11162
  const st = statSync5(file);
10967
11163
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
@@ -11022,7 +11218,7 @@ async function runLogsServer() {
11022
11218
  return;
11023
11219
  }
11024
11220
  try {
11025
- const text = readFileSync8(info.file, "utf-8");
11221
+ const text = readFileSync9(info.file, "utf-8");
11026
11222
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8", "Last-Modified": lastMod, "X-Solongate-Exists": "1" });
11027
11223
  res.end(text);
11028
11224
  } catch {
@@ -11074,7 +11270,7 @@ var init_logs_server = __esm({
11074
11270
 
11075
11271
  // src/inject.ts
11076
11272
  var inject_exports = {};
11077
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync } from "fs";
11273
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, existsSync as existsSync8, copyFileSync } from "fs";
11078
11274
  import { resolve as resolve7 } from "path";
11079
11275
  import { execSync } from "child_process";
11080
11276
  function parseInjectArgs(argv) {
@@ -11134,7 +11330,7 @@ WHAT IT DOES
11134
11330
  function detectProject() {
11135
11331
  if (!existsSync8(resolve7("package.json"))) return false;
11136
11332
  try {
11137
- const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
11333
+ const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11138
11334
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
11139
11335
  return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
11140
11336
  } catch {
@@ -11143,7 +11339,7 @@ function detectProject() {
11143
11339
  }
11144
11340
  function findTsEntryFile() {
11145
11341
  try {
11146
- const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
11342
+ const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11147
11343
  if (pkg.bin) {
11148
11344
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
11149
11345
  if (typeof binPath === "string") {
@@ -11170,7 +11366,7 @@ function findTsEntryFile() {
11170
11366
  const full = resolve7(c2);
11171
11367
  if (existsSync8(full)) {
11172
11368
  try {
11173
- const content = readFileSync9(full, "utf-8");
11369
+ const content = readFileSync10(full, "utf-8");
11174
11370
  if (content.includes("McpServer") || content.includes("McpServer")) {
11175
11371
  return full;
11176
11372
  }
@@ -11190,7 +11386,7 @@ function detectPackageManager() {
11190
11386
  }
11191
11387
  function installSdk() {
11192
11388
  try {
11193
- const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
11389
+ const pkg = JSON.parse(readFileSync10(resolve7("package.json"), "utf-8"));
11194
11390
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
11195
11391
  if (allDeps["@solongate/proxy"]) {
11196
11392
  log3(" @solongate/proxy already installed");
@@ -11211,7 +11407,7 @@ function installSdk() {
11211
11407
  }
11212
11408
  }
11213
11409
  function injectTypeScript(filePath) {
11214
- const original = readFileSync9(filePath, "utf-8");
11410
+ const original = readFileSync10(filePath, "utf-8");
11215
11411
  const changes = [];
11216
11412
  let modified = original;
11217
11413
  if (modified.includes("SecureMcpServer")) {
@@ -11382,7 +11578,7 @@ async function main2() {
11382
11578
  log3("");
11383
11579
  log3(` Backup: ${backupPath}`);
11384
11580
  }
11385
- writeFileSync4(entryFile, result.modified);
11581
+ writeFileSync5(entryFile, result.modified);
11386
11582
  log3("");
11387
11583
  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");
11388
11584
  log3(" \u2502 SolonGate SDK injected successfully! \u2502");
@@ -11411,8 +11607,8 @@ var init_inject = __esm({
11411
11607
 
11412
11608
  // src/create.ts
11413
11609
  var create_exports = {};
11414
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
11415
- import { resolve as resolve8, join as join10 } from "path";
11610
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
11611
+ import { resolve as resolve8, join as join11 } from "path";
11416
11612
  import { execSync as execSync2 } from "child_process";
11417
11613
  function withSpinner(message, fn) {
11418
11614
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -11491,8 +11687,8 @@ EXAMPLES
11491
11687
  `);
11492
11688
  }
11493
11689
  function createProject(dir, name, _policy) {
11494
- writeFileSync5(
11495
- join10(dir, "package.json"),
11690
+ writeFileSync6(
11691
+ join11(dir, "package.json"),
11496
11692
  JSON.stringify(
11497
11693
  {
11498
11694
  name,
@@ -11521,8 +11717,8 @@ function createProject(dir, name, _policy) {
11521
11717
  2
11522
11718
  ) + "\n"
11523
11719
  );
11524
- writeFileSync5(
11525
- join10(dir, "tsconfig.json"),
11720
+ writeFileSync6(
11721
+ join11(dir, "tsconfig.json"),
11526
11722
  JSON.stringify(
11527
11723
  {
11528
11724
  compilerOptions: {
@@ -11542,9 +11738,9 @@ function createProject(dir, name, _policy) {
11542
11738
  2
11543
11739
  ) + "\n"
11544
11740
  );
11545
- mkdirSync4(join10(dir, "src"), { recursive: true });
11546
- writeFileSync5(
11547
- join10(dir, "src", "index.ts"),
11741
+ mkdirSync5(join11(dir, "src"), { recursive: true });
11742
+ writeFileSync6(
11743
+ join11(dir, "src", "index.ts"),
11548
11744
  `#!/usr/bin/env node
11549
11745
 
11550
11746
  console.log = (...args: unknown[]) => {
@@ -11585,8 +11781,8 @@ console.log('');
11585
11781
  console.log('Press Ctrl+C to stop.');
11586
11782
  `
11587
11783
  );
11588
- writeFileSync5(
11589
- join10(dir, ".mcp.json"),
11784
+ writeFileSync6(
11785
+ join11(dir, ".mcp.json"),
11590
11786
  JSON.stringify(
11591
11787
  {
11592
11788
  mcpServers: {
@@ -11603,13 +11799,13 @@ console.log('Press Ctrl+C to stop.');
11603
11799
  2
11604
11800
  ) + "\n"
11605
11801
  );
11606
- writeFileSync5(
11607
- join10(dir, ".env"),
11802
+ writeFileSync6(
11803
+ join11(dir, ".env"),
11608
11804
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
11609
11805
  `
11610
11806
  );
11611
- writeFileSync5(
11612
- join10(dir, ".gitignore"),
11807
+ writeFileSync6(
11808
+ join11(dir, ".gitignore"),
11613
11809
  `node_modules/
11614
11810
  dist/
11615
11811
  *.solongate-backup
@@ -11628,7 +11824,7 @@ async function main3() {
11628
11824
  process.exit(1);
11629
11825
  }
11630
11826
  withSpinner(`Setting up ${opts.name}...`, () => {
11631
- mkdirSync4(dir, { recursive: true });
11827
+ mkdirSync5(dir, { recursive: true });
11632
11828
  createProject(dir, opts.name, opts.policy);
11633
11829
  });
11634
11830
  if (!opts.noInstall) {
@@ -11702,14 +11898,14 @@ var init_create = __esm({
11702
11898
 
11703
11899
  // src/pull-push.ts
11704
11900
  var pull_push_exports = {};
11705
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
11901
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
11706
11902
  import { resolve as resolve9 } from "path";
11707
11903
  function loadEnv() {
11708
11904
  if (process.env.SOLONGATE_API_KEY) return;
11709
11905
  const envPath = resolve9(".env");
11710
11906
  if (!existsSync10(envPath)) return;
11711
11907
  try {
11712
- const content = readFileSync10(envPath, "utf-8");
11908
+ const content = readFileSync11(envPath, "utf-8");
11713
11909
  for (const line of content.split("\n")) {
11714
11910
  const trimmed = line.trim();
11715
11911
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -11897,7 +12093,7 @@ async function pull(apiKey, file, policyId) {
11897
12093
  const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
11898
12094
  const { id: _id, ...policyWithoutId } = policy;
11899
12095
  const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
11900
- writeFileSync6(file, json, "utf-8");
12096
+ writeFileSync7(file, json, "utf-8");
11901
12097
  log5("");
11902
12098
  log5(green2(" Saved to: ") + file);
11903
12099
  log5(` ${dim2("Name:")} ${policy.name}`);
@@ -11926,7 +12122,7 @@ async function push(apiKey, file, policyId) {
11926
12122
  log5(" solongate-proxy list");
11927
12123
  process.exit(1);
11928
12124
  }
11929
- const content = readFileSync10(file, "utf-8");
12125
+ const content = readFileSync11(file, "utf-8");
11930
12126
  let policy;
11931
12127
  try {
11932
12128
  policy = JSON.parse(content);