@solongate/proxy 0.78.0 → 0.80.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
@@ -6867,10 +6867,18 @@ var init_policies = __esm({
6867
6867
  var settings_exports = {};
6868
6868
  __export(settings_exports, {
6869
6869
  clearRateLimitHistory: () => clearRateLimitHistory,
6870
+ createAlert: () => createAlert,
6871
+ createWebhook: () => createWebhook,
6872
+ deleteAlert: () => deleteAlert,
6873
+ deleteWebhook: () => deleteWebhook,
6874
+ getAlerts: () => getAlerts,
6870
6875
  getGuardStatus: () => getGuardStatus,
6871
6876
  getRateLimitHistory: () => getRateLimitHistory,
6872
6877
  getSecurityLayers: () => getSecurityLayers,
6873
- setSecurityLayers: () => setSecurityLayers
6878
+ getSelfProtection: () => getSelfProtection,
6879
+ getWebhooks: () => getWebhooks,
6880
+ setSecurityLayers: () => setSecurityLayers,
6881
+ setSelfProtection: () => setSelfProtection
6874
6882
  });
6875
6883
  function getSecurityLayers() {
6876
6884
  return request("GET", "/settings/security-layers");
@@ -6887,6 +6895,30 @@ function clearRateLimitHistory() {
6887
6895
  function getGuardStatus() {
6888
6896
  return request("GET", "/settings/guard-status");
6889
6897
  }
6898
+ function getSelfProtection() {
6899
+ return request("GET", "/settings/self-protection");
6900
+ }
6901
+ function setSelfProtection(enabled) {
6902
+ return request("PUT", "/settings/self-protection", { body: { enabled } });
6903
+ }
6904
+ function getAlerts() {
6905
+ return request("GET", "/settings/denial-alerts");
6906
+ }
6907
+ function createAlert(body) {
6908
+ return request("POST", "/settings/denial-alerts", { body });
6909
+ }
6910
+ function deleteAlert(id) {
6911
+ return request("DELETE", "/settings/denial-alerts", { query: { id } });
6912
+ }
6913
+ function getWebhooks() {
6914
+ return request("GET", "/settings/denial-webhook");
6915
+ }
6916
+ function createWebhook(body) {
6917
+ return request("POST", "/settings/denial-webhook", { body });
6918
+ }
6919
+ function deleteWebhook(id) {
6920
+ return request("DELETE", "/settings/denial-webhook", { query: { id } });
6921
+ }
6890
6922
  var init_settings = __esm({
6891
6923
  "src/api-client/settings.ts"() {
6892
6924
  "use strict";
@@ -6971,6 +7003,44 @@ var init_agents = __esm({
6971
7003
  }
6972
7004
  });
6973
7005
 
7006
+ // src/api-client/keys.ts
7007
+ var keys_exports = {};
7008
+ __export(keys_exports, {
7009
+ create: () => create2,
7010
+ list: () => list3,
7011
+ revoke: () => revoke
7012
+ });
7013
+ function list3() {
7014
+ return request("GET", "/keys");
7015
+ }
7016
+ function create2(name, isLive = true) {
7017
+ return request("POST", "/keys", { body: { name, is_live: isLive } });
7018
+ }
7019
+ function revoke(id) {
7020
+ return request("DELETE", `/keys/${encodeURIComponent(id)}`);
7021
+ }
7022
+ var init_keys = __esm({
7023
+ "src/api-client/keys.ts"() {
7024
+ "use strict";
7025
+ init_client();
7026
+ }
7027
+ });
7028
+
7029
+ // src/api-client/mcp.ts
7030
+ var mcp_exports = {};
7031
+ __export(mcp_exports, {
7032
+ list: () => list4
7033
+ });
7034
+ function list4() {
7035
+ return request("GET", "/mcp-servers");
7036
+ }
7037
+ var init_mcp = __esm({
7038
+ "src/api-client/mcp.ts"() {
7039
+ "use strict";
7040
+ init_client();
7041
+ }
7042
+ });
7043
+
6974
7044
  // src/api-client/index.ts
6975
7045
  var api;
6976
7046
  var init_api_client = __esm({
@@ -6983,7 +7053,9 @@ var init_api_client = __esm({
6983
7053
  init_stats();
6984
7054
  init_audit();
6985
7055
  init_agents();
6986
- api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports };
7056
+ init_keys();
7057
+ init_mcp();
7058
+ api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
6987
7059
  }
6988
7060
  });
6989
7061
 
@@ -7870,12 +7942,13 @@ function ruleSummary(r) {
7870
7942
  return bits.join(" ");
7871
7943
  }
7872
7944
  function PoliciesPanel({ focused }) {
7873
- const list4 = useLoader(() => api.policies.list());
7874
- const policies = list4.data?.policies ?? [];
7945
+ const list6 = useLoader(() => api.policies.list());
7946
+ const policies = list6.data?.policies ?? [];
7875
7947
  const [view, setView] = useState3("list");
7876
7948
  const [pi, setPi] = useState3(0);
7877
7949
  const [ri, setRi] = useState3(0);
7878
7950
  const [fi, setFi] = useState3(0);
7951
+ const [vi, setVi] = useState3(0);
7879
7952
  const [rules, setRules] = useState3([]);
7880
7953
  const [mode, setMode] = useState3("denylist");
7881
7954
  const [dirty, setDirty] = useState3(false);
@@ -7884,6 +7957,10 @@ function PoliciesPanel({ focused }) {
7884
7957
  const [status, setStatus] = useState3(null);
7885
7958
  const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
7886
7959
  const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
7960
+ const versionsQ = useLoader(
7961
+ () => view === "versions" && selected ? api.policies.versions(selected.id, { limit: 50 }) : Promise.resolve(null),
7962
+ [view, selected?.id]
7963
+ );
7887
7964
  useEffect3(() => {
7888
7965
  if (detail.data) {
7889
7966
  setRules(detail.data.rules);
@@ -7913,7 +7990,7 @@ function PoliciesPanel({ focused }) {
7913
7990
  setRules(res.rules);
7914
7991
  setDirty(false);
7915
7992
  setStatus("\u2713 Saved v" + res._version);
7916
- list4.reload();
7993
+ list6.reload();
7917
7994
  } catch (e) {
7918
7995
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
7919
7996
  }
@@ -7958,10 +8035,41 @@ function PoliciesPanel({ focused }) {
7958
8035
  }
7959
8036
  } else if (input === "m") {
7960
8037
  mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
8038
+ } else if (input === "n") {
8039
+ const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true };
8040
+ mutate([nr, ...rules]);
8041
+ setRi(0);
8042
+ setFi(0);
8043
+ setView("rule");
8044
+ } else if (input === "D") {
8045
+ setStatus("Dry-running\u2026");
8046
+ 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))));
8047
+ } else if (input === "v") {
8048
+ setVi(0);
8049
+ setView("versions");
7961
8050
  } else if (input === "s") void save();
7962
8051
  else if (input === "x") discard();
7963
8052
  return;
7964
8053
  }
8054
+ if (view === "versions") {
8055
+ const vers = versionsQ.data?.versions ?? [];
8056
+ if (key.leftArrow) return void setView("rules");
8057
+ if (key.upArrow) setVi((n) => Math.max(0, n - 1));
8058
+ else if (key.downArrow) setVi((n) => Math.min(vers.length - 1, n + 1));
8059
+ else if (key.return) {
8060
+ const v = vers[vi];
8061
+ if (v && selected) {
8062
+ setStatus("Rolling back\u2026");
8063
+ void api.policies.rollback(selected.id, v.version).then((r) => {
8064
+ setStatus(`\u2713 rolled back to v${v.version} \u2192 new v${r.version}`);
8065
+ detail.reload();
8066
+ list6.reload();
8067
+ setView("rules");
8068
+ }).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
8069
+ }
8070
+ }
8071
+ return;
8072
+ }
7965
8073
  const rule2 = rules[ri];
7966
8074
  if (!rule2) return setView("rules");
7967
8075
  const field = FIELDS[fi];
@@ -7983,7 +8091,7 @@ function PoliciesPanel({ focused }) {
7983
8091
  { isActive: focused && !editing }
7984
8092
  );
7985
8093
  if (view === "list") {
7986
- return /* @__PURE__ */ jsx3(DataView, { loading: list4.loading && !list4.data, error: list4.error, empty: !!list4.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8094
+ return /* @__PURE__ */ jsx3(DataView, { loading: list6.loading && !list6.data, error: list6.error, empty: !!list6.data && policies.length === 0, emptyText: "No policies.", children: /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
7987
8095
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
7988
8096
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: policies.map((p, i) => /* @__PURE__ */ jsxs3(Text3, { color: i === pi ? theme.accentBright : void 0, bold: i === pi, children: [
7989
8097
  (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
@@ -8006,7 +8114,7 @@ function PoliciesPanel({ focused }) {
8006
8114
  /* @__PURE__ */ jsx3(Text3, { color: mode === "whitelist" ? theme.warn : void 0, children: mode }),
8007
8115
  dirtyTag
8008
8116
  ] }),
8009
- /* @__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" }),
8117
+ /* @__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" }),
8010
8118
  /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8011
8119
  Table,
8012
8120
  {
@@ -8032,6 +8140,33 @@ function PoliciesPanel({ focused }) {
8032
8140
  status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8033
8141
  ] }) });
8034
8142
  }
8143
+ if (view === "versions") {
8144
+ const vers = versionsQ.data?.versions ?? [];
8145
+ 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: [
8146
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accentBright, children: truncate2(selected?.name ?? "", 28) }),
8147
+ /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: "\u2191\u2193 select \xB7 enter roll back to that version \xB7 \u2190 back" }),
8148
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
8149
+ Table,
8150
+ {
8151
+ columns: [
8152
+ { header: "", width: 2 },
8153
+ { header: "VER", width: 5 },
8154
+ { header: "RULES", width: 5 },
8155
+ { header: "REASON", width: 34 },
8156
+ { header: "WHEN", width: 16 }
8157
+ ],
8158
+ rows: vers.map((v, i) => [
8159
+ { value: i === vi ? "\u25B8" : "", color: theme.accentBright },
8160
+ { value: `v${v.version}`, bold: i === vi },
8161
+ { value: String(v.rules_count), dim: true },
8162
+ { value: truncate2(v.reason || "\u2014", 34) },
8163
+ { value: truncate2(v.created_at, 16), dim: true }
8164
+ ])
8165
+ }
8166
+ ) }),
8167
+ status ? /* @__PURE__ */ jsx3(Text3, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
8168
+ ] }) });
8169
+ }
8035
8170
  const rule = rules[ri];
8036
8171
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
8037
8172
  /* @__PURE__ */ jsxs3(Box3, { children: [
@@ -8255,12 +8390,19 @@ function DlpPanel({ focused }) {
8255
8390
  const [adding, setAdding] = useState5(null);
8256
8391
  const [newName, setNewName] = useState5("");
8257
8392
  const [newRe, setNewRe] = useState5("");
8393
+ const [ghostOn, setGhostOn] = useState5(false);
8394
+ const spQ = useLoader(() => api.settings.getSelfProtection());
8395
+ const [selfProt, setSelfProt] = useState5(null);
8258
8396
  useEffect5(() => {
8259
8397
  if (q.data && !dlp) {
8260
8398
  setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
8261
8399
  setAvailable(q.data.availablePatterns);
8400
+ setGhostOn(q.data.layers.ghost.mode === "on");
8262
8401
  }
8263
8402
  }, [q.data, dlp]);
8403
+ useEffect5(() => {
8404
+ if (spQ.data && selfProt === null) setSelfProt(spQ.data.enabled);
8405
+ }, [spQ.data, selfProt]);
8264
8406
  const mutate = (next) => {
8265
8407
  setDlp(next);
8266
8408
  setDirty(true);
@@ -8270,14 +8412,27 @@ function DlpPanel({ focused }) {
8270
8412
  if (!dlp || !q.data) return;
8271
8413
  setStatus("Saving\u2026");
8272
8414
  try {
8273
- const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp });
8415
+ const ghost = { ...q.data.layers.ghost, mode: ghostOn ? "on" : "off" };
8416
+ const res = await api.settings.setSecurityLayers({ ...q.data.layers, dlp, ghost });
8274
8417
  setDlp({ ...res.layers.dlp, patterns: [...res.layers.dlp.patterns], custom: [...res.layers.dlp.custom] });
8418
+ setGhostOn(res.layers.ghost.mode === "on");
8275
8419
  setDirty(false);
8276
8420
  setStatus("\u2713 Saved");
8277
8421
  } catch (e) {
8278
8422
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
8279
8423
  }
8280
8424
  };
8425
+ const toggleSelfProt = async () => {
8426
+ const next = !(selfProt ?? false);
8427
+ setSelfProt(next);
8428
+ try {
8429
+ await api.settings.setSelfProtection(next);
8430
+ setStatus(`\u2713 self-protection ${next ? "on" : "off"}`);
8431
+ } catch (e) {
8432
+ setSelfProt(!next);
8433
+ setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
8434
+ }
8435
+ };
8281
8436
  const discard = () => {
8282
8437
  if (q.data) setDlp({ ...q.data.layers.dlp, patterns: [...q.data.layers.dlp.patterns], custom: [...q.data.layers.dlp.custom] });
8283
8438
  setDirty(false);
@@ -8307,7 +8462,12 @@ function DlpPanel({ focused }) {
8307
8462
  const ci = sel - available.length;
8308
8463
  mutate({ ...dlp, custom: dlp.custom.filter((_, i) => i !== ci) });
8309
8464
  setSel((n) => Math.max(0, n - 1));
8310
- } else if (input === "s") void save();
8465
+ } else if (input === "g") {
8466
+ setGhostOn((v) => !v);
8467
+ setDirty(true);
8468
+ setStatus(null);
8469
+ } else if (input === "P") void toggleSelfProt();
8470
+ else if (input === "s") void save();
8311
8471
  else if (input === "x") discard();
8312
8472
  },
8313
8473
  { isActive: focused && !adding }
@@ -8319,7 +8479,13 @@ function DlpPanel({ focused }) {
8319
8479
  /* @__PURE__ */ jsx5(Text5, { color: modeColor(dlp.mode), children: dlp.mode }),
8320
8480
  dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
8321
8481
  ] }),
8322
- /* @__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" }),
8482
+ /* @__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" }),
8483
+ /* @__PURE__ */ jsxs5(Box5, { children: [
8484
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: "ghost: " }),
8485
+ /* @__PURE__ */ jsx5(Text5, { color: ghostOn ? theme.ok : theme.dim, children: ghostOn ? "on" : "off" }),
8486
+ /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " self-protection: " }),
8487
+ /* @__PURE__ */ jsx5(Text5, { color: selfProt ? theme.ok : theme.dim, children: selfProt === null ? "\u2026" : selfProt ? "on" : "off" })
8488
+ ] }),
8323
8489
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: available.map((p, i) => {
8324
8490
  const on = enabled.has(p);
8325
8491
  const active2 = focused && sel === i;
@@ -8387,12 +8553,15 @@ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
8387
8553
  function StatsPanel({ active: active2 }) {
8388
8554
  const stats = useLoader(() => api.stats.get());
8389
8555
  const ts = useLoader(() => api.stats.timeseries({ period: "24h" }));
8556
+ const drift2 = useLoader(() => api.stats.drift(7));
8390
8557
  usePoll(() => {
8391
8558
  stats.reload();
8392
8559
  ts.reload();
8393
8560
  }, 5e3, active2);
8561
+ usePoll(drift2.reload, 3e4, active2);
8394
8562
  const s = stats.data;
8395
8563
  const points = ts.data?.timeseries ?? [];
8564
+ const driftRules = (drift2.data?.rules ?? []).slice(0, 5);
8396
8565
  return /* @__PURE__ */ jsx6(DataView, { loading: stats.loading && !s, error: stats.error, children: s ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
8397
8566
  /* @__PURE__ */ jsxs6(Box6, { children: [
8398
8567
  /* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
@@ -8445,7 +8614,7 @@ function StatsPanel({ active: active2 }) {
8445
8614
  { header: "MS", width: 5 },
8446
8615
  { header: "WHEN", width: 6 }
8447
8616
  ],
8448
- rows: (s.recent_activity ?? []).slice(0, 8).map((a) => [
8617
+ rows: (s.recent_activity ?? []).slice(0, 6).map((a) => [
8449
8618
  { value: a.decision, color: decisionColor(a.decision) },
8450
8619
  { value: truncate2(a.tool_name, 20), color: theme.accent },
8451
8620
  { value: String(a.evaluation_time_ms ?? "\u2014"), dim: true },
@@ -8453,6 +8622,31 @@ function StatsPanel({ active: active2 }) {
8453
8622
  ])
8454
8623
  }
8455
8624
  )
8625
+ ] }),
8626
+ /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, flexDirection: "column", children: [
8627
+ /* @__PURE__ */ jsxs6(Text6, { color: theme.dim, children: [
8628
+ "Denial drift ",
8629
+ drift2.data ? `(7d: ${drift2.data.total_current} now vs ${drift2.data.total_previous} prev)` : ""
8630
+ ] }),
8631
+ driftRules.length ? /* @__PURE__ */ jsx6(
8632
+ Table,
8633
+ {
8634
+ columns: [
8635
+ { header: "NOW", width: 5 },
8636
+ { header: "PREV", width: 5 },
8637
+ { header: "\u0394", width: 6 },
8638
+ { header: "RULE", width: 22 },
8639
+ { header: "REASON", width: 26 }
8640
+ ],
8641
+ rows: driftRules.map((r) => [
8642
+ { value: String(r.current), bold: true },
8643
+ { value: String(r.previous), dim: true },
8644
+ { value: r.is_new ? "NEW" : r.spike ? `+${r.delta}` : String(r.delta), color: r.is_new ? theme.ok : r.spike ? theme.bad : void 0 },
8645
+ { value: truncate2(r.rule_id ?? "\u2014", 22), color: theme.accent },
8646
+ { value: truncate2(r.reason ?? r.last_tool ?? "\u2014", 26), dim: true }
8647
+ ])
8648
+ }
8649
+ ) : /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: " no denials in window" })
8456
8650
  ] })
8457
8651
  ] }) : null });
8458
8652
  }
@@ -8678,7 +8872,16 @@ function App() {
8678
8872
  const { exit } = useApp();
8679
8873
  const [section, setSection] = useState7(0);
8680
8874
  const [focus, setFocus] = useState7("nav");
8875
+ const [help, setHelp] = useState7(false);
8681
8876
  useInput6((input, key) => {
8877
+ if (help) {
8878
+ setHelp(false);
8879
+ return;
8880
+ }
8881
+ if (input === "?" && focus === "nav") {
8882
+ setHelp(true);
8883
+ return;
8884
+ }
8682
8885
  if (focus === "nav") {
8683
8886
  if (key.upArrow) setSection((n) => (n - 1 + SECTIONS.length) % SECTIONS.length);
8684
8887
  else if (key.downArrow) setSection((n) => (n + 1) % SECTIONS.length);
@@ -8692,6 +8895,7 @@ function App() {
8692
8895
  const cols = process.stdout.columns ?? 100;
8693
8896
  const rows = process.stdout.rows ?? 30;
8694
8897
  const current = SECTIONS[section];
8898
+ if (help) return /* @__PURE__ */ jsx8(HelpOverlay, { cols, rows });
8695
8899
  if (current.label === "Live" && focus === "panel") {
8696
8900
  return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: cols, height: rows, children: /* @__PURE__ */ jsx8(LivePanel, { active: true, focused: true }) });
8697
8901
  }
@@ -8702,10 +8906,23 @@ function App() {
8702
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)) }),
8703
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" }) })
8704
8908
  ] }),
8705
- /* @__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"]] }) })
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"]] }) })
8706
8910
  ] });
8707
8911
  }
8708
- var SECTIONS, BANNER_HEX;
8912
+ 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 })
8920
+ ] }, k))
8921
+ ] }, group)) }),
8922
+ /* @__PURE__ */ jsx8(Text8, { color: theme.dim, children: "press any key to close" })
8923
+ ] });
8924
+ }
8925
+ var SECTIONS, BANNER_HEX, HELP;
8709
8926
  var init_App = __esm({
8710
8927
  "src/tui/App.tsx"() {
8711
8928
  "use strict";
@@ -8727,6 +8944,12 @@ var init_App = __esm({
8727
8944
  { label: "Audit", Panel: AuditPanel }
8728
8945
  ];
8729
8946
  BANNER_HEX = ["#1432A0", "#2850BE", "#3C6ED7", "#5A8CE6", "#82AAF0", "#AAC8FA"];
8947
+ HELP = [
8948
+ ["Global", [["\u2191\u2193", "move between sections"], ["\u2192 / enter", "open a section"], ["\u2190 / esc", "back to the menu"], ["?", "this help"], ["q", "quit"]]],
8949
+ ["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)"]]],
8950
+ ["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"]]],
8951
+ ["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"]]]
8952
+ ];
8730
8953
  }
8731
8954
  });
8732
8955
 
@@ -9334,6 +9557,16 @@ async function run5(argv) {
9334
9557
  else err(green(` \u2713 Whitelisted (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
9335
9558
  return 0;
9336
9559
  }
9560
+ if (positionals[0] === "block") {
9561
+ const id = positionals[1];
9562
+ if (!id) return err(" Usage: audit block <logId> [--scope exact|tool]"), 1;
9563
+ const scope = flagStr(flags, "scope") ?? "exact";
9564
+ const res2 = await api.audit.block(id, scope);
9565
+ if (json) return printJson(res2), 0;
9566
+ if (res2.deduped) err(green(" \u2713 ") + dim("Equivalent DENY already present."));
9567
+ else err(green(` \u2713 Blocked (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
9568
+ return 0;
9569
+ }
9337
9570
  const query = {
9338
9571
  filter: flagStr(flags, "filter"),
9339
9572
  tool: flagStr(flags, "tool"),
@@ -9441,6 +9674,395 @@ var init_agents2 = __esm({
9441
9674
  }
9442
9675
  });
9443
9676
 
9677
+ // src/commands/doctor.ts
9678
+ import { existsSync as existsSync4, statSync as statSync2 } from "fs";
9679
+ import { homedir as homedir4 } from "os";
9680
+ import { join as join6 } from "path";
9681
+ async function run6(argv) {
9682
+ const { flags } = parse(argv);
9683
+ const json = flagBool(flags, "json");
9684
+ const checks = [];
9685
+ if (!isAuthenticated()) {
9686
+ checks.push({ name: "login", ok: false, detail: "not logged in \u2014 run `solongate login`" });
9687
+ } else {
9688
+ const { apiUrl } = resolveCredentials();
9689
+ checks.push({ name: "login", ok: true, detail: `paired \xB7 ${apiUrl}` });
9690
+ try {
9691
+ const active2 = await api.policies.active();
9692
+ if (active2.policy) {
9693
+ checks.push({ name: "active policy", ok: true, detail: `${active2.policy.name} v${active2.version} \xB7 ${active2.policy.mode ?? "denylist"} \xB7 matched by ${active2.matched_by}` });
9694
+ } else {
9695
+ checks.push({ name: "active policy", ok: "warn", detail: "no policy resolves \u2014 every call falls back to default" });
9696
+ }
9697
+ const sec = active2.security;
9698
+ checks.push({ name: "rate limit", ok: sec?.rateLimit ? true : "warn", detail: sec?.rateLimit ? `${sec.rateLimit.perMinute}/min` : "off" });
9699
+ checks.push({ name: "dlp", ok: sec?.dlpBlock ? true : "warn", detail: sec?.dlpBlock ? `block \xB7 ${sec.dlpBlock.patterns.length} patterns` : sec?.dlpRedact ? "redact" : "off" });
9700
+ checks.push({ name: "self-protection", ok: active2.self_protection_enabled ? true : "warn", detail: active2.self_protection_enabled ? "on" : "off" });
9701
+ } catch (e) {
9702
+ checks.push({ name: "api", ok: false, detail: "unreachable: " + (e instanceof Error ? e.message : String(e)) });
9703
+ }
9704
+ try {
9705
+ const g = await api.settings.getGuardStatus();
9706
+ checks.push({ name: "guard hook", ok: g.up_to_date ? true : "warn", detail: g.up_to_date ? `v${g.installed} (latest) \xB7 ${g.device_count} device(s)` : `v${g.installed} \u2192 v${g.latest} available \xB7 run \`solongate login\`` });
9707
+ } catch {
9708
+ }
9709
+ }
9710
+ if (existsSync4(LOCAL_LOG2)) {
9711
+ const st = statSync2(LOCAL_LOG2);
9712
+ const ageMin = (Date.now() - st.mtimeMs) / 6e4;
9713
+ checks.push({ name: "local logs", ok: true, detail: `on \xB7 ${(st.size / 1024).toFixed(0)}KB \xB7 last write ${ageMin < 1 ? "just now" : Math.round(ageMin) + "m ago"}` });
9714
+ } else {
9715
+ checks.push({ name: "local logs", ok: "warn", detail: "off (logs go to cloud) \u2014 enable in dashboard \u2192 Settings" });
9716
+ }
9717
+ if (json) return printJson(checks), checks.some((c2) => c2.ok === false) ? 1 : 0;
9718
+ err("");
9719
+ err(` ${bold("SolonGate doctor")}`);
9720
+ err("");
9721
+ for (const c2 of checks) {
9722
+ const mark = c2.ok === true ? green("\u2713") : c2.ok === "warn" ? yellow("!") : red("\u2717");
9723
+ err(` ${mark} ${c2.name.padEnd(16)} ${dim(c2.detail)}`);
9724
+ }
9725
+ err("");
9726
+ const bad = checks.filter((c2) => c2.ok === false).length;
9727
+ const warn = checks.filter((c2) => c2.ok === "warn").length;
9728
+ if (bad) err(` ${red(`${bad} problem(s)`)}${warn ? dim(` \xB7 ${warn} warning(s)`) : ""}`);
9729
+ else if (warn) err(` ${yellow(`${warn} warning(s)`)} ${dim("\u2014 guard is working")}`);
9730
+ else err(` ${green("all good")}`);
9731
+ return bad ? 1 : 0;
9732
+ }
9733
+ var LOCAL_LOG2;
9734
+ var init_doctor = __esm({
9735
+ "src/commands/doctor.ts"() {
9736
+ "use strict";
9737
+ init_api_client();
9738
+ init_format();
9739
+ init_args();
9740
+ LOCAL_LOG2 = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
9741
+ }
9742
+ });
9743
+
9744
+ // src/commands/watch.ts
9745
+ 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";
9748
+ function tailLocal(file, maxBytes = 131072) {
9749
+ try {
9750
+ const size = statSync3(file).size;
9751
+ const start = Math.max(0, size - maxBytes);
9752
+ const fd = openSync2(file, "r");
9753
+ const buf = Buffer.alloc(size - start);
9754
+ readSync2(fd, buf, 0, buf.length, start);
9755
+ closeSync2(fd);
9756
+ const lines = buf.toString("utf-8").split("\n").filter(Boolean);
9757
+ if (start > 0) lines.shift();
9758
+ return lines;
9759
+ } catch {
9760
+ return [];
9761
+ }
9762
+ }
9763
+ function print(r, json) {
9764
+ if (json) return out(JSON.stringify(r));
9765
+ const dec = r.decision === "ALLOW" ? c.green : c.red;
9766
+ const src = r.source === "local" ? c.green + "LOC" : c.blue4 + "CLD";
9767
+ out(
9768
+ `${c.dim}[${time(r.at)}]${c.reset} ${src}${c.reset} ${dec}${r.decision.padEnd(6)}${c.reset} ${c.cyan}${trunc(r.tool, 12).padEnd(13)}${c.reset}${c.dim}${r.permission.slice(0, 4).padEnd(5)}${c.reset}${r.dlp ? c.red + "DLP! " + c.reset : ""}${c.dim}${trunc(r.agent || "-", 12).padEnd(13)}${r.detail}${c.reset}`
9769
+ );
9770
+ }
9771
+ async function run7(argv) {
9772
+ const { flags } = parse(argv);
9773
+ const json = flagBool(flags, "json");
9774
+ const decisionFilter = (flagStr(flags, "filter") || "").toUpperCase();
9775
+ const toolFilter = (flagStr(flags, "tool") || "").toLowerCase();
9776
+ const localOnly = flagBool(flags, "local-only");
9777
+ const cloudOnly = flagBool(flags, "cloud-only");
9778
+ const keep = (r) => {
9779
+ if (decisionFilter && r.decision.toUpperCase() !== decisionFilter && !(decisionFilter === "DENY" && r.decision === "DENIED")) return false;
9780
+ if (toolFilter && !r.tool.toLowerCase().includes(toolFilter)) return false;
9781
+ return true;
9782
+ };
9783
+ const seen = /* @__PURE__ */ new Set();
9784
+ let lastLocalTs = 0;
9785
+ let first = true;
9786
+ const emit = (rows) => {
9787
+ rows.sort((a, b) => a.at - b.at);
9788
+ for (const r of rows) if (keep(r)) print(r, json);
9789
+ };
9790
+ const pollLocal = () => {
9791
+ if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
9792
+ const rows = [];
9793
+ for (const line of tailLocal(LOCAL_LOG3)) {
9794
+ try {
9795
+ const j = JSON.parse(line);
9796
+ const at = Date.parse(String(j.ts ?? ""));
9797
+ if (!Number.isFinite(at) || at <= lastLocalTs) continue;
9798
+ rows.push({
9799
+ at,
9800
+ tool: String(j.tool ?? "?"),
9801
+ decision: String(j.decision ?? "ALLOW"),
9802
+ permission: String(j.permission ?? ""),
9803
+ detail: (j.arguments ? JSON.stringify(j.arguments) : String(j.reason ?? "")).replace(/\s+/g, " "),
9804
+ agent: String(j.agent_name ?? ""),
9805
+ source: "local",
9806
+ dlp: !!j.dlp
9807
+ });
9808
+ } catch {
9809
+ }
9810
+ }
9811
+ if (rows.length) lastLocalTs = rows[rows.length - 1].at;
9812
+ if (!first) emit(rows);
9813
+ };
9814
+ const pollCloud = async () => {
9815
+ if (localOnly) return;
9816
+ try {
9817
+ const res = await api.audit.list({ limit: 50 });
9818
+ const rows = [];
9819
+ for (const e of res.entries) {
9820
+ if (seen.has(e.id)) continue;
9821
+ seen.add(e.id);
9822
+ rows.push({
9823
+ at: Date.parse(e.created_at),
9824
+ tool: e.tool_name,
9825
+ decision: e.decision,
9826
+ permission: e.permission ?? "",
9827
+ detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
9828
+ agent: e.agent_name ?? "",
9829
+ source: "cloud",
9830
+ dlp: !!e.dlp_matches?.length
9831
+ });
9832
+ }
9833
+ if (!first) emit(rows);
9834
+ } catch {
9835
+ }
9836
+ };
9837
+ if (!json) err(` ${c.dim}watching guard stream \u2014 Ctrl+C to stop${c.reset}`);
9838
+ pollLocal();
9839
+ await pollCloud();
9840
+ first = false;
9841
+ return new Promise(() => {
9842
+ setInterval(pollLocal, 2e3);
9843
+ setInterval(() => void pollCloud(), 4e3);
9844
+ });
9845
+ }
9846
+ var LOCAL_LOG3, trunc, time;
9847
+ var init_watch = __esm({
9848
+ "src/commands/watch.ts"() {
9849
+ "use strict";
9850
+ init_api_client();
9851
+ init_cli_utils();
9852
+ init_args();
9853
+ init_format();
9854
+ LOCAL_LOG3 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
9855
+ trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
9856
+ time = (ms) => new Date(ms).toTimeString().slice(0, 8);
9857
+ }
9858
+ });
9859
+
9860
+ // src/commands/keys.ts
9861
+ async function run8(argv) {
9862
+ const { positionals, flags } = parse(argv);
9863
+ const sub = positionals[0] ?? "list";
9864
+ const json = flagBool(flags, "json");
9865
+ switch (sub) {
9866
+ case "help":
9867
+ return err(USAGE6), 0;
9868
+ case "list": {
9869
+ const { keys } = await api.keys.list();
9870
+ if (json) return printJson(keys), 0;
9871
+ if (!keys.length) return err(dim(" No API keys.")), 0;
9872
+ table(
9873
+ ["ID", "NAME", "PREFIX", "CREATED"],
9874
+ keys.map((k) => [dim(truncate3(k.id, 10)), truncate3(k.name, 24), cyan(k.key_prefix), dim(k.created_at)])
9875
+ );
9876
+ return 0;
9877
+ }
9878
+ case "create": {
9879
+ const name = flagStr(flags, "name");
9880
+ if (!name) return err(" Usage: keys create --name <name>"), 1;
9881
+ const res = await api.keys.create(name, !flagBool(flags, "test"));
9882
+ if (json) return printJson(res), 0;
9883
+ err(green(` \u2713 Created "${res.name}"`));
9884
+ err("");
9885
+ err(" " + bold(res.key));
9886
+ err(dim(" \u2191 shown only once \u2014 copy it now."));
9887
+ return 0;
9888
+ }
9889
+ case "revoke": {
9890
+ const id = positionals[1];
9891
+ if (!id) return err(" Usage: keys revoke <id>"), 1;
9892
+ await api.keys.revoke(id);
9893
+ if (json) return printJson({ ok: true, id }), 0;
9894
+ return err(green(` \u2713 Revoked ${id}`)), 0;
9895
+ }
9896
+ default:
9897
+ return err(USAGE6), 1;
9898
+ }
9899
+ }
9900
+ var USAGE6;
9901
+ var init_keys2 = __esm({
9902
+ "src/commands/keys.ts"() {
9903
+ "use strict";
9904
+ init_api_client();
9905
+ init_args();
9906
+ init_format();
9907
+ USAGE6 = `${bold("solongate keys")} \u2014 API keys
9908
+
9909
+ keys list List keys (prefix only)
9910
+ keys create --name <n> Create a key (secret shown once)
9911
+ keys revoke <id> Revoke a key
9912
+
9913
+ Add --json for machine-readable output.`;
9914
+ }
9915
+ });
9916
+
9917
+ // src/commands/mcp.ts
9918
+ async function run9(argv) {
9919
+ const { flags } = parse(argv);
9920
+ const json = flagBool(flags, "json");
9921
+ const { servers } = await api.mcp.list();
9922
+ if (json) return printJson(servers), 0;
9923
+ if (!servers.length) return err(dim(" No MCP servers registered.")), 0;
9924
+ table(
9925
+ ["STATUS", "NAME", "TARGET"],
9926
+ servers.map((s) => [
9927
+ s.status === "active" ? green(s.status) : yellow(s.status),
9928
+ cyan(truncate3(s.name, 24)),
9929
+ dim(truncate3(s.url || s.command || "\u2014", 46))
9930
+ ])
9931
+ );
9932
+ return 0;
9933
+ }
9934
+ var init_mcp2 = __esm({
9935
+ "src/commands/mcp.ts"() {
9936
+ "use strict";
9937
+ init_api_client();
9938
+ init_args();
9939
+ init_format();
9940
+ }
9941
+ });
9942
+
9943
+ // src/commands/alerts.ts
9944
+ async function run10(argv) {
9945
+ const { positionals, flags } = parse(argv);
9946
+ const sub = positionals[0] ?? "list";
9947
+ const json = flagBool(flags, "json");
9948
+ switch (sub) {
9949
+ case "help":
9950
+ return err(USAGE7), 0;
9951
+ case "list": {
9952
+ const { rules } = await api.settings.getAlerts();
9953
+ if (json) return printJson(rules), 0;
9954
+ if (!rules.length) return err(dim(" No alert rules.")), 0;
9955
+ table(
9956
+ ["ID", "ON", "SIGNAL", "THRESH", "WINDOW", "CHANNELS"],
9957
+ rules.map((r) => [
9958
+ dim(truncate3(r.id, 10)),
9959
+ r.enabled ? green("\u25CF") : dim("\u25CB"),
9960
+ cyan(r.signal),
9961
+ `${r.threshold}`,
9962
+ `${r.windowSeconds}s`,
9963
+ dim([...r.emails ?? [], ...(r.telegram ?? []).map((t) => "tg:" + t), ...(r.slackUrls ?? []).map(() => "slack")].join(" ") || "\u2014")
9964
+ ])
9965
+ );
9966
+ return 0;
9967
+ }
9968
+ case "add": {
9969
+ const email = flagStr(flags, "email");
9970
+ const telegram = flagStr(flags, "telegram");
9971
+ const slack = flagStr(flags, "slack");
9972
+ if (!email && !telegram && !slack) return err(" Need a channel: --email / --telegram / --slack"), 1;
9973
+ const res = await api.settings.createAlert({
9974
+ signal: flagStr(flags, "signal") ?? "deny",
9975
+ threshold: flagNum(flags, "threshold") ?? 5,
9976
+ windowSeconds: flagNum(flags, "window") ?? 300,
9977
+ emails: email ? [email] : void 0,
9978
+ telegram: telegram ? [telegram] : void 0,
9979
+ slackUrl: slack
9980
+ });
9981
+ if (json) return printJson(res.rule), 0;
9982
+ return err(green(` \u2713 Alert added (${res.rule.signal}, ${res.rule.threshold}/${res.rule.windowSeconds}s)`)), 0;
9983
+ }
9984
+ case "remove": {
9985
+ const id = positionals[1];
9986
+ if (!id) return err(" Usage: alerts remove <id>"), 1;
9987
+ await api.settings.deleteAlert(id);
9988
+ if (json) return printJson({ ok: true, id }), 0;
9989
+ return err(green(` \u2713 Removed ${id}`)), 0;
9990
+ }
9991
+ default:
9992
+ return err(USAGE7), 1;
9993
+ }
9994
+ }
9995
+ var USAGE7;
9996
+ var init_alerts = __esm({
9997
+ "src/commands/alerts.ts"() {
9998
+ "use strict";
9999
+ init_api_client();
10000
+ init_args();
10001
+ init_format();
10002
+ USAGE7 = `${bold("solongate alerts")} \u2014 spike alerts (Telegram / email)
10003
+
10004
+ alerts list
10005
+ alerts add --signal deny|dlp|ratelimit|any --threshold N --window S
10006
+ (--email <a> | --telegram <chatId> | --slack <url>)
10007
+ alerts remove <id>
10008
+
10009
+ Add --json for machine-readable output.`;
10010
+ }
10011
+ });
10012
+
10013
+ // src/commands/webhooks.ts
10014
+ async function run11(argv) {
10015
+ const { positionals, flags } = parse(argv);
10016
+ const sub = positionals[0] ?? "list";
10017
+ const json = flagBool(flags, "json");
10018
+ switch (sub) {
10019
+ case "help":
10020
+ return err(USAGE8), 0;
10021
+ case "list": {
10022
+ const { webhooks } = await api.settings.getWebhooks();
10023
+ if (json) return printJson(webhooks), 0;
10024
+ if (!webhooks.length) return err(dim(" No webhooks.")), 0;
10025
+ table(
10026
+ ["ID", "ON", "EVENTS", "URL"],
10027
+ webhooks.map((w) => [dim(truncate3(w.id, 10)), w.enabled ? green("\u25CF") : dim("\u25CB"), cyan(w.events), dim(truncate3(w.url, 46))])
10028
+ );
10029
+ return 0;
10030
+ }
10031
+ case "add": {
10032
+ const url = flagStr(flags, "url");
10033
+ if (!url) return err(" Usage: webhooks add --url <https://\u2026> [--events denials|allowed|all]"), 1;
10034
+ const res = await api.settings.createWebhook({ url, events: flagStr(flags, "events") ?? "denials" });
10035
+ if (json) return printJson(res.webhook), 0;
10036
+ return err(green(` \u2713 Webhook added (${res.webhook.events}) \u2192 ${res.webhook.url}`)), 0;
10037
+ }
10038
+ case "remove": {
10039
+ const id = positionals[1];
10040
+ if (!id) return err(" Usage: webhooks remove <id>"), 1;
10041
+ await api.settings.deleteWebhook(id);
10042
+ if (json) return printJson({ ok: true, id }), 0;
10043
+ return err(green(` \u2713 Removed ${id}`)), 0;
10044
+ }
10045
+ default:
10046
+ return err(USAGE8), 1;
10047
+ }
10048
+ }
10049
+ var USAGE8;
10050
+ var init_webhooks = __esm({
10051
+ "src/commands/webhooks.ts"() {
10052
+ "use strict";
10053
+ init_api_client();
10054
+ init_args();
10055
+ init_format();
10056
+ USAGE8 = `${bold("solongate webhooks")} \u2014 event webhooks
10057
+
10058
+ webhooks list
10059
+ webhooks add --url <https://\u2026> [--events denials|allowed|all]
10060
+ webhooks remove <id>
10061
+
10062
+ Add --json for machine-readable output.`;
10063
+ }
10064
+ });
10065
+
9444
10066
  // src/commands/index.ts
9445
10067
  var commands_exports = {};
9446
10068
  __export(commands_exports, {
@@ -9463,6 +10085,18 @@ async function dispatch(command, argv) {
9463
10085
  return runAgents(argv);
9464
10086
  case "agent":
9465
10087
  return runAgent(argv);
10088
+ case "doctor":
10089
+ return run6(argv);
10090
+ case "watch":
10091
+ return run7(argv);
10092
+ case "keys":
10093
+ return run8(argv);
10094
+ case "mcp":
10095
+ return run9(argv);
10096
+ case "alerts":
10097
+ return run10(argv);
10098
+ case "webhooks":
10099
+ return run11(argv);
9466
10100
  default:
9467
10101
  err(` Unknown command: ${command}`);
9468
10102
  return 1;
@@ -9497,7 +10131,13 @@ var init_commands = __esm({
9497
10131
  init_stats2();
9498
10132
  init_audit2();
9499
10133
  init_agents2();
9500
- COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"];
10134
+ init_doctor();
10135
+ init_watch();
10136
+ init_keys2();
10137
+ init_mcp2();
10138
+ init_alerts();
10139
+ init_webhooks();
10140
+ COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"];
9501
10141
  }
9502
10142
  });
9503
10143
 
@@ -9513,14 +10153,14 @@ __export(global_install_exports, {
9513
10153
  runGlobalRestore: () => runGlobalRestore,
9514
10154
  unlockProtected: () => unlockProtected
9515
10155
  });
9516
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
9517
- import { resolve as resolve4, join as join6, dirname } from "path";
9518
- import { homedir as homedir4 } from "os";
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";
9519
10159
  import { fileURLToPath } from "url";
9520
10160
  import { createInterface } from "readline";
9521
10161
  import { execFileSync as execFileSync2 } from "child_process";
9522
10162
  function lockFile(file) {
9523
- if (!existsSync4(file)) return;
10163
+ if (!existsSync6(file)) return;
9524
10164
  try {
9525
10165
  if (process.platform === "win32") {
9526
10166
  try {
@@ -9546,7 +10186,7 @@ function lockFile(file) {
9546
10186
  }
9547
10187
  }
9548
10188
  function unlockFile(file) {
9549
- if (!existsSync4(file)) return;
10189
+ if (!existsSync6(file)) return;
9550
10190
  try {
9551
10191
  if (process.platform === "win32") {
9552
10192
  try {
@@ -9578,10 +10218,10 @@ function unlockFile(file) {
9578
10218
  function protectedTargets() {
9579
10219
  const p = globalPaths();
9580
10220
  return [
9581
- join6(p.hooksDir, "guard.mjs"),
9582
- join6(p.hooksDir, "audit.mjs"),
9583
- join6(p.hooksDir, "stop.mjs"),
9584
- join6(p.hooksDir, "shield.mjs"),
10221
+ join8(p.hooksDir, "guard.mjs"),
10222
+ join8(p.hooksDir, "audit.mjs"),
10223
+ join8(p.hooksDir, "stop.mjs"),
10224
+ join8(p.hooksDir, "shield.mjs"),
9585
10225
  p.configPath,
9586
10226
  p.settingsPath
9587
10227
  ];
@@ -9593,26 +10233,26 @@ function unlockProtected() {
9593
10233
  for (const f of protectedTargets()) unlockFile(f);
9594
10234
  }
9595
10235
  function globalPaths() {
9596
- const home = homedir4();
9597
- const sgDir = join6(home, ".solongate");
9598
- const hooksDir = join6(sgDir, "hooks");
9599
- const claudeDir = join6(home, ".claude");
10236
+ const home = homedir6();
10237
+ const sgDir = join8(home, ".solongate");
10238
+ const hooksDir = join8(sgDir, "hooks");
10239
+ const claudeDir = join8(home, ".claude");
9600
10240
  return {
9601
10241
  home,
9602
10242
  sgDir,
9603
10243
  hooksDir,
9604
10244
  claudeDir,
9605
- settingsPath: join6(claudeDir, "settings.json"),
9606
- backupPath: join6(claudeDir, "settings.solongate.bak"),
9607
- configPath: join6(sgDir, "cloud-guard.json")
10245
+ settingsPath: join8(claudeDir, "settings.json"),
10246
+ backupPath: join8(claudeDir, "settings.solongate.bak"),
10247
+ configPath: join8(sgDir, "cloud-guard.json")
9608
10248
  };
9609
10249
  }
9610
10250
  function readHook(filename) {
9611
- return readFileSync6(join6(HOOKS_DIR, filename), "utf-8");
10251
+ return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
9612
10252
  }
9613
10253
  function readGuard() {
9614
- const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
9615
- return existsSync4(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
10254
+ const bundled = join8(HOOKS_DIR, "guard.bundled.mjs");
10255
+ return existsSync6(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
9616
10256
  }
9617
10257
  function ask(question) {
9618
10258
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -9625,10 +10265,10 @@ function runGlobalRestore() {
9625
10265
  const p = globalPaths();
9626
10266
  unlockProtected();
9627
10267
  removeClaudeShim();
9628
- if (existsSync4(p.backupPath)) {
10268
+ if (existsSync6(p.backupPath)) {
9629
10269
  writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
9630
10270
  console.log(` Restored ${p.settingsPath} from backup.`);
9631
- } else if (existsSync4(p.settingsPath)) {
10271
+ } else if (existsSync6(p.settingsPath)) {
9632
10272
  try {
9633
10273
  const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
9634
10274
  delete s.hooks;
@@ -9666,11 +10306,11 @@ function shimTargets() {
9666
10306
  return [];
9667
10307
  }
9668
10308
  }
9669
- return [".bashrc", ".zshrc", ".profile"].map((f) => join6(homedir4(), f)).filter((f) => existsSync4(f));
10309
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join8(homedir6(), f)).filter((f) => existsSync6(f));
9670
10310
  }
9671
10311
  function writeShimBlock(file, block2) {
9672
10312
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
9673
- let content = existsSync4(file) ? readFileSync6(file, "utf-8") : "";
10313
+ let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
9674
10314
  content = content.replace(re, "");
9675
10315
  if (block2) {
9676
10316
  if (content.length && !content.endsWith("\n")) content += "\n";
@@ -9737,18 +10377,18 @@ async function runGlobalInstall(opts = {}) {
9737
10377
  mkdirSync3(p.hooksDir, { recursive: true });
9738
10378
  mkdirSync3(p.claudeDir, { recursive: true });
9739
10379
  unlockProtected();
9740
- writeFileSync3(join6(p.hooksDir, "guard.mjs"), readGuard());
9741
- writeFileSync3(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
9742
- writeFileSync3(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
9743
- writeFileSync3(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
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"));
9744
10384
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
9745
10385
  removeClaudeShim();
9746
10386
  writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
9747
10387
  console.log(` Wrote ${p.configPath}`);
9748
10388
  let existing = {};
9749
- if (existsSync4(p.settingsPath)) {
10389
+ if (existsSync6(p.settingsPath)) {
9750
10390
  const raw = readFileSync6(p.settingsPath, "utf-8");
9751
- if (!existsSync4(p.backupPath)) {
10391
+ if (!existsSync6(p.backupPath)) {
9752
10392
  writeFileSync3(p.backupPath, raw);
9753
10393
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
9754
10394
  }
@@ -9758,9 +10398,9 @@ async function runGlobalInstall(opts = {}) {
9758
10398
  existing = {};
9759
10399
  }
9760
10400
  }
9761
- const guardAbs = join6(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
9762
- const auditAbs = join6(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
9763
- const stopAbs = join6(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
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, "/");
9764
10404
  const nodeBin = process.execPath.replace(/\\/g, "/");
9765
10405
  const merged = {
9766
10406
  ...existing,
@@ -9950,22 +10590,22 @@ import { createServer, request as httpRequest } from "http";
9950
10590
  import { request as httpsRequest } from "https";
9951
10591
  import { spawn as spawn3 } from "child_process";
9952
10592
  import { URL as URL2 } from "url";
9953
- import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
10593
+ import { readFileSync as readFileSync7, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
9954
10594
  import { resolve as resolve5 } from "path";
9955
- import { homedir as homedir5 } from "os";
10595
+ import { homedir as homedir7 } from "os";
9956
10596
  function findCacheFile() {
9957
- const dir = resolve5(homedir5(), ".solongate");
10597
+ const dir = resolve5(homedir7(), ".solongate");
9958
10598
  const envSel = process.env.SOLONGATE_AGENT_ID;
9959
10599
  if (envSel) {
9960
10600
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
9961
- if (existsSync5(f)) return f;
10601
+ if (existsSync7(f)) return f;
9962
10602
  }
9963
10603
  let best = null, bestTs = -1;
9964
10604
  try {
9965
10605
  for (const name of readdirSync(dir)) {
9966
10606
  if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
9967
10607
  const full = resolve5(dir, name);
9968
- const ts = statSync2(full).mtimeMs;
10608
+ const ts = statSync4(full).mtimeMs;
9969
10609
  if (ts > bestTs) {
9970
10610
  bestTs = ts;
9971
10611
  best = full;
@@ -9979,7 +10619,7 @@ function findCacheFile() {
9979
10619
  function loadCfg() {
9980
10620
  try {
9981
10621
  const f = findCacheFile();
9982
- if (f && existsSync5(f)) {
10622
+ if (f && existsSync7(f)) {
9983
10623
  const c2 = JSON.parse(readFileSync7(f, "utf-8"));
9984
10624
  const d = c2?.security?.dlpRedact;
9985
10625
  const g = c2?.security?.ghost;
@@ -10256,9 +10896,9 @@ __export(logs_server_exports, {
10256
10896
  runLogsServer: () => runLogsServer
10257
10897
  });
10258
10898
  import { createServer as createServer2 } from "http";
10259
- import { readFileSync as readFileSync8, statSync as statSync3 } from "fs";
10260
- import { resolve as resolve6, join as join7, isAbsolute } from "path";
10261
- import { homedir as homedir6 } from "os";
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";
10262
10902
  import { readdirSync as readdirSync2 } from "fs";
10263
10903
  function allowedOrigins() {
10264
10904
  const base = [
@@ -10275,15 +10915,15 @@ function resolveLocalLogDir(rawPath) {
10275
10915
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
10276
10916
  if (!dir) return null;
10277
10917
  if (isAbsolute(dir)) return dir;
10278
- return resolve6(homedir6(), ".solongate", "local-logs");
10918
+ return resolve6(homedir8(), ".solongate", "local-logs");
10279
10919
  }
10280
10920
  async function findLogDir() {
10281
- const base = resolve6(homedir6(), ".solongate");
10921
+ const base = resolve6(homedir8(), ".solongate");
10282
10922
  try {
10283
10923
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
10284
10924
  for (const f of files) {
10285
10925
  try {
10286
- const c2 = JSON.parse(readFileSync8(join7(base, f), "utf-8"));
10926
+ const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
10287
10927
  const p = c2?.security?.localLogs?.path;
10288
10928
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
10289
10929
  } catch {
@@ -10292,7 +10932,7 @@ async function findLogDir() {
10292
10932
  } catch {
10293
10933
  }
10294
10934
  try {
10295
- const cfgRaw = readFileSync8(join7(base, "cloud-guard.json"), "utf-8");
10935
+ const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
10296
10936
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
10297
10937
  if (apiKey) {
10298
10938
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -10321,9 +10961,9 @@ function setCors(req, res) {
10321
10961
  }
10322
10962
  function fileInfo(dir) {
10323
10963
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
10324
- const file = join7(dir, LOG_FILENAME);
10964
+ const file = join9(dir, LOG_FILENAME);
10325
10965
  try {
10326
- const st = statSync3(file);
10966
+ const st = statSync5(file);
10327
10967
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
10328
10968
  } catch {
10329
10969
  return { file, exists: false, size: 0, mtimeMs: 0 };
@@ -10434,7 +11074,7 @@ var init_logs_server = __esm({
10434
11074
 
10435
11075
  // src/inject.ts
10436
11076
  var inject_exports = {};
10437
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync6, copyFileSync } from "fs";
11077
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync } from "fs";
10438
11078
  import { resolve as resolve7 } from "path";
10439
11079
  import { execSync } from "child_process";
10440
11080
  function parseInjectArgs(argv) {
@@ -10492,7 +11132,7 @@ WHAT IT DOES
10492
11132
  `);
10493
11133
  }
10494
11134
  function detectProject() {
10495
- if (!existsSync6(resolve7("package.json"))) return false;
11135
+ if (!existsSync8(resolve7("package.json"))) return false;
10496
11136
  try {
10497
11137
  const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
10498
11138
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -10508,13 +11148,13 @@ function findTsEntryFile() {
10508
11148
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
10509
11149
  if (typeof binPath === "string") {
10510
11150
  const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
10511
- if (existsSync6(resolve7(srcPath))) return resolve7(srcPath);
10512
- if (existsSync6(resolve7(binPath))) return resolve7(binPath);
11151
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
11152
+ if (existsSync8(resolve7(binPath))) return resolve7(binPath);
10513
11153
  }
10514
11154
  }
10515
11155
  if (pkg.main) {
10516
11156
  const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
10517
- if (existsSync6(resolve7(srcPath))) return resolve7(srcPath);
11157
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
10518
11158
  }
10519
11159
  } catch {
10520
11160
  }
@@ -10528,7 +11168,7 @@ function findTsEntryFile() {
10528
11168
  ];
10529
11169
  for (const c2 of candidates) {
10530
11170
  const full = resolve7(c2);
10531
- if (existsSync6(full)) {
11171
+ if (existsSync8(full)) {
10532
11172
  try {
10533
11173
  const content = readFileSync9(full, "utf-8");
10534
11174
  if (content.includes("McpServer") || content.includes("McpServer")) {
@@ -10539,13 +11179,13 @@ function findTsEntryFile() {
10539
11179
  }
10540
11180
  }
10541
11181
  for (const c2 of candidates) {
10542
- if (existsSync6(resolve7(c2))) return resolve7(c2);
11182
+ if (existsSync8(resolve7(c2))) return resolve7(c2);
10543
11183
  }
10544
11184
  return null;
10545
11185
  }
10546
11186
  function detectPackageManager() {
10547
- if (existsSync6(resolve7("pnpm-lock.yaml"))) return "pnpm";
10548
- if (existsSync6(resolve7("yarn.lock"))) return "yarn";
11187
+ if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
11188
+ if (existsSync8(resolve7("yarn.lock"))) return "yarn";
10549
11189
  return "npm";
10550
11190
  }
10551
11191
  function installSdk() {
@@ -10688,7 +11328,7 @@ async function main2() {
10688
11328
  }
10689
11329
  log3(" Language: TypeScript");
10690
11330
  const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
10691
- if (!entryFile || !existsSync6(entryFile)) {
11331
+ if (!entryFile || !existsSync8(entryFile)) {
10692
11332
  log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
10693
11333
  log3("");
10694
11334
  log3(" Specify it manually: --file <path>");
@@ -10701,7 +11341,7 @@ async function main2() {
10701
11341
  log3("");
10702
11342
  const backupPath = entryFile + ".solongate-backup";
10703
11343
  if (opts.restore) {
10704
- if (!existsSync6(backupPath)) {
11344
+ if (!existsSync8(backupPath)) {
10705
11345
  log3(" No backup found. Nothing to restore.");
10706
11346
  process.exit(1);
10707
11347
  }
@@ -10737,7 +11377,7 @@ async function main2() {
10737
11377
  log3(" To apply: npx @solongate/proxy inject");
10738
11378
  process.exit(0);
10739
11379
  }
10740
- if (!existsSync6(backupPath)) {
11380
+ if (!existsSync8(backupPath)) {
10741
11381
  copyFileSync(entryFile, backupPath);
10742
11382
  log3("");
10743
11383
  log3(` Backup: ${backupPath}`);
@@ -10771,8 +11411,8 @@ var init_inject = __esm({
10771
11411
 
10772
11412
  // src/create.ts
10773
11413
  var create_exports = {};
10774
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
10775
- import { resolve as resolve8, join as join8 } from "path";
11414
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
11415
+ import { resolve as resolve8, join as join10 } from "path";
10776
11416
  import { execSync as execSync2 } from "child_process";
10777
11417
  function withSpinner(message, fn) {
10778
11418
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -10852,7 +11492,7 @@ EXAMPLES
10852
11492
  }
10853
11493
  function createProject(dir, name, _policy) {
10854
11494
  writeFileSync5(
10855
- join8(dir, "package.json"),
11495
+ join10(dir, "package.json"),
10856
11496
  JSON.stringify(
10857
11497
  {
10858
11498
  name,
@@ -10882,7 +11522,7 @@ function createProject(dir, name, _policy) {
10882
11522
  ) + "\n"
10883
11523
  );
10884
11524
  writeFileSync5(
10885
- join8(dir, "tsconfig.json"),
11525
+ join10(dir, "tsconfig.json"),
10886
11526
  JSON.stringify(
10887
11527
  {
10888
11528
  compilerOptions: {
@@ -10902,9 +11542,9 @@ function createProject(dir, name, _policy) {
10902
11542
  2
10903
11543
  ) + "\n"
10904
11544
  );
10905
- mkdirSync4(join8(dir, "src"), { recursive: true });
11545
+ mkdirSync4(join10(dir, "src"), { recursive: true });
10906
11546
  writeFileSync5(
10907
- join8(dir, "src", "index.ts"),
11547
+ join10(dir, "src", "index.ts"),
10908
11548
  `#!/usr/bin/env node
10909
11549
 
10910
11550
  console.log = (...args: unknown[]) => {
@@ -10946,7 +11586,7 @@ console.log('Press Ctrl+C to stop.');
10946
11586
  `
10947
11587
  );
10948
11588
  writeFileSync5(
10949
- join8(dir, ".mcp.json"),
11589
+ join10(dir, ".mcp.json"),
10950
11590
  JSON.stringify(
10951
11591
  {
10952
11592
  mcpServers: {
@@ -10964,12 +11604,12 @@ console.log('Press Ctrl+C to stop.');
10964
11604
  ) + "\n"
10965
11605
  );
10966
11606
  writeFileSync5(
10967
- join8(dir, ".env"),
11607
+ join10(dir, ".env"),
10968
11608
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
10969
11609
  `
10970
11610
  );
10971
11611
  writeFileSync5(
10972
- join8(dir, ".gitignore"),
11612
+ join10(dir, ".gitignore"),
10973
11613
  `node_modules/
10974
11614
  dist/
10975
11615
  *.solongate-backup
@@ -10983,7 +11623,7 @@ async function main3() {
10983
11623
  const opts = parseCreateArgs(process.argv);
10984
11624
  const dir = resolve8(opts.name);
10985
11625
  printBanner("Create MCP Server");
10986
- if (existsSync7(dir)) {
11626
+ if (existsSync9(dir)) {
10987
11627
  log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
10988
11628
  process.exit(1);
10989
11629
  }
@@ -11062,12 +11702,12 @@ var init_create = __esm({
11062
11702
 
11063
11703
  // src/pull-push.ts
11064
11704
  var pull_push_exports = {};
11065
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "fs";
11705
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
11066
11706
  import { resolve as resolve9 } from "path";
11067
11707
  function loadEnv() {
11068
11708
  if (process.env.SOLONGATE_API_KEY) return;
11069
11709
  const envPath = resolve9(".env");
11070
- if (!existsSync8(envPath)) return;
11710
+ if (!existsSync10(envPath)) return;
11071
11711
  try {
11072
11712
  const content = readFileSync10(envPath, "utf-8");
11073
11713
  for (const line of content.split("\n")) {
@@ -11134,7 +11774,7 @@ async function listPolicies(apiKey) {
11134
11774
  const data = await res.json();
11135
11775
  return data.policies ?? [];
11136
11776
  }
11137
- async function list3(apiKey, policyId) {
11777
+ async function list5(apiKey, policyId) {
11138
11778
  const policies = await listPolicies(apiKey);
11139
11779
  if (policies.length === 0) {
11140
11780
  log5(yellow2("No policies found. Create one in the dashboard first."));
@@ -11269,7 +11909,7 @@ async function pull(apiKey, file, policyId) {
11269
11909
  log5("");
11270
11910
  }
11271
11911
  async function push(apiKey, file, policyId) {
11272
- if (!existsSync8(file)) {
11912
+ if (!existsSync10(file)) {
11273
11913
  log5(red2(`ERROR: File not found: ${file}`));
11274
11914
  process.exit(1);
11275
11915
  }
@@ -11337,7 +11977,7 @@ async function main4() {
11337
11977
  } else if (command === "push") {
11338
11978
  await push(apiKey, file, policyId);
11339
11979
  } else if (command === "list" || command === "ls") {
11340
- await list3(apiKey, policyId);
11980
+ await list5(apiKey, policyId);
11341
11981
  } else {
11342
11982
  log5(red2(`Unknown command: ${command}`));
11343
11983
  log5("");
@@ -14402,7 +15042,7 @@ ${msg.content.text}`;
14402
15042
 
14403
15043
  // src/index.ts
14404
15044
  init_cli_utils();
14405
- var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "ui", "dashboard", "tui"]);
15045
+ var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks", "ui", "dashboard", "tui"]);
14406
15046
  var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
14407
15047
  if (!IS_HUMAN_CLI) {
14408
15048
  console.log = (...args) => {
@@ -14451,7 +15091,7 @@ async function main5() {
14451
15091
  await launchTui2();
14452
15092
  return;
14453
15093
  }
14454
- const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"]);
15094
+ const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"]);
14455
15095
  if (MGMT_COMMANDS.has(subcommand ?? "")) {
14456
15096
  const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
14457
15097
  const code = await runCommand2(subcommand, process.argv.slice(3));