@solongate/proxy 0.78.0 → 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";
@@ -6971,6 +6995,44 @@ var init_agents = __esm({
6971
6995
  }
6972
6996
  });
6973
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
+
6974
7036
  // src/api-client/index.ts
6975
7037
  var api;
6976
7038
  var init_api_client = __esm({
@@ -6983,7 +7045,9 @@ var init_api_client = __esm({
6983
7045
  init_stats();
6984
7046
  init_audit();
6985
7047
  init_agents();
6986
- 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 };
6987
7051
  }
6988
7052
  });
6989
7053
 
@@ -7870,8 +7934,8 @@ function ruleSummary(r) {
7870
7934
  return bits.join(" ");
7871
7935
  }
7872
7936
  function PoliciesPanel({ focused }) {
7873
- const list4 = useLoader(() => api.policies.list());
7874
- const policies = list4.data?.policies ?? [];
7937
+ const list6 = useLoader(() => api.policies.list());
7938
+ const policies = list6.data?.policies ?? [];
7875
7939
  const [view, setView] = useState3("list");
7876
7940
  const [pi, setPi] = useState3(0);
7877
7941
  const [ri, setRi] = useState3(0);
@@ -7913,7 +7977,7 @@ function PoliciesPanel({ focused }) {
7913
7977
  setRules(res.rules);
7914
7978
  setDirty(false);
7915
7979
  setStatus("\u2713 Saved v" + res._version);
7916
- list4.reload();
7980
+ list6.reload();
7917
7981
  } catch (e) {
7918
7982
  setStatus("\u2717 " + (e instanceof Error ? e.message : String(e)));
7919
7983
  }
@@ -7983,7 +8047,7 @@ function PoliciesPanel({ focused }) {
7983
8047
  { isActive: focused && !editing }
7984
8048
  );
7985
8049
  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: [
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: [
7987
8051
  /* @__PURE__ */ jsx3(Text3, { color: theme.dim, children: focused ? "\u2191\u2193 select \xB7 enter open" : "press \u2192 to browse" }),
7988
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: [
7989
8053
  (i === pi ? "\u25B8 " : " ") + truncate2(p.name, 26).padEnd(27),
@@ -9334,6 +9398,16 @@ async function run5(argv) {
9334
9398
  else err(green(` \u2713 Whitelisted (${res2.scope})`) + dim(` \u2192 ${res2.policy_id} v${res2.policy_version}`));
9335
9399
  return 0;
9336
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
+ }
9337
9411
  const query = {
9338
9412
  filter: flagStr(flags, "filter"),
9339
9413
  tool: flagStr(flags, "tool"),
@@ -9441,6 +9515,395 @@ var init_agents2 = __esm({
9441
9515
  }
9442
9516
  });
9443
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
+
9444
9907
  // src/commands/index.ts
9445
9908
  var commands_exports = {};
9446
9909
  __export(commands_exports, {
@@ -9463,6 +9926,18 @@ async function dispatch(command, argv) {
9463
9926
  return runAgents(argv);
9464
9927
  case "agent":
9465
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);
9466
9941
  default:
9467
9942
  err(` Unknown command: ${command}`);
9468
9943
  return 1;
@@ -9497,7 +9972,13 @@ var init_commands = __esm({
9497
9972
  init_stats2();
9498
9973
  init_audit2();
9499
9974
  init_agents2();
9500
- 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"];
9501
9982
  }
9502
9983
  });
9503
9984
 
@@ -9513,14 +9994,14 @@ __export(global_install_exports, {
9513
9994
  runGlobalRestore: () => runGlobalRestore,
9514
9995
  unlockProtected: () => unlockProtected
9515
9996
  });
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";
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";
9519
10000
  import { fileURLToPath } from "url";
9520
10001
  import { createInterface } from "readline";
9521
10002
  import { execFileSync as execFileSync2 } from "child_process";
9522
10003
  function lockFile(file) {
9523
- if (!existsSync4(file)) return;
10004
+ if (!existsSync6(file)) return;
9524
10005
  try {
9525
10006
  if (process.platform === "win32") {
9526
10007
  try {
@@ -9546,7 +10027,7 @@ function lockFile(file) {
9546
10027
  }
9547
10028
  }
9548
10029
  function unlockFile(file) {
9549
- if (!existsSync4(file)) return;
10030
+ if (!existsSync6(file)) return;
9550
10031
  try {
9551
10032
  if (process.platform === "win32") {
9552
10033
  try {
@@ -9578,10 +10059,10 @@ function unlockFile(file) {
9578
10059
  function protectedTargets() {
9579
10060
  const p = globalPaths();
9580
10061
  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"),
10062
+ join8(p.hooksDir, "guard.mjs"),
10063
+ join8(p.hooksDir, "audit.mjs"),
10064
+ join8(p.hooksDir, "stop.mjs"),
10065
+ join8(p.hooksDir, "shield.mjs"),
9585
10066
  p.configPath,
9586
10067
  p.settingsPath
9587
10068
  ];
@@ -9593,26 +10074,26 @@ function unlockProtected() {
9593
10074
  for (const f of protectedTargets()) unlockFile(f);
9594
10075
  }
9595
10076
  function globalPaths() {
9596
- const home = homedir4();
9597
- const sgDir = join6(home, ".solongate");
9598
- const hooksDir = join6(sgDir, "hooks");
9599
- 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");
9600
10081
  return {
9601
10082
  home,
9602
10083
  sgDir,
9603
10084
  hooksDir,
9604
10085
  claudeDir,
9605
- settingsPath: join6(claudeDir, "settings.json"),
9606
- backupPath: join6(claudeDir, "settings.solongate.bak"),
9607
- 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")
9608
10089
  };
9609
10090
  }
9610
10091
  function readHook(filename) {
9611
- return readFileSync6(join6(HOOKS_DIR, filename), "utf-8");
10092
+ return readFileSync6(join8(HOOKS_DIR, filename), "utf-8");
9612
10093
  }
9613
10094
  function readGuard() {
9614
- const bundled = join6(HOOKS_DIR, "guard.bundled.mjs");
9615
- 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");
9616
10097
  }
9617
10098
  function ask(question) {
9618
10099
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -9625,10 +10106,10 @@ function runGlobalRestore() {
9625
10106
  const p = globalPaths();
9626
10107
  unlockProtected();
9627
10108
  removeClaudeShim();
9628
- if (existsSync4(p.backupPath)) {
10109
+ if (existsSync6(p.backupPath)) {
9629
10110
  writeFileSync3(p.settingsPath, readFileSync6(p.backupPath, "utf-8"));
9630
10111
  console.log(` Restored ${p.settingsPath} from backup.`);
9631
- } else if (existsSync4(p.settingsPath)) {
10112
+ } else if (existsSync6(p.settingsPath)) {
9632
10113
  try {
9633
10114
  const s = JSON.parse(readFileSync6(p.settingsPath, "utf-8"));
9634
10115
  delete s.hooks;
@@ -9666,11 +10147,11 @@ function shimTargets() {
9666
10147
  return [];
9667
10148
  }
9668
10149
  }
9669
- 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));
9670
10151
  }
9671
10152
  function writeShimBlock(file, block2) {
9672
10153
  const re = new RegExp(escapeRe(SHIM_BEGIN) + "[\\s\\S]*?" + escapeRe(SHIM_END) + "\\r?\\n?", "g");
9673
- let content = existsSync4(file) ? readFileSync6(file, "utf-8") : "";
10154
+ let content = existsSync6(file) ? readFileSync6(file, "utf-8") : "";
9674
10155
  content = content.replace(re, "");
9675
10156
  if (block2) {
9676
10157
  if (content.length && !content.endsWith("\n")) content += "\n";
@@ -9737,18 +10218,18 @@ async function runGlobalInstall(opts = {}) {
9737
10218
  mkdirSync3(p.hooksDir, { recursive: true });
9738
10219
  mkdirSync3(p.claudeDir, { recursive: true });
9739
10220
  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"));
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"));
9744
10225
  console.log(` Installed hooks \u2192 ${p.hooksDir}`);
9745
10226
  removeClaudeShim();
9746
10227
  writeFileSync3(p.configPath, JSON.stringify({ apiKey, apiUrl }, null, 2) + "\n");
9747
10228
  console.log(` Wrote ${p.configPath}`);
9748
10229
  let existing = {};
9749
- if (existsSync4(p.settingsPath)) {
10230
+ if (existsSync6(p.settingsPath)) {
9750
10231
  const raw = readFileSync6(p.settingsPath, "utf-8");
9751
- if (!existsSync4(p.backupPath)) {
10232
+ if (!existsSync6(p.backupPath)) {
9752
10233
  writeFileSync3(p.backupPath, raw);
9753
10234
  console.log(` Backed up existing settings \u2192 ${p.backupPath}`);
9754
10235
  }
@@ -9758,9 +10239,9 @@ async function runGlobalInstall(opts = {}) {
9758
10239
  existing = {};
9759
10240
  }
9760
10241
  }
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, "/");
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, "/");
9764
10245
  const nodeBin = process.execPath.replace(/\\/g, "/");
9765
10246
  const merged = {
9766
10247
  ...existing,
@@ -9950,22 +10431,22 @@ import { createServer, request as httpRequest } from "http";
9950
10431
  import { request as httpsRequest } from "https";
9951
10432
  import { spawn as spawn3 } from "child_process";
9952
10433
  import { URL as URL2 } from "url";
9953
- 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";
9954
10435
  import { resolve as resolve5 } from "path";
9955
- import { homedir as homedir5 } from "os";
10436
+ import { homedir as homedir7 } from "os";
9956
10437
  function findCacheFile() {
9957
- const dir = resolve5(homedir5(), ".solongate");
10438
+ const dir = resolve5(homedir7(), ".solongate");
9958
10439
  const envSel = process.env.SOLONGATE_AGENT_ID;
9959
10440
  if (envSel) {
9960
10441
  const f = resolve5(dir, ".policy-cache-" + envSel.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
9961
- if (existsSync5(f)) return f;
10442
+ if (existsSync7(f)) return f;
9962
10443
  }
9963
10444
  let best = null, bestTs = -1;
9964
10445
  try {
9965
10446
  for (const name of readdirSync(dir)) {
9966
10447
  if (name.startsWith(".policy-cache-") && name.endsWith(".json")) {
9967
10448
  const full = resolve5(dir, name);
9968
- const ts = statSync2(full).mtimeMs;
10449
+ const ts = statSync4(full).mtimeMs;
9969
10450
  if (ts > bestTs) {
9970
10451
  bestTs = ts;
9971
10452
  best = full;
@@ -9979,7 +10460,7 @@ function findCacheFile() {
9979
10460
  function loadCfg() {
9980
10461
  try {
9981
10462
  const f = findCacheFile();
9982
- if (f && existsSync5(f)) {
10463
+ if (f && existsSync7(f)) {
9983
10464
  const c2 = JSON.parse(readFileSync7(f, "utf-8"));
9984
10465
  const d = c2?.security?.dlpRedact;
9985
10466
  const g = c2?.security?.ghost;
@@ -10256,9 +10737,9 @@ __export(logs_server_exports, {
10256
10737
  runLogsServer: () => runLogsServer
10257
10738
  });
10258
10739
  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";
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";
10262
10743
  import { readdirSync as readdirSync2 } from "fs";
10263
10744
  function allowedOrigins() {
10264
10745
  const base = [
@@ -10275,15 +10756,15 @@ function resolveLocalLogDir(rawPath) {
10275
10756
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
10276
10757
  if (!dir) return null;
10277
10758
  if (isAbsolute(dir)) return dir;
10278
- return resolve6(homedir6(), ".solongate", "local-logs");
10759
+ return resolve6(homedir8(), ".solongate", "local-logs");
10279
10760
  }
10280
10761
  async function findLogDir() {
10281
- const base = resolve6(homedir6(), ".solongate");
10762
+ const base = resolve6(homedir8(), ".solongate");
10282
10763
  try {
10283
10764
  const files = readdirSync2(base).filter((f) => f.startsWith(".policy-cache-") && f.endsWith(".json"));
10284
10765
  for (const f of files) {
10285
10766
  try {
10286
- const c2 = JSON.parse(readFileSync8(join7(base, f), "utf-8"));
10767
+ const c2 = JSON.parse(readFileSync8(join9(base, f), "utf-8"));
10287
10768
  const p = c2?.security?.localLogs?.path;
10288
10769
  if (typeof p === "string" && p.trim()) return { dir: resolveLocalLogDir(p), configured: p };
10289
10770
  } catch {
@@ -10292,7 +10773,7 @@ async function findLogDir() {
10292
10773
  } catch {
10293
10774
  }
10294
10775
  try {
10295
- const cfgRaw = readFileSync8(join7(base, "cloud-guard.json"), "utf-8");
10776
+ const cfgRaw = readFileSync8(join9(base, "cloud-guard.json"), "utf-8");
10296
10777
  const { apiKey, apiUrl } = JSON.parse(cfgRaw);
10297
10778
  if (apiKey) {
10298
10779
  const url = `${apiUrl || "https://api.solongate.com"}/api/v1/policies/active`;
@@ -10321,9 +10802,9 @@ function setCors(req, res) {
10321
10802
  }
10322
10803
  function fileInfo(dir) {
10323
10804
  if (!dir) return { file: null, exists: false, size: 0, mtimeMs: 0 };
10324
- const file = join7(dir, LOG_FILENAME);
10805
+ const file = join9(dir, LOG_FILENAME);
10325
10806
  try {
10326
- const st = statSync3(file);
10807
+ const st = statSync5(file);
10327
10808
  return { file, exists: true, size: st.size, mtimeMs: st.mtimeMs };
10328
10809
  } catch {
10329
10810
  return { file, exists: false, size: 0, mtimeMs: 0 };
@@ -10434,7 +10915,7 @@ var init_logs_server = __esm({
10434
10915
 
10435
10916
  // src/inject.ts
10436
10917
  var inject_exports = {};
10437
- 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";
10438
10919
  import { resolve as resolve7 } from "path";
10439
10920
  import { execSync } from "child_process";
10440
10921
  function parseInjectArgs(argv) {
@@ -10492,7 +10973,7 @@ WHAT IT DOES
10492
10973
  `);
10493
10974
  }
10494
10975
  function detectProject() {
10495
- if (!existsSync6(resolve7("package.json"))) return false;
10976
+ if (!existsSync8(resolve7("package.json"))) return false;
10496
10977
  try {
10497
10978
  const pkg = JSON.parse(readFileSync9(resolve7("package.json"), "utf-8"));
10498
10979
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -10508,13 +10989,13 @@ function findTsEntryFile() {
10508
10989
  const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
10509
10990
  if (typeof binPath === "string") {
10510
10991
  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);
10992
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
10993
+ if (existsSync8(resolve7(binPath))) return resolve7(binPath);
10513
10994
  }
10514
10995
  }
10515
10996
  if (pkg.main) {
10516
10997
  const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
10517
- if (existsSync6(resolve7(srcPath))) return resolve7(srcPath);
10998
+ if (existsSync8(resolve7(srcPath))) return resolve7(srcPath);
10518
10999
  }
10519
11000
  } catch {
10520
11001
  }
@@ -10528,7 +11009,7 @@ function findTsEntryFile() {
10528
11009
  ];
10529
11010
  for (const c2 of candidates) {
10530
11011
  const full = resolve7(c2);
10531
- if (existsSync6(full)) {
11012
+ if (existsSync8(full)) {
10532
11013
  try {
10533
11014
  const content = readFileSync9(full, "utf-8");
10534
11015
  if (content.includes("McpServer") || content.includes("McpServer")) {
@@ -10539,13 +11020,13 @@ function findTsEntryFile() {
10539
11020
  }
10540
11021
  }
10541
11022
  for (const c2 of candidates) {
10542
- if (existsSync6(resolve7(c2))) return resolve7(c2);
11023
+ if (existsSync8(resolve7(c2))) return resolve7(c2);
10543
11024
  }
10544
11025
  return null;
10545
11026
  }
10546
11027
  function detectPackageManager() {
10547
- if (existsSync6(resolve7("pnpm-lock.yaml"))) return "pnpm";
10548
- if (existsSync6(resolve7("yarn.lock"))) return "yarn";
11028
+ if (existsSync8(resolve7("pnpm-lock.yaml"))) return "pnpm";
11029
+ if (existsSync8(resolve7("yarn.lock"))) return "yarn";
10549
11030
  return "npm";
10550
11031
  }
10551
11032
  function installSdk() {
@@ -10688,7 +11169,7 @@ async function main2() {
10688
11169
  }
10689
11170
  log3(" Language: TypeScript");
10690
11171
  const entryFile = opts.file ? resolve7(opts.file) : findTsEntryFile();
10691
- if (!entryFile || !existsSync6(entryFile)) {
11172
+ if (!entryFile || !existsSync8(entryFile)) {
10692
11173
  log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
10693
11174
  log3("");
10694
11175
  log3(" Specify it manually: --file <path>");
@@ -10701,7 +11182,7 @@ async function main2() {
10701
11182
  log3("");
10702
11183
  const backupPath = entryFile + ".solongate-backup";
10703
11184
  if (opts.restore) {
10704
- if (!existsSync6(backupPath)) {
11185
+ if (!existsSync8(backupPath)) {
10705
11186
  log3(" No backup found. Nothing to restore.");
10706
11187
  process.exit(1);
10707
11188
  }
@@ -10737,7 +11218,7 @@ async function main2() {
10737
11218
  log3(" To apply: npx @solongate/proxy inject");
10738
11219
  process.exit(0);
10739
11220
  }
10740
- if (!existsSync6(backupPath)) {
11221
+ if (!existsSync8(backupPath)) {
10741
11222
  copyFileSync(entryFile, backupPath);
10742
11223
  log3("");
10743
11224
  log3(` Backup: ${backupPath}`);
@@ -10771,8 +11252,8 @@ var init_inject = __esm({
10771
11252
 
10772
11253
  // src/create.ts
10773
11254
  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";
11255
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, existsSync as existsSync9 } from "fs";
11256
+ import { resolve as resolve8, join as join10 } from "path";
10776
11257
  import { execSync as execSync2 } from "child_process";
10777
11258
  function withSpinner(message, fn) {
10778
11259
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -10852,7 +11333,7 @@ EXAMPLES
10852
11333
  }
10853
11334
  function createProject(dir, name, _policy) {
10854
11335
  writeFileSync5(
10855
- join8(dir, "package.json"),
11336
+ join10(dir, "package.json"),
10856
11337
  JSON.stringify(
10857
11338
  {
10858
11339
  name,
@@ -10882,7 +11363,7 @@ function createProject(dir, name, _policy) {
10882
11363
  ) + "\n"
10883
11364
  );
10884
11365
  writeFileSync5(
10885
- join8(dir, "tsconfig.json"),
11366
+ join10(dir, "tsconfig.json"),
10886
11367
  JSON.stringify(
10887
11368
  {
10888
11369
  compilerOptions: {
@@ -10902,9 +11383,9 @@ function createProject(dir, name, _policy) {
10902
11383
  2
10903
11384
  ) + "\n"
10904
11385
  );
10905
- mkdirSync4(join8(dir, "src"), { recursive: true });
11386
+ mkdirSync4(join10(dir, "src"), { recursive: true });
10906
11387
  writeFileSync5(
10907
- join8(dir, "src", "index.ts"),
11388
+ join10(dir, "src", "index.ts"),
10908
11389
  `#!/usr/bin/env node
10909
11390
 
10910
11391
  console.log = (...args: unknown[]) => {
@@ -10946,7 +11427,7 @@ console.log('Press Ctrl+C to stop.');
10946
11427
  `
10947
11428
  );
10948
11429
  writeFileSync5(
10949
- join8(dir, ".mcp.json"),
11430
+ join10(dir, ".mcp.json"),
10950
11431
  JSON.stringify(
10951
11432
  {
10952
11433
  mcpServers: {
@@ -10964,12 +11445,12 @@ console.log('Press Ctrl+C to stop.');
10964
11445
  ) + "\n"
10965
11446
  );
10966
11447
  writeFileSync5(
10967
- join8(dir, ".env"),
11448
+ join10(dir, ".env"),
10968
11449
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
10969
11450
  `
10970
11451
  );
10971
11452
  writeFileSync5(
10972
- join8(dir, ".gitignore"),
11453
+ join10(dir, ".gitignore"),
10973
11454
  `node_modules/
10974
11455
  dist/
10975
11456
  *.solongate-backup
@@ -10983,7 +11464,7 @@ async function main3() {
10983
11464
  const opts = parseCreateArgs(process.argv);
10984
11465
  const dir = resolve8(opts.name);
10985
11466
  printBanner("Create MCP Server");
10986
- if (existsSync7(dir)) {
11467
+ if (existsSync9(dir)) {
10987
11468
  log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
10988
11469
  process.exit(1);
10989
11470
  }
@@ -11062,12 +11543,12 @@ var init_create = __esm({
11062
11543
 
11063
11544
  // src/pull-push.ts
11064
11545
  var pull_push_exports = {};
11065
- 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";
11066
11547
  import { resolve as resolve9 } from "path";
11067
11548
  function loadEnv() {
11068
11549
  if (process.env.SOLONGATE_API_KEY) return;
11069
11550
  const envPath = resolve9(".env");
11070
- if (!existsSync8(envPath)) return;
11551
+ if (!existsSync10(envPath)) return;
11071
11552
  try {
11072
11553
  const content = readFileSync10(envPath, "utf-8");
11073
11554
  for (const line of content.split("\n")) {
@@ -11134,7 +11615,7 @@ async function listPolicies(apiKey) {
11134
11615
  const data = await res.json();
11135
11616
  return data.policies ?? [];
11136
11617
  }
11137
- async function list3(apiKey, policyId) {
11618
+ async function list5(apiKey, policyId) {
11138
11619
  const policies = await listPolicies(apiKey);
11139
11620
  if (policies.length === 0) {
11140
11621
  log5(yellow2("No policies found. Create one in the dashboard first."));
@@ -11269,7 +11750,7 @@ async function pull(apiKey, file, policyId) {
11269
11750
  log5("");
11270
11751
  }
11271
11752
  async function push(apiKey, file, policyId) {
11272
- if (!existsSync8(file)) {
11753
+ if (!existsSync10(file)) {
11273
11754
  log5(red2(`ERROR: File not found: ${file}`));
11274
11755
  process.exit(1);
11275
11756
  }
@@ -11337,7 +11818,7 @@ async function main4() {
11337
11818
  } else if (command === "push") {
11338
11819
  await push(apiKey, file, policyId);
11339
11820
  } else if (command === "list" || command === "ls") {
11340
- await list3(apiKey, policyId);
11821
+ await list5(apiKey, policyId);
11341
11822
  } else {
11342
11823
  log5(red2(`Unknown command: ${command}`));
11343
11824
  log5("");
@@ -14402,7 +14883,7 @@ ${msg.content.text}`;
14402
14883
 
14403
14884
  // src/index.ts
14404
14885
  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"]);
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"]);
14406
14887
  var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
14407
14888
  if (!IS_HUMAN_CLI) {
14408
14889
  console.log = (...args) => {
@@ -14451,7 +14932,7 @@ async function main5() {
14451
14932
  await launchTui2();
14452
14933
  return;
14453
14934
  }
14454
- 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"]);
14455
14936
  if (MGMT_COMMANDS.has(subcommand ?? "")) {
14456
14937
  const { runCommand: runCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
14457
14938
  const code = await runCommand2(subcommand, process.argv.slice(3));