@solongate/proxy 0.77.2 → 0.79.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,9 +6867,15 @@ 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,
6878
+ getWebhooks: () => getWebhooks,
6873
6879
  setSecurityLayers: () => setSecurityLayers
6874
6880
  });
6875
6881
  function getSecurityLayers() {
@@ -6887,6 +6893,24 @@ function clearRateLimitHistory() {
6887
6893
  function getGuardStatus() {
6888
6894
  return request("GET", "/settings/guard-status");
6889
6895
  }
6896
+ function getAlerts() {
6897
+ return request("GET", "/settings/denial-alerts");
6898
+ }
6899
+ function createAlert(body) {
6900
+ return request("POST", "/settings/denial-alerts", { body });
6901
+ }
6902
+ function deleteAlert(id) {
6903
+ return request("DELETE", "/settings/denial-alerts", { query: { id } });
6904
+ }
6905
+ function getWebhooks() {
6906
+ return request("GET", "/settings/denial-webhook");
6907
+ }
6908
+ function createWebhook(body) {
6909
+ return request("POST", "/settings/denial-webhook", { body });
6910
+ }
6911
+ function deleteWebhook(id) {
6912
+ return request("DELETE", "/settings/denial-webhook", { query: { id } });
6913
+ }
6890
6914
  var init_settings = __esm({
6891
6915
  "src/api-client/settings.ts"() {
6892
6916
  "use strict";
@@ -6924,6 +6948,7 @@ var init_stats = __esm({
6924
6948
  // src/api-client/audit.ts
6925
6949
  var audit_exports = {};
6926
6950
  __export(audit_exports, {
6951
+ block: () => block,
6927
6952
  list: () => list2,
6928
6953
  whitelist: () => whitelist
6929
6954
  });
@@ -6933,6 +6958,9 @@ function list2(query = {}) {
6933
6958
  function whitelist(id, scope = "exact") {
6934
6959
  return request("POST", `/audit-logs/${encodeURIComponent(id)}/whitelist`, { body: { scope } });
6935
6960
  }
6961
+ function block(id, scope = "exact") {
6962
+ return request("POST", `/audit-logs/${encodeURIComponent(id)}/block`, { body: { scope } });
6963
+ }
6936
6964
  var init_audit = __esm({
6937
6965
  "src/api-client/audit.ts"() {
6938
6966
  "use strict";
@@ -6967,6 +6995,44 @@ var init_agents = __esm({
6967
6995
  }
6968
6996
  });
6969
6997
 
6998
+ // src/api-client/keys.ts
6999
+ var keys_exports = {};
7000
+ __export(keys_exports, {
7001
+ create: () => create2,
7002
+ list: () => list3,
7003
+ revoke: () => revoke
7004
+ });
7005
+ function list3() {
7006
+ return request("GET", "/keys");
7007
+ }
7008
+ function create2(name, isLive = true) {
7009
+ return request("POST", "/keys", { body: { name, is_live: isLive } });
7010
+ }
7011
+ function revoke(id) {
7012
+ return request("DELETE", `/keys/${encodeURIComponent(id)}`);
7013
+ }
7014
+ var init_keys = __esm({
7015
+ "src/api-client/keys.ts"() {
7016
+ "use strict";
7017
+ init_client();
7018
+ }
7019
+ });
7020
+
7021
+ // src/api-client/mcp.ts
7022
+ var mcp_exports = {};
7023
+ __export(mcp_exports, {
7024
+ list: () => list4
7025
+ });
7026
+ function list4() {
7027
+ return request("GET", "/mcp-servers");
7028
+ }
7029
+ var init_mcp = __esm({
7030
+ "src/api-client/mcp.ts"() {
7031
+ "use strict";
7032
+ init_client();
7033
+ }
7034
+ });
7035
+
6970
7036
  // src/api-client/index.ts
6971
7037
  var api;
6972
7038
  var init_api_client = __esm({
@@ -6979,7 +7045,9 @@ var init_api_client = __esm({
6979
7045
  init_stats();
6980
7046
  init_audit();
6981
7047
  init_agents();
6982
- api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports };
7048
+ init_keys();
7049
+ init_mcp();
7050
+ api = { policies: policies_exports, settings: settings_exports, stats: stats_exports, audit: audit_exports, agents: agents_exports, keys: keys_exports, mcp: mcp_exports };
6983
7051
  }
6984
7052
  });
6985
7053
 
@@ -7052,6 +7120,19 @@ function tailLines(file, maxBytes = 131072) {
7052
7120
  return [];
7053
7121
  }
7054
7122
  }
7123
+ function extractTarget(detail) {
7124
+ try {
7125
+ const j = JSON.parse(detail);
7126
+ const cmd = String(j.command ?? j.cmd ?? "").trim();
7127
+ if (cmd) return { kind: "command", value: cmd };
7128
+ const fp = String(j.file_path ?? j.path ?? "").trim();
7129
+ if (fp) return { kind: "path", value: fp.split(/[\\/]/).pop() || fp };
7130
+ const url = String(j.url ?? "").trim();
7131
+ if (url) return { kind: "url", value: url };
7132
+ } catch {
7133
+ }
7134
+ return null;
7135
+ }
7055
7136
  function cloudItem(e) {
7056
7137
  return {
7057
7138
  id: "c:" + e.id,
@@ -7061,6 +7142,7 @@ function cloudItem(e) {
7061
7142
  permission: (e.permission ?? "").slice(0, 4),
7062
7143
  detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
7063
7144
  dlp: !!e.dlp_matches?.length,
7145
+ burst: !!e.rate_limit_burst,
7064
7146
  source: "cloud",
7065
7147
  session: e.session_id ?? void 0,
7066
7148
  agent: e.agent_name ?? void 0,
@@ -7116,8 +7198,9 @@ function PaneTitle({ label, extra, width: width2 }) {
7116
7198
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: tail + "\u2500".repeat(Math.max(0, width2 - used)) })
7117
7199
  ] });
7118
7200
  }
7119
- function StreamLine({ e, loc }) {
7120
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7201
+ function StreamLine({ e, loc, selected }) {
7202
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: selected ? "#1c2f63" : void 0, children: [
7203
+ /* @__PURE__ */ jsx2(Text2, { color: selected ? theme.accentBright : theme.dim, children: selected ? "\u25B8" : " " }),
7121
7204
  /* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
7122
7205
  "[",
7123
7206
  hhmmss(e.at),
@@ -7144,9 +7227,14 @@ function LivePanel({ active: active2 }) {
7144
7227
  const [ring, setRing] = useState2(null);
7145
7228
  const [log6, setLog] = useState2([]);
7146
7229
  const [filter, setFilter] = useState2("all");
7230
+ const [signal, setSignal] = useState2("none");
7147
7231
  const [search, setSearch] = useState2("");
7148
7232
  const [editingSearch, setEditingSearch] = useState2(false);
7149
- const [scroll, setScroll] = useState2(0);
7233
+ const [sel, setSel] = useState2(0);
7234
+ const [actionMsg, setActionMsg] = useState2(null);
7235
+ const notifiedRef = useRef2(/* @__PURE__ */ new Set());
7236
+ const notifyReady = useRef2(false);
7237
+ const activePolicyRef = useRef2(null);
7150
7238
  const [mode, setMode] = useState2("stream");
7151
7239
  const [pickIdx, setPickIdx] = useState2(0);
7152
7240
  const [detail, setDetail] = useState2(null);
@@ -7156,6 +7244,7 @@ function LivePanel({ active: active2 }) {
7156
7244
  const lastLocalTs = useRef2(0);
7157
7245
  const pausedUntil = useRef2(0);
7158
7246
  const sessRef = useRef2([]);
7247
+ const mergedRef = useRef2([]);
7159
7248
  const prevStatus = useRef2(/* @__PURE__ */ new Map());
7160
7249
  const [alerts, setAlerts] = useState2([]);
7161
7250
  const [frozen, setFrozen] = useState2(false);
@@ -7204,6 +7293,7 @@ function LivePanel({ active: active2 }) {
7204
7293
  permission: (j.permission ?? "").slice(0, 4),
7205
7294
  detail: (j.arguments ? JSON.stringify(j.arguments) : j.reason ?? "").replace(/\s+/g, " "),
7206
7295
  dlp: !!j.dlp,
7296
+ burst: !!j.rate_limit_burst,
7207
7297
  source: "local",
7208
7298
  session: j.session_id,
7209
7299
  agent: j.agent_name,
@@ -7318,30 +7408,52 @@ function LivePanel({ active: active2 }) {
7318
7408
  return () => clearInterval(t);
7319
7409
  }, [active2, frozen]);
7320
7410
  const startRef = useRef2(Date.now());
7411
+ const fireAlert = useCallback2(
7412
+ (id, title, msg, level = "warn") => {
7413
+ pushLog(`${level === "bad" ? "\u26D4" : "\u23F8"} ${msg}`, level);
7414
+ setAlerts((a) => [...a.filter((x) => x.id !== id), { id, msg, level, until: Date.now() + 15e3 }].slice(-4));
7415
+ try {
7416
+ process.stdout.write("\x07");
7417
+ } catch {
7418
+ }
7419
+ try {
7420
+ const child = spawn("notify-send", ["-a", "SolonGate", title, msg], { stdio: "ignore", detached: true });
7421
+ child.on("error", () => {
7422
+ });
7423
+ child.unref();
7424
+ } catch {
7425
+ }
7426
+ },
7427
+ [pushLog]
7428
+ );
7321
7429
  useEffect2(() => {
7322
7430
  if (!active2) return;
7323
7431
  for (const r of sessRef.current) {
7324
7432
  const prev = prevStatus.current.get(r.id);
7325
7433
  if (prev && prev === "active" && r.status === "idle") {
7326
7434
  const name = r.isMe ? `${r.agent} (this machine)` : r.agent;
7327
- const msg = `${name} went IDLE, no tool call for 60s`;
7328
- pushLog(`\u23F8 ${msg}`, "warn");
7329
- setAlerts((a) => [...a.filter((x) => x.id !== r.id), { id: r.id, msg, until: Date.now() + 15e3 }].slice(-4));
7330
- try {
7331
- process.stdout.write("\x07");
7332
- } catch {
7333
- }
7334
- try {
7335
- const child = spawn("notify-send", ["-a", "SolonGate", "Agent idle", msg], { stdio: "ignore", detached: true });
7336
- child.on("error", () => {
7337
- });
7338
- child.unref();
7339
- } catch {
7340
- }
7435
+ fireAlert("idle:" + r.id, "Agent idle", `${name} went IDLE, no tool call for 60s`, "warn");
7341
7436
  }
7342
7437
  prevStatus.current.set(r.id, r.status);
7343
7438
  }
7344
- }, [tick, active2, pushLog]);
7439
+ }, [tick, active2, fireAlert]);
7440
+ useEffect2(() => {
7441
+ if (!active2) return;
7442
+ const notable = mergedRef.current.filter((e) => e.decision !== "ALLOW" || e.dlp || e.burst);
7443
+ if (!notifyReady.current) {
7444
+ for (const e of notable) notifiedRef.current.add(e.id);
7445
+ notifyReady.current = true;
7446
+ return;
7447
+ }
7448
+ for (const e of notable) {
7449
+ if (notifiedRef.current.has(e.id)) continue;
7450
+ notifiedRef.current.add(e.id);
7451
+ const who = e.agent ? ` \xB7 ${truncate2(e.agent, 20)}` : "";
7452
+ if (e.dlp) fireAlert("sec:" + e.id, "DLP hit", `SECRET in ${e.tool}${who}: ${truncate2(e.detail, 60)}`, "bad");
7453
+ else if (e.decision !== "ALLOW") fireAlert("sec:" + e.id, "Call denied", `DENY ${e.tool}${who}: ${truncate2(e.rule ?? e.detail, 60)}`, "bad");
7454
+ else if (e.burst) fireAlert("sec:" + e.id, "Rate-limit burst", `BURST ${e.tool}${who}`, "warn");
7455
+ }
7456
+ }, [tick, active2, fireAlert]);
7345
7457
  const cols = process.stdout.columns ?? 100;
7346
7458
  const rows = process.stdout.rows ?? 30;
7347
7459
  const ins = insights.data ?? {};
@@ -7354,6 +7466,7 @@ function LivePanel({ active: active2 }) {
7354
7466
  }
7355
7467
  const cloudDeduped = cloudBuf.filter((e) => !localKeys.has(`${e.tool}|${e.decision}|${Math.round(e.at / 6e4)}`));
7356
7468
  const mergedAll = [...cloudDeduped, ...localBuf].sort((a, b) => a.at - b.at);
7469
+ mergedRef.current = mergedAll;
7357
7470
  const nowSlot = Math.floor(nowMs / 1e4);
7358
7471
  const traffic = new Array(60).fill(0);
7359
7472
  const trafficHot = new Array(60).fill(false);
@@ -7413,7 +7526,8 @@ function LivePanel({ active: active2 }) {
7413
7526
  const isLoc = (e) => e.source === "local" || !!e.session && localSessIds.has(e.session);
7414
7527
  const q = search.trim().toLowerCase();
7415
7528
  const matches = (e) => !q || `${e.tool} ${e.agent ?? ""} ${e.decision} ${e.permission} ${e.rule ?? ""} ${e.detail}`.toLowerCase().includes(q);
7416
- const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches);
7529
+ const signalOk = (e) => signal === "none" ? true : signal === "deny" ? e.decision !== "ALLOW" : signal === "dlp" ? e.dlp : e.burst;
7530
+ const filtered = (filter === "all" ? mergedAll : mergedAll.filter((e) => filter === "local" ? isLoc(e) : !isLoc(e))).filter(matches).filter(signalOk);
7417
7531
  const visibleDesc = filtered.slice().reverse();
7418
7532
  const chartH = rows >= 36 ? 6 : 4;
7419
7533
  const colH = rows >= 32 ? 7 : 5;
@@ -7424,9 +7538,11 @@ function LivePanel({ active: active2 }) {
7424
7538
  const colW = Math.max(20, Math.floor((innerW - 4) / 3));
7425
7539
  const pickable = combinedSess.slice(0, colH - 1);
7426
7540
  const streamBodyRows = Math.max(3, streamRows - 1);
7541
+ const selClamped = Math.min(Math.max(0, sel), Math.max(0, visibleDesc.length - 1));
7427
7542
  const maxScroll = Math.max(0, visibleDesc.length - streamBodyRows);
7428
- const clampedScroll = Math.min(scroll, maxScroll);
7543
+ const clampedScroll = Math.min(Math.max(0, selClamped - Math.floor((streamBodyRows - 1) / 2)), maxScroll);
7429
7544
  const windowed = visibleDesc.slice(clampedScroll, clampedScroll + streamBodyRows);
7545
+ const selEntry = visibleDesc[selClamped];
7430
7546
  const detailEntries = detail ? (() => {
7431
7547
  const seen = /* @__PURE__ */ new Set();
7432
7548
  const all = [];
@@ -7438,6 +7554,42 @@ function LivePanel({ active: active2 }) {
7438
7554
  }
7439
7555
  return all.sort((a, b) => b.at - a.at);
7440
7556
  })() : [];
7557
+ const doAction = async (act) => {
7558
+ const e = selEntry;
7559
+ if (!e) return;
7560
+ const ok = act === "whitelist" ? e.decision !== "ALLOW" : e.decision === "ALLOW";
7561
+ if (!ok) {
7562
+ setActionMsg({ text: act === "whitelist" ? "select a DENY to whitelist (w)" : "select an ALLOW to block (b)", level: "bad", until: Date.now() + 4e3 });
7563
+ return;
7564
+ }
7565
+ setActionMsg({ text: act === "whitelist" ? "whitelisting\u2026" : "blocking\u2026", level: "ok", until: Date.now() + 4e3 });
7566
+ try {
7567
+ let res;
7568
+ if (e.source === "cloud") {
7569
+ const realId = e.id.slice(2);
7570
+ res = act === "whitelist" ? await api.audit.whitelist(realId) : await api.audit.block(realId);
7571
+ } else {
7572
+ let pid = activePolicyRef.current;
7573
+ if (!pid) {
7574
+ const a = await api.policies.active();
7575
+ pid = a.policy?.id ?? null;
7576
+ activePolicyRef.current = pid;
7577
+ }
7578
+ if (!pid) throw new Error("no active policy resolves for this machine");
7579
+ const t = extractTarget(e.detail);
7580
+ res = await api.policies.addRule(pid, { toolPattern: e.tool, kind: t?.kind ?? "tool", value: t?.value, effect: act === "whitelist" ? "ALLOW" : "DENY" });
7581
+ }
7582
+ const verb = act === "whitelist" ? "ALLOW" : "DENY";
7583
+ setActionMsg({
7584
+ text: res.deduped ? `${verb} rule already present` : `${verb} rule added \u2192 ${res.policy_id ?? "?"}${res.policy_version ? " v" + res.policy_version : ""} \xB7 reaches agents in ~30s`,
7585
+ level: "ok",
7586
+ until: Date.now() + 7e3
7587
+ });
7588
+ pushLog(`${act === "whitelist" ? "\u2713 whitelisted" : "\u26D4 blocked"} ${e.tool}`, act === "whitelist" ? "ok" : "warn");
7589
+ } catch (err2) {
7590
+ setActionMsg({ text: "\u2717 " + (err2 instanceof Error ? err2.message : String(err2)), level: "bad", until: Date.now() + 7e3 });
7591
+ }
7592
+ };
7441
7593
  useInput(
7442
7594
  (input, key) => {
7443
7595
  if (input === " ") {
@@ -7472,16 +7624,25 @@ function LivePanel({ active: active2 }) {
7472
7624
  } else if (input === "s" || key.leftArrow) setMode("stream");
7473
7625
  return;
7474
7626
  }
7627
+ const toggleSignal = (s2) => {
7628
+ setSignal((cur) => cur === s2 ? "none" : s2);
7629
+ setSel(0);
7630
+ };
7475
7631
  if (input === "f") {
7476
7632
  setFilter((f) => FILTERS[(FILTERS.indexOf(f) + 1) % FILTERS.length]);
7477
- setScroll(0);
7633
+ setSel(0);
7478
7634
  } else if (input === "s") {
7479
7635
  setPickIdx(0);
7480
7636
  setMode("pick");
7481
- } else if (key.downArrow) setScroll((n) => Math.min(maxScroll, n + 1));
7482
- else if (key.upArrow) setScroll((n) => Math.max(0, n - 1));
7483
- else if (key.pageDown) setScroll((n) => Math.min(maxScroll, n + streamRows));
7484
- else if (key.pageUp) setScroll((n) => Math.max(0, n - streamRows));
7637
+ } else if (input === "w") void doAction("whitelist");
7638
+ else if (input === "b") void doAction("block");
7639
+ else if (input === "d") toggleSignal("deny");
7640
+ else if (input === "x") toggleSignal("dlp");
7641
+ else if (input === "r") toggleSignal("ratelimit");
7642
+ else if (key.downArrow) setSel((n) => Math.min(visibleDesc.length - 1, n + 1));
7643
+ else if (key.upArrow) setSel((n) => Math.max(0, n - 1));
7644
+ else if (key.pageDown) setSel((n) => Math.min(visibleDesc.length - 1, n + streamBodyRows));
7645
+ else if (key.pageUp) setSel((n) => Math.max(0, n - streamBodyRows));
7485
7646
  },
7486
7647
  { isActive: active2 && !editingSearch }
7487
7648
  );
@@ -7498,7 +7659,7 @@ function LivePanel({ active: active2 }) {
7498
7659
  return;
7499
7660
  }
7500
7661
  setSearch(v);
7501
- setScroll(0);
7662
+ setSel(0);
7502
7663
  setDetailScroll(0);
7503
7664
  },
7504
7665
  onSubmit: () => setEditingSearch(false)
@@ -7618,7 +7779,18 @@ function LivePanel({ active: active2 }) {
7618
7779
  /* @__PURE__ */ jsx2(Text2, { color: guard.data?.up_to_date ? theme.ok : theme.warn, children: guard.data ? `v${guard.data.installed ?? "?"}${guard.data.up_to_date ? "" : "\u2192v" + guard.data.latest} ${guard.data.device_count}dev` : "\xB7" }),
7619
7780
  /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: " \u2502" })
7620
7781
  ] }),
7621
- alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: "#3d2a12", color: "#ffb454", bold: true, children: ` \u23F8 IDLE \xB7 ${truncate2(a.msg, innerW - 12)} ` }, a.id)),
7782
+ alerts.filter((a) => nowMs < a.until).slice(-1).map((a) => /* @__PURE__ */ jsx2(
7783
+ Text2,
7784
+ {
7785
+ wrap: "truncate",
7786
+ backgroundColor: a.level === "bad" ? "#3d1220" : "#3d2a12",
7787
+ color: a.level === "bad" ? "#ff6b6b" : "#ffb454",
7788
+ bold: true,
7789
+ children: ` ${a.level === "bad" ? "\u26D4 ALERT" : "\u23F8 IDLE"} \xB7 ${truncate2(a.msg, innerW - 14)} `
7790
+ },
7791
+ a.id
7792
+ )),
7793
+ actionMsg && nowMs < actionMsg.until ? /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", backgroundColor: actionMsg.level === "bad" ? "#3d1220" : "#123d1f", color: actionMsg.level === "bad" ? "#ff6b6b" : "#7bd88f", bold: true, children: ` ${truncate2(actionMsg.text, innerW - 4)} ` }) : null,
7622
7794
  /* @__PURE__ */ jsxs2(Box2, { height: 1 + chartH, children: [
7623
7795
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: leftW, marginRight: 2, children: [
7624
7796
  /* @__PURE__ */ jsx2(PaneTitle, { label: "TRAFFIC", extra: `${trafficLabel} \xB7 peak ${Math.max(0, ...traffic)} \xB7 red = denials`, width: leftW }),
@@ -7656,11 +7828,11 @@ function LivePanel({ active: active2 }) {
7656
7828
  /* @__PURE__ */ jsx2(PaneTitle, { label: "SESSIONS", extra: mode === "pick" ? "\u2191\u2193 pick \xB7 enter open" : `${combinedSess.length} \xB7 s = inspect`, width: colW }),
7657
7829
  pickable.length === 0 ? /* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "scanning\u2026" }) : null,
7658
7830
  pickable.map((r, i) => {
7659
- const sel = mode === "pick" && i === pickIdx;
7831
+ const sel2 = mode === "pick" && i === pickIdx;
7660
7832
  const st = STATUS_STYLE[r.status];
7661
- return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel ? "#1c2f63" : void 0, bold: sel || r.isMe, children: [
7662
- /* @__PURE__ */ jsxs2(Text2, { color: sel ? theme.accentBright : st.color, children: [
7663
- sel ? "\u25B8" : st.dot,
7833
+ return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", backgroundColor: sel2 ? "#1c2f63" : void 0, bold: sel2 || r.isMe, children: [
7834
+ /* @__PURE__ */ jsxs2(Text2, { color: sel2 ? theme.accentBright : st.color, children: [
7835
+ sel2 ? "\u25B8" : st.dot,
7664
7836
  " "
7665
7837
  ] }),
7666
7838
  /* @__PURE__ */ jsx2(Text2, { color: st.color, children: st.label.padEnd(7) }),
@@ -7693,7 +7865,7 @@ function LivePanel({ active: active2 }) {
7693
7865
  PaneTitle,
7694
7866
  {
7695
7867
  label: "TOOL STREAM",
7696
- extra: `${visibleDesc.length} entries${q ? " matched" : ""} \xB7 newest first \xB7 filter ${filter}${clampedScroll > 0 ? ` \xB7 \u25BC${clampedScroll} older` : ""} \xB7 f \xB7 / \xB7 \u2191\u2193 \xB7 s`,
7868
+ extra: `${selClamped + 1}/${visibleDesc.length}${signal !== "none" ? ` \xB7 ${signal}` : ""}${q ? " \xB7 search" : ""} \xB7 \u2191\u2193 select \xB7 w allow \xB7 b block \xB7 d/x/r ${filter} \xB7 s`,
7697
7869
  width: innerW
7698
7870
  }
7699
7871
  ),
@@ -7707,12 +7879,12 @@ function LivePanel({ active: active2 }) {
7707
7879
  spin,
7708
7880
  " awaiting traffic\u2026"
7709
7881
  ] }) : null,
7710
- windowed.map((e) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e) }, e.id))
7882
+ windowed.map((e, i) => /* @__PURE__ */ jsx2(StreamLine, { e, loc: isLoc(e), selected: clampedScroll + i === selClamped }, e.id))
7711
7883
  ] }),
7712
7884
  /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
7713
7885
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", bold: true, children: " LIVE " }),
7714
7886
  /* @__PURE__ */ jsx2(Text2, { backgroundColor: "#0b1530", color: "#4f6db8", children: ` local ${localOn ? "on" : "off"} \xB7 ${localBuf.length} loc/${cloudBuf.length} cld \xB7 top ${topTools.map(([t, c2]) => `${t}\xD7${c2}`).join(" ") || "\u2014"} ` }),
7715
- /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " space copy-mode \xB7 / search \xB7 f filter \xB7 s sessions \xB7 esc menu \xB7 q quit " })
7887
+ /* @__PURE__ */ jsx2(Text2, { backgroundColor: BG, color: "#82AAF0", children: " \u2191\u2193 select \xB7 w allow \xB7 b block \xB7 d/x/r \xB7 / search \xB7 f \xB7 s \xB7 space copy \xB7 esc \xB7 q " })
7716
7888
  ] })
7717
7889
  ] });
7718
7890
  }
@@ -7762,8 +7934,8 @@ function ruleSummary(r) {
7762
7934
  return bits.join(" ");
7763
7935
  }
7764
7936
  function PoliciesPanel({ focused }) {
7765
- const list4 = useLoader(() => api.policies.list());
7766
- const policies = list4.data?.policies ?? [];
7937
+ const list6 = useLoader(() => api.policies.list());
7938
+ const policies = list6.data?.policies ?? [];
7767
7939
  const [view, setView] = useState3("list");
7768
7940
  const [pi, setPi] = useState3(0);
7769
7941
  const [ri, setRi] = useState3(0);
@@ -7805,7 +7977,7 @@ function PoliciesPanel({ focused }) {
7805
7977
  setRules(res.rules);
7806
7978
  setDirty(false);
7807
7979
  setStatus("\u2713 Saved v" + res._version);
7808
- list4.reload();
7980
+ list6.reload();
7809
7981
  } catch (e) {
7810
7982
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
7811
7983
  }
@@ -7875,7 +8047,7 @@ function PoliciesPanel({ focused }) {
7875
8047
  { isActive: focused && !editing }
7876
8048
  );
7877
8049
  if (view === "list") {
7878
- 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: [
8050
+ 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: [
7879
8051
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
7880
8052
  /* @__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: [
7881
8053
  (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
@@ -9226,6 +9398,16 @@ async function run5(argv) {
9226
9398
  else err(green(` \u2713 Whitelisted (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
9227
9399
  return 0;
9228
9400
  }
9401
+ if (positionals[0] === "block") {
9402
+ const id = positionals[1];
9403
+ if (!id) return err(" Usage: audit block <logId> [--scope exact|tool]"), 1;
9404
+ const scope = flagStr(flags, "scope") ?? "exact";
9405
+ const res2 = await api.audit.block(id, scope);
9406
+ if (json) return printJson(res2), 0;
9407
+ if (res2.deduped) err(green(" \u2713 ") + dim("Equivalent DENY already present."));
9408
+ else err(green(` \u2713 Blocked (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
9409
+ return 0;
9410
+ }
9229
9411
  const query = {
9230
9412
  filter: flagStr(flags, "filter"),
9231
9413
  tool: flagStr(flags, "tool"),
@@ -9333,6 +9515,395 @@ var init_agents2 = __esm({
9333
9515
  }
9334
9516
  });
9335
9517
 
9518
+ // src/commands/doctor.ts
9519
+ import { existsSync as existsSync4, statSync as statSync2 } from "fs";
9520
+ import { homedir as homedir4 } from "os";
9521
+ import { join as join6 } from "path";
9522
+ async function run6(argv) {
9523
+ const { flags } = parse(argv);
9524
+ const json = flagBool(flags, "json");
9525
+ const checks = [];
9526
+ if (!isAuthenticated()) {
9527
+ checks.push({ name: "login", ok: false, detail: "not logged in \u2014 run `solongate login`" });
9528
+ } else {
9529
+ const { apiUrl } = resolveCredentials();
9530
+ checks.push({ name: "login", ok: true, detail: `paired \xB7 ${apiUrl}` });
9531
+ try {
9532
+ const active2 = await api.policies.active();
9533
+ if (active2.policy) {
9534
+ 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}` });
9535
+ } else {
9536
+ checks.push({ name: "active policy", ok: "warn", detail: "no policy resolves \u2014 every call falls back to default" });
9537
+ }
9538
+ const sec = active2.security;
9539
+ checks.push({ name: "rate limit", ok: sec?.rateLimit ? true : "warn", detail: sec?.rateLimit ? `${sec.rateLimit.perMinute}/min` : "off" });
9540
+ checks.push({ name: "dlp", ok: sec?.dlpBlock ? true : "warn", detail: sec?.dlpBlock ? `block \xB7 ${sec.dlpBlock.patterns.length} patterns` : sec?.dlpRedact ? "redact" : "off" });
9541
+ checks.push({ name: "self-protection", ok: active2.self_protection_enabled ? true : "warn", detail: active2.self_protection_enabled ? "on" : "off" });
9542
+ } catch (e) {
9543
+ checks.push({ name: "api", ok: false, detail: "unreachable: " + (e instanceof Error ? e.message : String(e)) });
9544
+ }
9545
+ try {
9546
+ const g = await api.settings.getGuardStatus();
9547
+ 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\`` });
9548
+ } catch {
9549
+ }
9550
+ }
9551
+ if (existsSync4(LOCAL_LOG2)) {
9552
+ const st = statSync2(LOCAL_LOG2);
9553
+ const ageMin = (Date.now() - st.mtimeMs) / 6e4;
9554
+ 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"}` });
9555
+ } else {
9556
+ checks.push({ name: "local logs", ok: "warn", detail: "off (logs go to cloud) \u2014 enable in dashboard \u2192 Settings" });
9557
+ }
9558
+ if (json) return printJson(checks), checks.some((c2) => c2.ok === false) ? 1 : 0;
9559
+ err("");
9560
+ err(` ${bold("SolonGate doctor")}`);
9561
+ err("");
9562
+ for (const c2 of checks) {
9563
+ const mark = c2.ok === true ? green("\u2713") : c2.ok === "warn" ? yellow("!") : red("\u2717");
9564
+ err(` ${mark} ${c2.name.padEnd(16)} ${dim(c2.detail)}`);
9565
+ }
9566
+ err("");
9567
+ const bad = checks.filter((c2) => c2.ok === false).length;
9568
+ const warn = checks.filter((c2) => c2.ok === "warn").length;
9569
+ if (bad) err(` ${red(`${bad} problem(s)`)}${warn ? dim(` \xB7 ${warn} warning(s)`) : ""}`);
9570
+ else if (warn) err(` ${yellow(`${warn} warning(s)`)} ${dim("\u2014 guard is working")}`);
9571
+ else err(` ${green("all good")}`);
9572
+ return bad ? 1 : 0;
9573
+ }
9574
+ var LOCAL_LOG2;
9575
+ var init_doctor = __esm({
9576
+ "src/commands/doctor.ts"() {
9577
+ "use strict";
9578
+ init_api_client();
9579
+ init_format();
9580
+ init_args();
9581
+ LOCAL_LOG2 = join6(homedir4(), ".solongate", "local-logs", "solongate-audit.jsonl");
9582
+ }
9583
+ });
9584
+
9585
+ // src/commands/watch.ts
9586
+ import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readSync as readSync2, statSync as statSync3 } from "fs";
9587
+ import { homedir as homedir5 } from "os";
9588
+ import { join as join7 } from "path";
9589
+ function tailLocal(file, maxBytes = 131072) {
9590
+ try {
9591
+ const size = statSync3(file).size;
9592
+ const start = Math.max(0, size - maxBytes);
9593
+ const fd = openSync2(file, "r");
9594
+ const buf = Buffer.alloc(size - start);
9595
+ readSync2(fd, buf, 0, buf.length, start);
9596
+ closeSync2(fd);
9597
+ const lines = buf.toString("utf-8").split("\n").filter(Boolean);
9598
+ if (start > 0) lines.shift();
9599
+ return lines;
9600
+ } catch {
9601
+ return [];
9602
+ }
9603
+ }
9604
+ function print(r, json) {
9605
+ if (json) return out(JSON.stringify(r));
9606
+ const dec = r.decision === "ALLOW" ? c.green : c.red;
9607
+ const src = r.source === "local" ? c.green + "LOC" : c.blue4 + "CLD";
9608
+ out(
9609
+ `${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}`
9610
+ );
9611
+ }
9612
+ async function run7(argv) {
9613
+ const { flags } = parse(argv);
9614
+ const json = flagBool(flags, "json");
9615
+ const decisionFilter = (flagStr(flags, "filter") || "").toUpperCase();
9616
+ const toolFilter = (flagStr(flags, "tool") || "").toLowerCase();
9617
+ const localOnly = flagBool(flags, "local-only");
9618
+ const cloudOnly = flagBool(flags, "cloud-only");
9619
+ const keep = (r) => {
9620
+ if (decisionFilter && r.decision.toUpperCase() !== decisionFilter && !(decisionFilter === "DENY" && r.decision === "DENIED")) return false;
9621
+ if (toolFilter && !r.tool.toLowerCase().includes(toolFilter)) return false;
9622
+ return true;
9623
+ };
9624
+ const seen = /* @__PURE__ */ new Set();
9625
+ let lastLocalTs = 0;
9626
+ let first = true;
9627
+ const emit = (rows) => {
9628
+ rows.sort((a, b) => a.at - b.at);
9629
+ for (const r of rows) if (keep(r)) print(r, json);
9630
+ };
9631
+ const pollLocal = () => {
9632
+ if (cloudOnly || !existsSync5(LOCAL_LOG3)) return;
9633
+ const rows = [];
9634
+ for (const line of tailLocal(LOCAL_LOG3)) {
9635
+ try {
9636
+ const j = JSON.parse(line);
9637
+ const at = Date.parse(String(j.ts ?? ""));
9638
+ if (!Number.isFinite(at) || at <= lastLocalTs) continue;
9639
+ rows.push({
9640
+ at,
9641
+ tool: String(j.tool ?? "?"),
9642
+ decision: String(j.decision ?? "ALLOW"),
9643
+ permission: String(j.permission ?? ""),
9644
+ detail: (j.arguments ? JSON.stringify(j.arguments) : String(j.reason ?? "")).replace(/\s+/g, " "),
9645
+ agent: String(j.agent_name ?? ""),
9646
+ source: "local",
9647
+ dlp: !!j.dlp
9648
+ });
9649
+ } catch {
9650
+ }
9651
+ }
9652
+ if (rows.length) lastLocalTs = rows[rows.length - 1].at;
9653
+ if (!first) emit(rows);
9654
+ };
9655
+ const pollCloud = async () => {
9656
+ if (localOnly) return;
9657
+ try {
9658
+ const res = await api.audit.list({ limit: 50 });
9659
+ const rows = [];
9660
+ for (const e of res.entries) {
9661
+ if (seen.has(e.id)) continue;
9662
+ seen.add(e.id);
9663
+ rows.push({
9664
+ at: Date.parse(e.created_at),
9665
+ tool: e.tool_name,
9666
+ decision: e.decision,
9667
+ permission: e.permission ?? "",
9668
+ detail: (e.arguments_summary ? JSON.stringify(e.arguments_summary) : e.reason ?? "").replace(/\s+/g, " "),
9669
+ agent: e.agent_name ?? "",
9670
+ source: "cloud",
9671
+ dlp: !!e.dlp_matches?.length
9672
+ });
9673
+ }
9674
+ if (!first) emit(rows);
9675
+ } catch {
9676
+ }
9677
+ };
9678
+ if (!json) err(` ${c.dim}watching guard stream \u2014 Ctrl+C to stop${c.reset}`);
9679
+ pollLocal();
9680
+ await pollCloud();
9681
+ first = false;
9682
+ return new Promise(() => {
9683
+ setInterval(pollLocal, 2e3);
9684
+ setInterval(() => void pollCloud(), 4e3);
9685
+ });
9686
+ }
9687
+ var LOCAL_LOG3, trunc, time;
9688
+ var init_watch = __esm({
9689
+ "src/commands/watch.ts"() {
9690
+ "use strict";
9691
+ init_api_client();
9692
+ init_cli_utils();
9693
+ init_args();
9694
+ init_format();
9695
+ LOCAL_LOG3 = join7(homedir5(), ".solongate", "local-logs", "solongate-audit.jsonl");
9696
+ trunc = (s, n) => s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
9697
+ time = (ms) => new Date(ms).toTimeString().slice(0, 8);
9698
+ }
9699
+ });
9700
+
9701
+ // src/commands/keys.ts
9702
+ async function run8(argv) {
9703
+ const { positionals, flags } = parse(argv);
9704
+ const sub = positionals[0] ?? "list";
9705
+ const json = flagBool(flags, "json");
9706
+ switch (sub) {
9707
+ case "help":
9708
+ return err(USAGE6), 0;
9709
+ case "list": {
9710
+ const { keys } = await api.keys.list();
9711
+ if (json) return printJson(keys), 0;
9712
+ if (!keys.length) return err(dim(" No API keys.")), 0;
9713
+ table(
9714
+ ["ID", "NAME", "PREFIX", "CREATED"],
9715
+ keys.map((k) => [dim(truncate3(k.id, 10)), truncate3(k.name, 24), cyan(k.key_prefix), dim(k.created_at)])
9716
+ );
9717
+ return 0;
9718
+ }
9719
+ case "create": {
9720
+ const name = flagStr(flags, "name");
9721
+ if (!name) return err(" Usage: keys create --name <name>"), 1;
9722
+ const res = await api.keys.create(name, !flagBool(flags, "test"));
9723
+ if (json) return printJson(res), 0;
9724
+ err(green(` \u2713 Created "${res.name}"`));
9725
+ err("");
9726
+ err(" " + bold(res.key));
9727
+ err(dim(" \u2191 shown only once \u2014 copy it now."));
9728
+ return 0;
9729
+ }
9730
+ case "revoke": {
9731
+ const id = positionals[1];
9732
+ if (!id) return err(" Usage: keys revoke <id>"), 1;
9733
+ await api.keys.revoke(id);
9734
+ if (json) return printJson({ ok: true, id }), 0;
9735
+ return err(green(` \u2713 Revoked ${id}`)), 0;
9736
+ }
9737
+ default:
9738
+ return err(USAGE6), 1;
9739
+ }
9740
+ }
9741
+ var USAGE6;
9742
+ var init_keys2 = __esm({
9743
+ "src/commands/keys.ts"() {
9744
+ "use strict";
9745
+ init_api_client();
9746
+ init_args();
9747
+ init_format();
9748
+ USAGE6 = `${bold("solongate keys")} \u2014 API keys
9749
+
9750
+ keys list List keys (prefix only)
9751
+ keys create --name <n> Create a key (secret shown once)
9752
+ keys revoke <id> Revoke a key
9753
+
9754
+ Add --json for machine-readable output.`;
9755
+ }
9756
+ });
9757
+
9758
+ // src/commands/mcp.ts
9759
+ async function run9(argv) {
9760
+ const { flags } = parse(argv);
9761
+ const json = flagBool(flags, "json");
9762
+ const { servers } = await api.mcp.list();
9763
+ if (json) return printJson(servers), 0;
9764
+ if (!servers.length) return err(dim(" No MCP servers registered.")), 0;
9765
+ table(
9766
+ ["STATUS", "NAME", "TARGET"],
9767
+ servers.map((s) => [
9768
+ s.status === "active" ? green(s.status) : yellow(s.status),
9769
+ cyan(truncate3(s.name, 24)),
9770
+ dim(truncate3(s.url || s.command || "\u2014", 46))
9771
+ ])
9772
+ );
9773
+ return 0;
9774
+ }
9775
+ var init_mcp2 = __esm({
9776
+ "src/commands/mcp.ts"() {
9777
+ "use strict";
9778
+ init_api_client();
9779
+ init_args();
9780
+ init_format();
9781
+ }
9782
+ });
9783
+
9784
+ // src/commands/alerts.ts
9785
+ async function run10(argv) {
9786
+ const { positionals, flags } = parse(argv);
9787
+ const sub = positionals[0] ?? "list";
9788
+ const json = flagBool(flags, "json");
9789
+ switch (sub) {
9790
+ case "help":
9791
+ return err(USAGE7), 0;
9792
+ case "list": {
9793
+ const { rules } = await api.settings.getAlerts();
9794
+ if (json) return printJson(rules), 0;
9795
+ if (!rules.length) return err(dim(" No alert rules.")), 0;
9796
+ table(
9797
+ ["ID", "ON", "SIGNAL", "THRESH", "WINDOW", "CHANNELS"],
9798
+ rules.map((r) => [
9799
+ dim(truncate3(r.id, 10)),
9800
+ r.enabled ? green("\u25CF") : dim("\u25CB"),
9801
+ cyan(r.signal),
9802
+ `${r.threshold}`,
9803
+ `${r.windowSeconds}s`,
9804
+ dim([...r.emails ?? [], ...(r.telegram ?? []).map((t) => "tg:" + t), ...(r.slackUrls ?? []).map(() => "slack")].join(" ") || "\u2014")
9805
+ ])
9806
+ );
9807
+ return 0;
9808
+ }
9809
+ case "add": {
9810
+ const email = flagStr(flags, "email");
9811
+ const telegram = flagStr(flags, "telegram");
9812
+ const slack = flagStr(flags, "slack");
9813
+ if (!email && !telegram && !slack) return err(" Need a channel: --email / --telegram / --slack"), 1;
9814
+ const res = await api.settings.createAlert({
9815
+ signal: flagStr(flags, "signal") ?? "deny",
9816
+ threshold: flagNum(flags, "threshold") ?? 5,
9817
+ windowSeconds: flagNum(flags, "window") ?? 300,
9818
+ emails: email ? [email] : void 0,
9819
+ telegram: telegram ? [telegram] : void 0,
9820
+ slackUrl: slack
9821
+ });
9822
+ if (json) return printJson(res.rule), 0;
9823
+ return err(green(` \u2713 Alert added (${res.rule.signal}, ${res.rule.threshold}/${res.rule.windowSeconds}s)`)), 0;
9824
+ }
9825
+ case "remove": {
9826
+ const id = positionals[1];
9827
+ if (!id) return err(" Usage: alerts remove <id>"), 1;
9828
+ await api.settings.deleteAlert(id);
9829
+ if (json) return printJson({ ok: true, id }), 0;
9830
+ return err(green(` \u2713 Removed ${id}`)), 0;
9831
+ }
9832
+ default:
9833
+ return err(USAGE7), 1;
9834
+ }
9835
+ }
9836
+ var USAGE7;
9837
+ var init_alerts = __esm({
9838
+ "src/commands/alerts.ts"() {
9839
+ "use strict";
9840
+ init_api_client();
9841
+ init_args();
9842
+ init_format();
9843
+ USAGE7 = `${bold("solongate alerts")} \u2014 spike alerts (Telegram / email)
9844
+
9845
+ alerts list
9846
+ alerts add --signal deny|dlp|ratelimit|any --threshold N --window S
9847
+ (--email <a> | --telegram <chatId> | --slack <url>)
9848
+ alerts remove <id>
9849
+
9850
+ Add --json for machine-readable output.`;
9851
+ }
9852
+ });
9853
+
9854
+ // src/commands/webhooks.ts
9855
+ async function run11(argv) {
9856
+ const { positionals, flags } = parse(argv);
9857
+ const sub = positionals[0] ?? "list";
9858
+ const json = flagBool(flags, "json");
9859
+ switch (sub) {
9860
+ case "help":
9861
+ return err(USAGE8), 0;
9862
+ case "list": {
9863
+ const { webhooks } = await api.settings.getWebhooks();
9864
+ if (json) return printJson(webhooks), 0;
9865
+ if (!webhooks.length) return err(dim(" No webhooks.")), 0;
9866
+ table(
9867
+ ["ID", "ON", "EVENTS", "URL"],
9868
+ webhooks.map((w) => [dim(truncate3(w.id, 10)), w.enabled ? green("\u25CF") : dim("\u25CB"), cyan(w.events), dim(truncate3(w.url, 46))])
9869
+ );
9870
+ return 0;
9871
+ }
9872
+ case "add": {
9873
+ const url = flagStr(flags, "url");
9874
+ if (!url) return err(" Usage: webhooks add --url <https://\u2026> [--events denials|allowed|all]"), 1;
9875
+ const res = await api.settings.createWebhook({ url, events: flagStr(flags, "events") ?? "denials" });
9876
+ if (json) return printJson(res.webhook), 0;
9877
+ return err(green(` \u2713 Webhook added (${res.webhook.events}) \u2192 ${res.webhook.url}`)), 0;
9878
+ }
9879
+ case "remove": {
9880
+ const id = positionals[1];
9881
+ if (!id) return err(" Usage: webhooks remove <id>"), 1;
9882
+ await api.settings.deleteWebhook(id);
9883
+ if (json) return printJson({ ok: true, id }), 0;
9884
+ return err(green(` \u2713 Removed ${id}`)), 0;
9885
+ }
9886
+ default:
9887
+ return err(USAGE8), 1;
9888
+ }
9889
+ }
9890
+ var USAGE8;
9891
+ var init_webhooks = __esm({
9892
+ "src/commands/webhooks.ts"() {
9893
+ "use strict";
9894
+ init_api_client();
9895
+ init_args();
9896
+ init_format();
9897
+ USAGE8 = `${bold("solongate webhooks")} \u2014 event webhooks
9898
+
9899
+ webhooks list
9900
+ webhooks add --url <https://\u2026> [--events denials|allowed|all]
9901
+ webhooks remove <id>
9902
+
9903
+ Add --json for machine-readable output.`;
9904
+ }
9905
+ });
9906
+
9336
9907
  // src/commands/index.ts
9337
9908
  var commands_exports = {};
9338
9909
  __export(commands_exports, {
@@ -9355,6 +9926,18 @@ async function dispatch(command, argv) {
9355
9926
  return runAgents(argv);
9356
9927
  case "agent":
9357
9928
  return runAgent(argv);
9929
+ case "doctor":
9930
+ return run6(argv);
9931
+ case "watch":
9932
+ return run7(argv);
9933
+ case "keys":
9934
+ return run8(argv);
9935
+ case "mcp":
9936
+ return run9(argv);
9937
+ case "alerts":
9938
+ return run10(argv);
9939
+ case "webhooks":
9940
+ return run11(argv);
9358
9941
  default:
9359
9942
  err(` Unknown command: ${command}`);
9360
9943
  return 1;
@@ -9389,7 +9972,13 @@ var init_commands = __esm({
9389
9972
  init_stats2();
9390
9973
  init_audit2();
9391
9974
  init_agents2();
9392
- COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"];
9975
+ init_doctor();
9976
+ init_watch();
9977
+ init_keys2();
9978
+ init_mcp2();
9979
+ init_alerts();
9980
+ init_webhooks();
9981
+ COMMAND_NAMES = ["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"];
9393
9982
  }
9394
9983
  });
9395
9984
 
@@ -9405,14 +9994,14 @@ __export(global_install_exports, {
9405
9994
  runGlobalRestore: () => runGlobalRestore,
9406
9995
  unlockProtected: () => unlockProtected
9407
9996
  });
9408
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
9409
- import { resolve as resolve4, join as join6, dirname } from "path";
9410
- import { homedir as homedir4 } from "os";
9997
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
9998
+ import { resolve as resolve4, join as join8, dirname } from "path";
9999
+ import { homedir as homedir6 } from "os";
9411
10000
  import { fileURLToPath } from "url";
9412
10001
  import { createInterface } from "readline";
9413
10002
  import { execFileSync as execFileSync2 } from "child_process";
9414
10003
  function lockFile(file) {
9415
- if (!existsSync4(file)) return;
10004
+ if (!existsSync6(file)) return;
9416
10005
  try {
9417
10006
  if (process.platform === "win32") {
9418
10007
  try {
@@ -9438,7 +10027,7 @@ function lockFile(file) {
9438
10027
  }
9439
10028
  }
9440
10029
  function unlockFile(file) {
9441
- if (!existsSync4(file)) return;
10030
+ if (!existsSync6(file)) return;
9442
10031
  try {
9443
10032
  if (process.platform === "win32") {
9444
10033
  try {
@@ -9470,10 +10059,10 @@ function unlockFile(file) {
9470
10059
  function protectedTargets() {
9471
10060
  const p = globalPaths();
9472
10061
  return [
9473
- join6(p.hooksDir, "guard.mjs"),
9474
- join6(p.hooksDir, "audit.mjs"),
9475
- join6(p.hooksDir, "stop.mjs"),
9476
- join6(p.hooksDir, "shield.mjs"),
10062
+ join8(p.hooksDir, "guard.mjs"),
10063
+ join8(p.hooksDir, "audit.mjs"),
10064
+ join8(p.hooksDir, "stop.mjs"),
10065
+ join8(p.hooksDir, "shield.mjs"),
9477
10066
  p.configPath,
9478
10067
  p.settingsPath
9479
10068
  ];
@@ -9485,26 +10074,26 @@ function unlockProtected() {
9485
10074
  for (const f of protectedTargets()) unlockFile(f);
9486
10075
  }
9487
10076
  function globalPaths() {
9488
- const home = homedir4();
9489
- const sgDir = join6(home, ".solongate");
9490
- const hooksDir = join6(sgDir, "hooks");
9491
- const claudeDir = join6(home, ".claude");
10077
+ const home = homedir6();
10078
+ const sgDir = join8(home, ".solongate");
10079
+ const hooksDir = join8(sgDir, "hooks");
10080
+ const claudeDir = join8(home, ".claude");
9492
10081
  return {
9493
10082
  home,
9494
10083
  sgDir,
9495
10084
  hooksDir,
9496
10085
  claudeDir,
9497
- settingsPath: join6(claudeDir, "settings.json"),
9498
- backupPath: join6(claudeDir, "settings.solongate.bak"),
9499
- configPath: join6(sgDir, "cloud-guard.json")
10086
+ settingsPath: join8(claudeDir, "settings.json"),
10087
+ backupPath: join8(claudeDir, "settings.solongate.bak"),
10088
+ configPath: join8(sgDir, "cloud-guard.json")
9500
10089
  };
9501
10090
  }
9502
10091
  function readHook(filename) {
9503
- return readFileSync6(join6(HOOKS_DIR, filename), "utf-8");
10092
+ return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
9504
10093
  }
9505
10094
  function readGuard() {
9506
- const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
9507
- return existsSync4(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
10095
+ const bundled = join8(HOOKS_DIR, "guard.bundled.mjs");
10096
+ return existsSync6(bundled) ? readFileSync6(bundled, "utf-8") : readHook("guard.mjs");
9508
10097
  }
9509
10098
  function ask(question) {
9510
10099
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -9517,10 +10106,10 @@ function runGlobalRestore() {
9517
10106
  const p = globalPaths();
9518
10107
  unlockProtected();
9519
10108
  removeClaudeShim();
9520
- if (existsSync4(p.backupPath)) {
10109
+ if (existsSync6(p.backupPath)) {
9521
10110
  writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
9522
10111
  console.log(` Restored ${p.settingsPath} from backup.`);
9523
- } else if (existsSync4(p.settingsPath)) {
10112
+ } else if (existsSync6(p.settingsPath)) {
9524
10113
  try {
9525
10114
  const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
9526
10115
  delete s.hooks;
@@ -9558,15 +10147,15 @@ function shimTargets() {
9558
10147
  return [];
9559
10148
  }
9560
10149
  }
9561
- return [".bashrc", ".zshrc", ".profile"].map((f) => join6(homedir4(), f)).filter((f) => existsSync4(f));
10150
+ return [".bashrc", ".zshrc", ".profile"].map((f) => join8(homedir6(), f)).filter((f) => existsSync6(f));
9562
10151
  }
9563
- function writeShimBlock(file, block) {
10152
+ function writeShimBlock(file, block2) {
9564
10153
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
9565
- let content = existsSync4(file) ? readFileSync6(file, "utf-8") : "";
10154
+ let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
9566
10155
  content = content.replace(re, "");
9567
- if (block) {
10156
+ if (block2) {
9568
10157
  if (content.length && !content.endsWith("\n")) content += "\n";
9569
- content += block + "\n";
10158
+ content += block2 + "\n";
9570
10159
  }
9571
10160
  mkdirSync3(dirname(file), { recursive: true });
9572
10161
  writeFileSync3(file, content);
@@ -9580,7 +10169,7 @@ function installClaudeShim(shieldPath) {
9580
10169
  const node = process.execPath.replace(/\\/g, "/");
9581
10170
  const shield = shieldPath.replace(/\\/g, "/");
9582
10171
  const win = process.platform === "win32";
9583
- const block = win ? `${SHIM_BEGIN}
10172
+ const block2 = win ? `${SHIM_BEGIN}
9584
10173
  function claude { & "${node}" "${shield}" -- "${real}" @args }
9585
10174
  ${SHIM_END}` : `${SHIM_BEGIN}
9586
10175
  claude() { "${node}" "${shield}" -- "${real}" "$@"; }
@@ -9592,7 +10181,7 @@ ${SHIM_END}`;
9592
10181
  }
9593
10182
  for (const file of targets) {
9594
10183
  try {
9595
- writeShimBlock(file, block);
10184
+ writeShimBlock(file, block2);
9596
10185
  console.log(` Auto-shield on: \`claude\` now masks secrets (${file})`);
9597
10186
  } catch (e) {
9598
10187
  console.log(` (Could not enable auto-shield in ${file}: ${e.message})`);
@@ -9629,18 +10218,18 @@ async function runGlobalInstall(opts = {}) {
9629
10218
  mkdirSync3(p.hooksDir, { recursive: true });
9630
10219
  mkdirSync3(p.claudeDir, { recursive: true });
9631
10220
  unlockProtected();
9632
- writeFileSync3(join6(p.hooksDir, "guard.mjs"), readGuard());
9633
- writeFileSync3(join6(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
9634
- writeFileSync3(join6(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
9635
- writeFileSync3(join6(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
10221
+ writeFileSync3(join8(p.hooksDir, "guard.mjs"), readGuard());
10222
+ writeFileSync3(join8(p.hooksDir, "audit.mjs"), readHook("audit.mjs"));
10223
+ writeFileSync3(join8(p.hooksDir, "stop.mjs"), readHook("stop.mjs"));
10224
+ writeFileSync3(join8(p.hooksDir, "shield.mjs"), readHook("shield.mjs"));
9636
10225
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
9637
10226
  removeClaudeShim();
9638
10227
  writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
9639
10228
  console.log(` Wrote ${p.configPath}`);
9640
10229
  let existing = {};
9641
- if (existsSync4(p.settingsPath)) {
10230
+ if (existsSync6(p.settingsPath)) {
9642
10231
  const raw = readFileSync6(p.settingsPath, "utf-8");
9643
- if (!existsSync4(p.backupPath)) {
10232
+ if (!existsSync6(p.backupPath)) {
9644
10233
  writeFileSync3(p.backupPath, raw);
9645
10234
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
9646
10235
  }
@@ -9650,9 +10239,9 @@ async function runGlobalInstall(opts = {}) {
9650
10239
  existing = {};
9651
10240
  }
9652
10241
  }
9653
- const guardAbs = join6(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
9654
- const auditAbs = join6(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
9655
- const stopAbs = join6(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
10242
+ const guardAbs = join8(p.hooksDir, "guard.mjs").replace(/\\/g, "/");
10243
+ const auditAbs = join8(p.hooksDir, "audit.mjs").replace(/\\/g, "/");
10244
+ const stopAbs = join8(p.hooksDir, "stop.mjs").replace(/\\/g, "/");
9656
10245
  const nodeBin = process.execPath.replace(/\\/g, "/");
9657
10246
  const merged = {
9658
10247
  ...existing,
@@ -9842,22 +10431,22 @@ import { createServer, request as httpRequest } from "http";
9842
10431
  import { request as httpsRequest } from "https";
9843
10432
  import { spawn as spawn3 } from "child_process";
9844
10433
  import { URL as URL2 } from "url";
9845
- import { readFileSync as readFileSync7, existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
10434
+ import { readFileSync as readFileSync7, existsSync as existsSync7, readdirSync, statSync as statSync4 } from "fs";
9846
10435
  import { resolve as resolve5 } from "path";
9847
- import { homedir as homedir5 } from "os";
10436
+ import { homedir as homedir7 } from "os";
9848
10437
  function findCacheFile() {
9849
- const dir = resolve5(homedir5(), ".solongate");
10438
+ const dir = resolve5(homedir7(), ".solongate");
9850
10439
  const envSel = process.env.SOLONGATE_AGENT_ID;
9851
10440
  if (envSel) {
9852
10441
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
9853
- if (existsSync5(f)) return f;
10442
+ if (existsSync7(f)) return f;
9854
10443
  }
9855
10444
  let best = null, bestTs = -1;
9856
10445
  try {
9857
10446
  for (const name of readdirSync(dir)) {
9858
10447
  if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
9859
10448
  const full = resolve5(dir, name);
9860
- const ts = statSync2(full).mtimeMs;
10449
+ const ts = statSync4(full).mtimeMs;
9861
10450
  if (ts > bestTs) {
9862
10451
  bestTs = ts;
9863
10452
  best = full;
@@ -9871,7 +10460,7 @@ function findCacheFile() {
9871
10460
  function loadCfg() {
9872
10461
  try {
9873
10462
  const f = findCacheFile();
9874
- if (f && existsSync5(f)) {
10463
+ if (f && existsSync7(f)) {
9875
10464
  const c2 = JSON.parse(readFileSync7(f, "utf-8"));
9876
10465
  const d = c2?.security?.dlpRedact;
9877
10466
  const g = c2?.security?.ghost;
@@ -10148,9 +10737,9 @@ __export(logs_server_exports, {
10148
10737
  runLogsServer: () => runLogsServer
10149
10738
  });
10150
10739
  import { createServer as createServer2 } from "http";
10151
- import { readFileSync as readFileSync8, statSync as statSync3 } from "fs";
10152
- import { resolve as resolve6, join as join7, isAbsolute } from "path";
10153
- import { homedir as homedir6 } from "os";
10740
+ import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
10741
+ import { resolve as resolve6, join as join9, isAbsolute } from "path";
10742
+ import { homedir as homedir8 } from "os";
10154
10743
  import { readdirSync as readdirSync2 } from "fs";
10155
10744
  function allowedOrigins() {
10156
10745
  const base = [
@@ -10167,15 +10756,15 @@ function resolveLocalLogDir(rawPath) {
10167
10756
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
10168
10757
  if (!dir) return null;
10169
10758
  if (isAbsolute(dir)) return dir;
10170
- return resolve6(homedir6(), ".solongate", "local-logs");
10759
+ return resolve6(homedir8(), ".solongate", "local-logs");
10171
10760
  }
10172
10761
  async function findLogDir() {
10173
- const base = resolve6(homedir6(), ".solongate");
10762
+ const base = resolve6(homedir8(), ".solongate");
10174
10763
  try {
10175
10764
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
10176
10765
  for (const f of files) {
10177
10766
  try {
10178
- const c2 = JSON.parse(readFileSync8(join7(base, f), "utf-8"));
10767
+ const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
10179
10768
  const p = c2?.security?.localLogs?.path;
10180
10769
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
10181
10770
  } catch {
@@ -10184,7 +10773,7 @@ async function findLogDir() {
10184
10773
  } catch {
10185
10774
  }
10186
10775
  try {
10187
- const cfgRaw = readFileSync8(join7(base, "cloud-guard.json"), "utf-8");
10776
+ const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
10188
10777
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
10189
10778
  if (apiKey) {
10190
10779
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -10213,9 +10802,9 @@ function setCors(req, res) {
10213
10802
  }
10214
10803
  function fileInfo(dir) {
10215
10804
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
10216
- const file = join7(dir, LOG_FILENAME);
10805
+ const file = join9(dir, LOG_FILENAME);
10217
10806
  try {
10218
- const st = statSync3(file);
10807
+ const st = statSync5(file);
10219
10808
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
10220
10809
  } catch {
10221
10810
  return { file, exists: false, size: 0, mtimeMs: 0 };
@@ -10326,7 +10915,7 @@ var init_logs_server = __esm({
10326
10915
 
10327
10916
  // src/inject.ts
10328
10917
  var inject_exports = {};
10329
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync6, copyFileSync } from "fs";
10918
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync } from "fs";
10330
10919
  import { resolve as resolve7 } from "path";
10331
10920
  import { execSync } from "child_process";
10332
10921
  function parseInjectArgs(argv) {
@@ -10384,7 +10973,7 @@ WHAT IT DOES
10384
10973
  `);
10385
10974
  }
10386
10975
  function detectProject() {
10387
- if (!existsSync6(resolve7("package.json"))) return false;
10976
+ if (!existsSync8(resolve7("package.json"))) return false;
10388
10977
  try {
10389
10978
  const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
10390
10979
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -10400,13 +10989,13 @@ function findTsEntryFile() {
10400
10989
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
10401
10990
  if (typeof binPath === "string") {
10402
10991
  const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
10403
- if (existsSync6(resolve7(srcPath))) return resolve7(srcPath);
10404
- if (existsSync6(resolve7(binPath))) return resolve7(binPath);
10992
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
10993
+ if (existsSync8(resolve7(binPath))) return resolve7(binPath);
10405
10994
  }
10406
10995
  }
10407
10996
  if (pkg.main) {
10408
10997
  const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
10409
- if (existsSync6(resolve7(srcPath))) return resolve7(srcPath);
10998
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
10410
10999
  }
10411
11000
  } catch {
10412
11001
  }
@@ -10420,7 +11009,7 @@ function findTsEntryFile() {
10420
11009
  ];
10421
11010
  for (const c2 of candidates) {
10422
11011
  const full = resolve7(c2);
10423
- if (existsSync6(full)) {
11012
+ if (existsSync8(full)) {
10424
11013
  try {
10425
11014
  const content = readFileSync9(full, "utf-8");
10426
11015
  if (content.includes("McpServer") || content.includes("McpServer")) {
@@ -10431,13 +11020,13 @@ function findTsEntryFile() {
10431
11020
  }
10432
11021
  }
10433
11022
  for (const c2 of candidates) {
10434
- if (existsSync6(resolve7(c2))) return resolve7(c2);
11023
+ if (existsSync8(resolve7(c2))) return resolve7(c2);
10435
11024
  }
10436
11025
  return null;
10437
11026
  }
10438
11027
  function detectPackageManager() {
10439
- if (existsSync6(resolve7("pnpm-lock.yaml"))) return "pnpm";
10440
- if (existsSync6(resolve7("yarn.lock"))) return "yarn";
11028
+ if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
11029
+ if (existsSync8(resolve7("yarn.lock"))) return "yarn";
10441
11030
  return "npm";
10442
11031
  }
10443
11032
  function installSdk() {
@@ -10580,7 +11169,7 @@ async function main2() {
10580
11169
  }
10581
11170
  log3(" Language: TypeScript");
10582
11171
  const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
10583
- if (!entryFile || !existsSync6(entryFile)) {
11172
+ if (!entryFile || !existsSync8(entryFile)) {
10584
11173
  log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
10585
11174
  log3("");
10586
11175
  log3(" Specify it manually: --file <path>");
@@ -10593,7 +11182,7 @@ async function main2() {
10593
11182
  log3("");
10594
11183
  const backupPath = entryFile + ".solongate-backup";
10595
11184
  if (opts.restore) {
10596
- if (!existsSync6(backupPath)) {
11185
+ if (!existsSync8(backupPath)) {
10597
11186
  log3(" No backup found. Nothing to restore.");
10598
11187
  process.exit(1);
10599
11188
  }
@@ -10629,7 +11218,7 @@ async function main2() {
10629
11218
  log3(" To apply: npx @solongate/proxy inject");
10630
11219
  process.exit(0);
10631
11220
  }
10632
- if (!existsSync6(backupPath)) {
11221
+ if (!existsSync8(backupPath)) {
10633
11222
  copyFileSync(entryFile, backupPath);
10634
11223
  log3("");
10635
11224
  log3(` Backup: ${backupPath}`);
@@ -10663,8 +11252,8 @@ var init_inject = __esm({
10663
11252
 
10664
11253
  // src/create.ts
10665
11254
  var create_exports = {};
10666
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
10667
- import { resolve as resolve8, join as join8 } from "path";
11255
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
11256
+ import { resolve as resolve8, join as join10 } from "path";
10668
11257
  import { execSync as execSync2 } from "child_process";
10669
11258
  function withSpinner(message, fn) {
10670
11259
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -10744,7 +11333,7 @@ EXAMPLES
10744
11333
  }
10745
11334
  function createProject(dir, name, _policy) {
10746
11335
  writeFileSync5(
10747
- join8(dir, "package.json"),
11336
+ join10(dir, "package.json"),
10748
11337
  JSON.stringify(
10749
11338
  {
10750
11339
  name,
@@ -10774,7 +11363,7 @@ function createProject(dir, name, _policy) {
10774
11363
  ) + "\n"
10775
11364
  );
10776
11365
  writeFileSync5(
10777
- join8(dir, "tsconfig.json"),
11366
+ join10(dir, "tsconfig.json"),
10778
11367
  JSON.stringify(
10779
11368
  {
10780
11369
  compilerOptions: {
@@ -10794,9 +11383,9 @@ function createProject(dir, name, _policy) {
10794
11383
  2
10795
11384
  ) + "\n"
10796
11385
  );
10797
- mkdirSync4(join8(dir, "src"), { recursive: true });
11386
+ mkdirSync4(join10(dir, "src"), { recursive: true });
10798
11387
  writeFileSync5(
10799
- join8(dir, "src", "index.ts"),
11388
+ join10(dir, "src", "index.ts"),
10800
11389
  `#!/usr/bin/env node
10801
11390
 
10802
11391
  console.log = (...args: unknown[]) => {
@@ -10838,7 +11427,7 @@ console.log('Press Ctrl+C to stop.');
10838
11427
  `
10839
11428
  );
10840
11429
  writeFileSync5(
10841
- join8(dir, ".mcp.json"),
11430
+ join10(dir, ".mcp.json"),
10842
11431
  JSON.stringify(
10843
11432
  {
10844
11433
  mcpServers: {
@@ -10856,12 +11445,12 @@ console.log('Press Ctrl+C to stop.');
10856
11445
  ) + "\n"
10857
11446
  );
10858
11447
  writeFileSync5(
10859
- join8(dir, ".env"),
11448
+ join10(dir, ".env"),
10860
11449
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
10861
11450
  `
10862
11451
  );
10863
11452
  writeFileSync5(
10864
- join8(dir, ".gitignore"),
11453
+ join10(dir, ".gitignore"),
10865
11454
  `node_modules/
10866
11455
  dist/
10867
11456
  *.solongate-backup
@@ -10875,7 +11464,7 @@ async function main3() {
10875
11464
  const opts = parseCreateArgs(process.argv);
10876
11465
  const dir = resolve8(opts.name);
10877
11466
  printBanner("Create MCP Server");
10878
- if (existsSync7(dir)) {
11467
+ if (existsSync9(dir)) {
10879
11468
  log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
10880
11469
  process.exit(1);
10881
11470
  }
@@ -10954,12 +11543,12 @@ var init_create = __esm({
10954
11543
 
10955
11544
  // src/pull-push.ts
10956
11545
  var pull_push_exports = {};
10957
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "fs";
11546
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
10958
11547
  import { resolve as resolve9 } from "path";
10959
11548
  function loadEnv() {
10960
11549
  if (process.env.SOLONGATE_API_KEY) return;
10961
11550
  const envPath = resolve9(".env");
10962
- if (!existsSync8(envPath)) return;
11551
+ if (!existsSync10(envPath)) return;
10963
11552
  try {
10964
11553
  const content = readFileSync10(envPath, "utf-8");
10965
11554
  for (const line of content.split("\n")) {
@@ -11026,7 +11615,7 @@ async function listPolicies(apiKey) {
11026
11615
  const data = await res.json();
11027
11616
  return data.policies ?? [];
11028
11617
  }
11029
- async function list3(apiKey, policyId) {
11618
+ async function list5(apiKey, policyId) {
11030
11619
  const policies = await listPolicies(apiKey);
11031
11620
  if (policies.length === 0) {
11032
11621
  log5(yellow2("No policies found. Create one in the dashboard first."));
@@ -11161,7 +11750,7 @@ async function pull(apiKey, file, policyId) {
11161
11750
  log5("");
11162
11751
  }
11163
11752
  async function push(apiKey, file, policyId) {
11164
- if (!existsSync8(file)) {
11753
+ if (!existsSync10(file)) {
11165
11754
  log5(red2(`ERROR: File not found: ${file}`));
11166
11755
  process.exit(1);
11167
11756
  }
@@ -11229,7 +11818,7 @@ async function main4() {
11229
11818
  } else if (command === "push") {
11230
11819
  await push(apiKey, file, policyId);
11231
11820
  } else if (command === "list" || command === "ls") {
11232
- await list3(apiKey, policyId);
11821
+ await list5(apiKey, policyId);
11233
11822
  } else {
11234
11823
  log5(red2(`Unknown command: ${command}`));
11235
11824
  log5("");
@@ -14294,7 +14883,7 @@ ${msg.content.text}`;
14294
14883
 
14295
14884
  // src/index.ts
14296
14885
  init_cli_utils();
14297
- 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"]);
14886
+ 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"]);
14298
14887
  var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
14299
14888
  if (!IS_HUMAN_CLI) {
14300
14889
  console.log = (...args) => {
@@ -14343,7 +14932,7 @@ async function main5() {
14343
14932
  await launchTui2();
14344
14933
  return;
14345
14934
  }
14346
- const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent"]);
14935
+ const MGMT_COMMANDS = /* @__PURE__ */ new Set(["policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks"]);
14347
14936
  if (MGMT_COMMANDS.has(subcommand ?? "")) {
14348
14937
  const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
14349
14938
  const code = await runCommand2(subcommand, process.argv.slice(3));