@solongate/proxy 0.32.0 → 0.34.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/create.js CHANGED
@@ -214,7 +214,6 @@ console.log('You need an MCP client to connect:');
214
214
  console.log('');
215
215
  console.log(' Claude Code Open this folder, .mcp.json is auto-detected');
216
216
  console.log(' Claude Desktop Add to Settings > MCP Servers');
217
- console.log(' Cursor Open this folder, .mcp.json is auto-detected');
218
217
  console.log(' Windsurf Open this folder, .mcp.json is auto-detected');
219
218
  console.log(' Cline VS Code extension, add server in settings');
220
219
  console.log(' Zed Add to settings.json under mcp_servers');
@@ -312,7 +311,7 @@ async function main() {
312
311
  bEmpty();
313
312
  log(` ${c.dim}\u251C${hr}\u2524${c.reset}`);
314
313
  bEmpty();
315
- bLine(`${c.yellow}Use with Claude Code / Cursor / MCP client:${c.reset}`);
314
+ bLine(`${c.yellow}Use with Claude Code / MCP client:${c.reset}`);
316
315
  bEmpty();
317
316
  bLine(` ${c.dim}1.${c.reset} Replace ${c.blue3}sg_live_YOUR_KEY_HERE${c.reset} in .mcp.json`);
318
317
  bLine(` ${c.dim}2.${c.reset} Open this folder in your MCP client`);
package/dist/index.js CHANGED
@@ -461,7 +461,6 @@ var init_cli_utils = __esm({
461
461
  var init_exports = {};
462
462
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
463
463
  import { resolve as resolve3, join as join2, dirname as dirname2 } from "path";
464
- import { homedir } from "os";
465
464
  import { fileURLToPath } from "url";
466
465
  import { execFileSync } from "child_process";
467
466
  import { createInterface } from "readline";
@@ -580,24 +579,15 @@ function parseInitArgs(argv) {
580
579
  case "--all":
581
580
  options.all = true;
582
581
  break;
583
- case "--claude":
584
- options.tools.push("claude");
585
- break;
586
- case "--cursor":
587
- options.tools.push("cursor");
582
+ case "--claude-code":
583
+ options.tools.push("claude-code");
588
584
  break;
589
585
  case "--gemini":
590
586
  options.tools.push("gemini");
591
587
  break;
592
- case "--antigravity":
593
- options.tools.push("antigravity");
594
- break;
595
588
  case "--openclaw":
596
589
  options.tools.push("openclaw");
597
590
  break;
598
- case "--perplexity":
599
- options.tools.push("perplexity");
600
- break;
601
591
  case "--help":
602
592
  case "-h":
603
593
  printHelp();
@@ -621,16 +611,13 @@ OPTIONS
621
611
  -h, --help Show this help message
622
612
 
623
613
  AI TOOL HOOKS (default: all)
624
- --claude Install hooks for Claude Code
625
- --cursor Install hooks for Cursor
614
+ --claude-code Install hooks for Claude Code
626
615
  --gemini Install hooks for Gemini CLI
627
- --antigravity Install hooks for Antigravity
628
616
  --openclaw Install hooks for OpenClaw
629
- --perplexity Install hooks for Perplexity
630
617
 
631
618
  EXAMPLES
632
619
  npx @solongate/proxy init --all # Protect everything, all tools
633
- npx @solongate/proxy init --all --claude --cursor # Only Claude + Cursor hooks
620
+ npx @solongate/proxy init --all --claude-code --gemini # Only Claude Code + Gemini hooks
634
621
  npx @solongate/proxy init --all --policy policy.json # With custom policy
635
622
  `;
636
623
  console.log(help);
@@ -639,7 +626,7 @@ function readHookScript(filename) {
639
626
  return readFileSync4(join2(HOOKS_DIR, filename), "utf-8");
640
627
  }
641
628
  function unlockProtectedDirs() {
642
- const dirs = [".solongate", ".claude", ".cursor", ".gemini", ".antigravity", ".openclaw", ".perplexity"];
629
+ const dirs = [".solongate", ".claude", ".gemini", ".openclaw"];
643
630
  for (const dir of dirs) {
644
631
  const fullDir = resolve3(dir);
645
632
  if (!existsSync4(fullDir)) continue;
@@ -716,12 +703,9 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
716
703
  console.log(` Hook files already up to date`);
717
704
  }
718
705
  const allClients = [
719
- { name: "Claude Code", dir: ".claude", key: "claude", agentId: "claude-code", agentName: "Claude Code" },
720
- { name: "Cursor", dir: ".cursor", key: "cursor", agentId: "cursor", agentName: "Cursor" },
706
+ { name: "Claude Code", dir: ".claude", key: "claude-code", agentId: "claude-code", agentName: "Claude Code" },
721
707
  { name: "Gemini CLI", dir: ".gemini", key: "gemini", agentId: "gemini-cli", agentName: "Gemini CLI" },
722
- { name: "Antigravity", dir: ".antigravity", key: "antigravity", agentId: "antigravity", agentName: "Antigravity" },
723
- { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" },
724
- { name: "Perplexity", dir: ".perplexity", key: "perplexity", agentId: "perplexity", agentName: "Perplexity" }
708
+ { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" }
725
709
  ];
726
710
  const clients = selectedTools.length > 0 ? allClients.filter((c3) => selectedTools.includes(c3.key)) : allClients;
727
711
  const activatedNames = [];
@@ -732,11 +716,7 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
732
716
  const guardCmd = `node .solongate/hooks/guard.mjs ${client.agentId} "${client.agentName}"`;
733
717
  const auditCmd = `node .solongate/hooks/audit.mjs ${client.agentId} "${client.agentName}"`;
734
718
  const stopCmd = `node .solongate/hooks/stop.mjs ${client.agentId} "${client.agentName}"`;
735
- if (client.key === "cursor") {
736
- const result = installCursorConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
737
- if (result === "skipped") skippedNames.push(client.name);
738
- else activatedNames.push(client.name);
739
- } else if (client.key === "gemini") {
719
+ if (client.key === "gemini") {
740
720
  const result = installGeminiConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
741
721
  if (result === "skipped") skippedNames.push(client.name);
742
722
  else activatedNames.push(client.name);
@@ -788,90 +768,6 @@ function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, st
788
768
  console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
789
769
  return "installed";
790
770
  }
791
- function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
792
- let changed = false;
793
- const hookConfig = {
794
- preToolUse: [
795
- { matcher: "", command: guardCmd, failClosed: true }
796
- ],
797
- postToolUse: [
798
- { matcher: "", command: auditCmd }
799
- ],
800
- beforeShellExecution: [
801
- { matcher: "", command: guardCmd, failClosed: true }
802
- ],
803
- afterShellExecution: [
804
- { matcher: "", command: auditCmd }
805
- ],
806
- beforeReadFile: [
807
- { matcher: "", command: guardCmd, failClosed: true }
808
- ],
809
- afterFileEdit: [
810
- { matcher: "", command: auditCmd }
811
- ],
812
- beforeMCPExecution: [
813
- { matcher: "", command: guardCmd, failClosed: true }
814
- ],
815
- afterMCPExecution: [
816
- { matcher: "", command: auditCmd }
817
- ]
818
- };
819
- const hooksContent = JSON.stringify({ version: 1, hooks: hookConfig }, null, 2) + "\n";
820
- const userCursorDir = join2(homedir(), ".cursor");
821
- if (existsSync4(userCursorDir)) {
822
- const userHooksPath = join2(userCursorDir, "hooks.json");
823
- const existingUserHooks = existsSync4(userHooksPath) ? readFileSync4(userHooksPath, "utf-8") : "";
824
- if (hooksContent !== existingUserHooks) {
825
- writeFileSync2(userHooksPath, hooksContent);
826
- console.log(` ${existingUserHooks ? "Updated" : "Created"} ${userHooksPath} (user-level)`);
827
- changed = true;
828
- }
829
- }
830
- const hooksPath = join2(clientDir, "hooks.json");
831
- const existingHooks = existsSync4(hooksPath) ? readFileSync4(hooksPath, "utf-8") : "";
832
- if (hooksContent !== existingHooks) {
833
- writeFileSync2(hooksPath, hooksContent);
834
- console.log(` ${existingHooks ? "Updated" : "Created"} ${hooksPath}`);
835
- changed = true;
836
- }
837
- if (wrappedMcpConfig) {
838
- const cursorMcpConfig = { mcpServers: {} };
839
- for (const [name, server] of Object.entries(wrappedMcpConfig.mcpServers)) {
840
- const args = [...server.args ?? []];
841
- const agentIdx = args.indexOf("--agent-name");
842
- if (agentIdx >= 0 && agentIdx + 1 < args.length) {
843
- args[agentIdx + 1] = "cursor";
844
- } else {
845
- const sepIdx = args.indexOf("--");
846
- if (sepIdx >= 0) {
847
- args.splice(sepIdx, 0, "--agent-name", "cursor");
848
- }
849
- }
850
- cursorMcpConfig.mcpServers[name] = { ...server, args };
851
- }
852
- const mcpPath = join2(clientDir, "mcp.json");
853
- const mcpStr = JSON.stringify(cursorMcpConfig, null, 2) + "\n";
854
- const existingMcp = existsSync4(mcpPath) ? readFileSync4(mcpPath, "utf-8") : "";
855
- if (mcpStr !== existingMcp) {
856
- writeFileSync2(mcpPath, mcpStr);
857
- console.log(` ${existingMcp ? "Updated" : "Created"} ${mcpPath}`);
858
- changed = true;
859
- }
860
- }
861
- const staleSettings = join2(clientDir, "settings.json");
862
- if (existsSync4(staleSettings)) {
863
- try {
864
- const content = JSON.parse(readFileSync4(staleSettings, "utf-8"));
865
- if (content.hooks?.PreToolUse || content.hooks?.PostToolUse) {
866
- writeFileSync2(staleSettings, JSON.stringify({}, null, 2) + "\n");
867
- console.log(` Cleaned stale ${staleSettings} (had wrong hook format)`);
868
- changed = true;
869
- }
870
- } catch {
871
- }
872
- }
873
- return changed ? "installed" : "skipped";
874
- }
875
771
  function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
876
772
  const settingsPath = join2(clientDir, "settings.json");
877
773
  let existing = {};
@@ -897,10 +793,10 @@ function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
897
793
  }
898
794
  merged.hooks = {
899
795
  BeforeTool: [
900
- { matcher: ".*", command: guardCmd }
796
+ { matcher: ".*", hooks: [{ type: "command", command: guardCmd }] }
901
797
  ],
902
798
  AfterTool: [
903
- { matcher: ".*", command: auditCmd }
799
+ { matcher: ".*", hooks: [{ type: "command", command: auditCmd }] }
904
800
  ]
905
801
  };
906
802
  const mergedStr = JSON.stringify(merged, null, 2) + "\n";
@@ -965,11 +861,8 @@ SOLONGATE_API_KEY=sg_live_your_key_here
965
861
  ".mcp.json",
966
862
  ".solongate/**",
967
863
  ".claude/**",
968
- ".cursor/**",
969
864
  ".gemini/**",
970
- ".antigravity/**",
971
- ".openclaw/**",
972
- ".perplexity/**"
865
+ ".openclaw/**"
973
866
  ];
974
867
  if (existsSync4(gitignorePath)) {
975
868
  let gitignore = readFileSync4(gitignorePath, "utf-8");
@@ -1206,8 +1099,7 @@ async function main() {
1206
1099
  console.log(" \u2502 \u2502");
1207
1100
  console.log(" \u2502 MCP servers \u2192 Protected via proxy \u2502");
1208
1101
  console.log(" \u2502 AI tools \u2192 Guarded via hooks \u2502");
1209
- console.log(" \u2502 Claude, Cursor, Gemini, Antigravity, \u2502");
1210
- console.log(" \u2502 OpenClaw, Perplexity \u2502");
1102
+ console.log(" \u2502 Claude Code, Gemini, OpenClaw \u2502");
1211
1103
  console.log(" \u2502 API key \u2192 Set in .env \u2502");
1212
1104
  console.log(" \u2502 \u2502");
1213
1105
  console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
@@ -1749,7 +1641,6 @@ console.log('You need an MCP client to connect:');
1749
1641
  console.log('');
1750
1642
  console.log(' Claude Code Open this folder, .mcp.json is auto-detected');
1751
1643
  console.log(' Claude Desktop Add to Settings > MCP Servers');
1752
- console.log(' Cursor Open this folder, .mcp.json is auto-detected');
1753
1644
  console.log(' Windsurf Open this folder, .mcp.json is auto-detected');
1754
1645
  console.log(' Cline VS Code extension, add server in settings');
1755
1646
  console.log(' Zed Add to settings.json under mcp_servers');
@@ -1847,7 +1738,7 @@ async function main3() {
1847
1738
  bEmpty();
1848
1739
  log3(` ${c.dim}\u251C${hr}\u2524${c.reset}`);
1849
1740
  bEmpty();
1850
- bLine(`${c.yellow}Use with Claude Code / Cursor / MCP client:${c.reset}`);
1741
+ bLine(`${c.yellow}Use with Claude Code / MCP client:${c.reset}`);
1851
1742
  bEmpty();
1852
1743
  bLine(` ${c.dim}1.${c.reset} Replace ${c.blue3}sg_live_YOUR_KEY_HERE${c.reset} in .mcp.json`);
1853
1744
  bLine(` ${c.dim}2.${c.reset} Open this folder in your MCP client`);
@@ -6062,12 +5953,9 @@ var SolonGateProxy = class {
6062
5953
  /** Normalize well-known MCP client names to display-friendly agent identities */
6063
5954
  normalizeAgentName(raw) {
6064
5955
  const lower = raw.toLowerCase();
6065
- if (lower.includes("cursor")) return { id: "cursor", name: "Cursor" };
6066
5956
  if (lower.includes("claude-code") || lower === "claude code") return { id: "claude-code", name: "Claude Code" };
6067
5957
  if (lower.includes("claude")) return { id: "claude-desktop", name: "Claude Desktop" };
6068
5958
  if (lower.includes("gemini")) return { id: "gemini-cli", name: "Gemini CLI" };
6069
- if (lower.includes("antigravity")) return { id: "antigravity", name: "Antigravity" };
6070
- if (lower.includes("perplexity")) return { id: "perplexity", name: "Perplexity" };
6071
5959
  return { id: raw.toLowerCase().replace(/\s+/g, "-"), name: raw };
6072
5960
  }
6073
5961
  /** Extract sub-agent identity from MCP _meta field */
@@ -6710,7 +6598,7 @@ ${msg.content.text}`;
6710
6598
  /**
6711
6599
  * Start serving downstream.
6712
6600
  * If --port is set, serves via StreamableHTTP on that port.
6713
- * Otherwise, serves on stdio (default for Claude Code / Cursor / etc).
6601
+ * Otherwise, serves on stdio (default for Claude Code / etc).
6714
6602
  */
6715
6603
  async serve() {
6716
6604
  if (!this.server) throw new Error("Server not created");
package/dist/init.js CHANGED
@@ -3,7 +3,6 @@
3
3
  // src/init.ts
4
4
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
5
  import { resolve, join, dirname } from "path";
6
- import { homedir } from "os";
7
6
  import { fileURLToPath } from "url";
8
7
  import { execFileSync } from "child_process";
9
8
  import { createInterface } from "readline";
@@ -164,24 +163,15 @@ function parseInitArgs(argv) {
164
163
  case "--all":
165
164
  options.all = true;
166
165
  break;
167
- case "--claude":
168
- options.tools.push("claude");
169
- break;
170
- case "--cursor":
171
- options.tools.push("cursor");
166
+ case "--claude-code":
167
+ options.tools.push("claude-code");
172
168
  break;
173
169
  case "--gemini":
174
170
  options.tools.push("gemini");
175
171
  break;
176
- case "--antigravity":
177
- options.tools.push("antigravity");
178
- break;
179
172
  case "--openclaw":
180
173
  options.tools.push("openclaw");
181
174
  break;
182
- case "--perplexity":
183
- options.tools.push("perplexity");
184
- break;
185
175
  case "--help":
186
176
  case "-h":
187
177
  printHelp();
@@ -205,16 +195,13 @@ OPTIONS
205
195
  -h, --help Show this help message
206
196
 
207
197
  AI TOOL HOOKS (default: all)
208
- --claude Install hooks for Claude Code
209
- --cursor Install hooks for Cursor
198
+ --claude-code Install hooks for Claude Code
210
199
  --gemini Install hooks for Gemini CLI
211
- --antigravity Install hooks for Antigravity
212
200
  --openclaw Install hooks for OpenClaw
213
- --perplexity Install hooks for Perplexity
214
201
 
215
202
  EXAMPLES
216
203
  npx @solongate/proxy init --all # Protect everything, all tools
217
- npx @solongate/proxy init --all --claude --cursor # Only Claude + Cursor hooks
204
+ npx @solongate/proxy init --all --claude-code --gemini # Only Claude Code + Gemini hooks
218
205
  npx @solongate/proxy init --all --policy policy.json # With custom policy
219
206
  `;
220
207
  console.log(help);
@@ -225,7 +212,7 @@ function readHookScript(filename) {
225
212
  return readFileSync(join(HOOKS_DIR, filename), "utf-8");
226
213
  }
227
214
  function unlockProtectedDirs() {
228
- const dirs = [".solongate", ".claude", ".cursor", ".gemini", ".antigravity", ".openclaw", ".perplexity"];
215
+ const dirs = [".solongate", ".claude", ".gemini", ".openclaw"];
229
216
  for (const dir of dirs) {
230
217
  const fullDir = resolve(dir);
231
218
  if (!existsSync(fullDir)) continue;
@@ -302,12 +289,9 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
302
289
  console.log(` Hook files already up to date`);
303
290
  }
304
291
  const allClients = [
305
- { name: "Claude Code", dir: ".claude", key: "claude", agentId: "claude-code", agentName: "Claude Code" },
306
- { name: "Cursor", dir: ".cursor", key: "cursor", agentId: "cursor", agentName: "Cursor" },
292
+ { name: "Claude Code", dir: ".claude", key: "claude-code", agentId: "claude-code", agentName: "Claude Code" },
307
293
  { name: "Gemini CLI", dir: ".gemini", key: "gemini", agentId: "gemini-cli", agentName: "Gemini CLI" },
308
- { name: "Antigravity", dir: ".antigravity", key: "antigravity", agentId: "antigravity", agentName: "Antigravity" },
309
- { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" },
310
- { name: "Perplexity", dir: ".perplexity", key: "perplexity", agentId: "perplexity", agentName: "Perplexity" }
294
+ { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" }
311
295
  ];
312
296
  const clients = selectedTools.length > 0 ? allClients.filter((c2) => selectedTools.includes(c2.key)) : allClients;
313
297
  const activatedNames = [];
@@ -318,11 +302,7 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
318
302
  const guardCmd = `node .solongate/hooks/guard.mjs ${client.agentId} "${client.agentName}"`;
319
303
  const auditCmd = `node .solongate/hooks/audit.mjs ${client.agentId} "${client.agentName}"`;
320
304
  const stopCmd = `node .solongate/hooks/stop.mjs ${client.agentId} "${client.agentName}"`;
321
- if (client.key === "cursor") {
322
- const result = installCursorConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
323
- if (result === "skipped") skippedNames.push(client.name);
324
- else activatedNames.push(client.name);
325
- } else if (client.key === "gemini") {
305
+ if (client.key === "gemini") {
326
306
  const result = installGeminiConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
327
307
  if (result === "skipped") skippedNames.push(client.name);
328
308
  else activatedNames.push(client.name);
@@ -374,90 +354,6 @@ function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, st
374
354
  console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
375
355
  return "installed";
376
356
  }
377
- function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
378
- let changed = false;
379
- const hookConfig = {
380
- preToolUse: [
381
- { matcher: "", command: guardCmd, failClosed: true }
382
- ],
383
- postToolUse: [
384
- { matcher: "", command: auditCmd }
385
- ],
386
- beforeShellExecution: [
387
- { matcher: "", command: guardCmd, failClosed: true }
388
- ],
389
- afterShellExecution: [
390
- { matcher: "", command: auditCmd }
391
- ],
392
- beforeReadFile: [
393
- { matcher: "", command: guardCmd, failClosed: true }
394
- ],
395
- afterFileEdit: [
396
- { matcher: "", command: auditCmd }
397
- ],
398
- beforeMCPExecution: [
399
- { matcher: "", command: guardCmd, failClosed: true }
400
- ],
401
- afterMCPExecution: [
402
- { matcher: "", command: auditCmd }
403
- ]
404
- };
405
- const hooksContent = JSON.stringify({ version: 1, hooks: hookConfig }, null, 2) + "\n";
406
- const userCursorDir = join(homedir(), ".cursor");
407
- if (existsSync(userCursorDir)) {
408
- const userHooksPath = join(userCursorDir, "hooks.json");
409
- const existingUserHooks = existsSync(userHooksPath) ? readFileSync(userHooksPath, "utf-8") : "";
410
- if (hooksContent !== existingUserHooks) {
411
- writeFileSync(userHooksPath, hooksContent);
412
- console.log(` ${existingUserHooks ? "Updated" : "Created"} ${userHooksPath} (user-level)`);
413
- changed = true;
414
- }
415
- }
416
- const hooksPath = join(clientDir, "hooks.json");
417
- const existingHooks = existsSync(hooksPath) ? readFileSync(hooksPath, "utf-8") : "";
418
- if (hooksContent !== existingHooks) {
419
- writeFileSync(hooksPath, hooksContent);
420
- console.log(` ${existingHooks ? "Updated" : "Created"} ${hooksPath}`);
421
- changed = true;
422
- }
423
- if (wrappedMcpConfig) {
424
- const cursorMcpConfig = { mcpServers: {} };
425
- for (const [name, server] of Object.entries(wrappedMcpConfig.mcpServers)) {
426
- const args = [...server.args ?? []];
427
- const agentIdx = args.indexOf("--agent-name");
428
- if (agentIdx >= 0 && agentIdx + 1 < args.length) {
429
- args[agentIdx + 1] = "cursor";
430
- } else {
431
- const sepIdx = args.indexOf("--");
432
- if (sepIdx >= 0) {
433
- args.splice(sepIdx, 0, "--agent-name", "cursor");
434
- }
435
- }
436
- cursorMcpConfig.mcpServers[name] = { ...server, args };
437
- }
438
- const mcpPath = join(clientDir, "mcp.json");
439
- const mcpStr = JSON.stringify(cursorMcpConfig, null, 2) + "\n";
440
- const existingMcp = existsSync(mcpPath) ? readFileSync(mcpPath, "utf-8") : "";
441
- if (mcpStr !== existingMcp) {
442
- writeFileSync(mcpPath, mcpStr);
443
- console.log(` ${existingMcp ? "Updated" : "Created"} ${mcpPath}`);
444
- changed = true;
445
- }
446
- }
447
- const staleSettings = join(clientDir, "settings.json");
448
- if (existsSync(staleSettings)) {
449
- try {
450
- const content = JSON.parse(readFileSync(staleSettings, "utf-8"));
451
- if (content.hooks?.PreToolUse || content.hooks?.PostToolUse) {
452
- writeFileSync(staleSettings, JSON.stringify({}, null, 2) + "\n");
453
- console.log(` Cleaned stale ${staleSettings} (had wrong hook format)`);
454
- changed = true;
455
- }
456
- } catch {
457
- }
458
- }
459
- return changed ? "installed" : "skipped";
460
- }
461
357
  function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
462
358
  const settingsPath = join(clientDir, "settings.json");
463
359
  let existing = {};
@@ -483,10 +379,10 @@ function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
483
379
  }
484
380
  merged.hooks = {
485
381
  BeforeTool: [
486
- { matcher: ".*", command: guardCmd }
382
+ { matcher: ".*", hooks: [{ type: "command", command: guardCmd }] }
487
383
  ],
488
384
  AfterTool: [
489
- { matcher: ".*", command: auditCmd }
385
+ { matcher: ".*", hooks: [{ type: "command", command: auditCmd }] }
490
386
  ]
491
387
  };
492
388
  const mergedStr = JSON.stringify(merged, null, 2) + "\n";
@@ -551,11 +447,8 @@ SOLONGATE_API_KEY=sg_live_your_key_here
551
447
  ".mcp.json",
552
448
  ".solongate/**",
553
449
  ".claude/**",
554
- ".cursor/**",
555
450
  ".gemini/**",
556
- ".antigravity/**",
557
- ".openclaw/**",
558
- ".perplexity/**"
451
+ ".openclaw/**"
559
452
  ];
560
453
  if (existsSync(gitignorePath)) {
561
454
  let gitignore = readFileSync(gitignorePath, "utf-8");
@@ -792,8 +685,7 @@ async function main() {
792
685
  console.log(" \u2502 \u2502");
793
686
  console.log(" \u2502 MCP servers \u2192 Protected via proxy \u2502");
794
687
  console.log(" \u2502 AI tools \u2192 Guarded via hooks \u2502");
795
- console.log(" \u2502 Claude, Cursor, Gemini, Antigravity, \u2502");
796
- console.log(" \u2502 OpenClaw, Perplexity \u2502");
688
+ console.log(" \u2502 Claude Code, Gemini, OpenClaw \u2502");
797
689
  console.log(" \u2502 API key \u2192 Set in .env \u2502");
798
690
  console.log(" \u2502 \u2502");
799
691
  console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
package/dist/lib.js CHANGED
@@ -4255,12 +4255,9 @@ var SolonGateProxy = class {
4255
4255
  /** Normalize well-known MCP client names to display-friendly agent identities */
4256
4256
  normalizeAgentName(raw) {
4257
4257
  const lower = raw.toLowerCase();
4258
- if (lower.includes("cursor")) return { id: "cursor", name: "Cursor" };
4259
4258
  if (lower.includes("claude-code") || lower === "claude code") return { id: "claude-code", name: "Claude Code" };
4260
4259
  if (lower.includes("claude")) return { id: "claude-desktop", name: "Claude Desktop" };
4261
4260
  if (lower.includes("gemini")) return { id: "gemini-cli", name: "Gemini CLI" };
4262
- if (lower.includes("antigravity")) return { id: "antigravity", name: "Antigravity" };
4263
- if (lower.includes("perplexity")) return { id: "perplexity", name: "Perplexity" };
4264
4261
  return { id: raw.toLowerCase().replace(/\s+/g, "-"), name: raw };
4265
4262
  }
4266
4263
  /** Extract sub-agent identity from MCP _meta field */
@@ -4903,7 +4900,7 @@ ${msg.content.text}`;
4903
4900
  /**
4904
4901
  * Start serving downstream.
4905
4902
  * If --port is set, serves via StreamableHTTP on that port.
4906
- * Otherwise, serves on stdio (default for Claude Code / Cursor / etc).
4903
+ * Otherwise, serves on stdio (default for Claude Code / etc).
4907
4904
  */
4908
4905
  async serve() {
4909
4906
  if (!this.server) throw new Error("Server not created");
package/hooks/audit.mjs CHANGED
@@ -26,9 +26,8 @@ const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
26
26
  const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
27
27
 
28
28
  // Agent identity from CLI args: node audit.mjs <agent_id> <agent_name>
29
- // Can be overridden at runtime when stdin contains cursor_version or gemini_version
30
- let AGENT_ID = process.argv[2] || 'claude-code';
31
- let AGENT_NAME = process.argv[3] || 'Claude Code';
29
+ const AGENT_ID = process.argv[2] || 'claude-code';
30
+ const AGENT_NAME = process.argv[3] || 'Claude Code';
32
31
 
33
32
  if (!API_KEY || !API_KEY.startsWith('sg_live_')) process.exit(0);
34
33
 
@@ -40,48 +39,13 @@ process.stdin.on('end', async () => {
40
39
 
41
40
  // Debug: append raw stdin to file for agent detection troubleshooting
42
41
  try {
43
- const debugLine = JSON.stringify({ ts: new Date().toISOString(), argv: process.argv.slice(2), cursor_version: data.cursor_version || null, tool_name: data.tool_name || data.toolName, agent_id: AGENT_ID, hook_event_name: data.hook_event_name || null }) + '\n';
42
+ const debugLine = JSON.stringify({ ts: new Date().toISOString(), argv: process.argv.slice(2), tool_name: data.tool_name || data.toolName, agent_id: AGENT_ID }) + '\n';
44
43
  const { appendFileSync: afs } = await import('node:fs');
45
44
  afs(resolve('.solongate', '.debug-audit-log'), debugLine);
46
45
  } catch {}
47
46
 
48
- // Auto-detect agent from stdin data (overrides CLI args if detected)
49
- if (data.cursor_version) {
50
- AGENT_ID = 'cursor';
51
- AGENT_NAME = 'Cursor';
52
- } else if (data.gemini_version) {
53
- AGENT_ID = 'gemini-cli';
54
- AGENT_NAME = 'Gemini CLI';
55
- }
56
-
57
- const hookEvent = data.hook_event_name || '';
58
-
59
- // Map Cursor-specific hook events to standard tool_name/tool_input
60
47
  let toolName = data.tool_name || data.toolName || '';
61
48
  let toolInput = data.tool_input || data.toolInput || data.params || {};
62
-
63
- if (!toolName && hookEvent) {
64
- switch (hookEvent) {
65
- case 'afterShellExecution':
66
- toolName = 'Shell';
67
- toolInput = { command: data.command || '' };
68
- break;
69
- case 'afterFileEdit':
70
- toolName = 'Edit';
71
- toolInput = { file_path: data.file_path || '' };
72
- break;
73
- case 'afterMCPExecution':
74
- toolName = data.tool_name || data.toolName || `mcp:${data.server_name || 'unknown'}`;
75
- toolInput = data.arguments || {};
76
- break;
77
- case 'postToolUse':
78
- case 'postToolUseFailure':
79
- toolName = data.tool_name || data.toolName || 'unknown';
80
- break;
81
- default:
82
- break;
83
- }
84
- }
85
49
  if (!toolName) toolName = 'unknown';
86
50
 
87
51
  if (toolName === 'Bash' && JSON.stringify(toolInput).includes('audit-logs')) {
@@ -101,7 +65,6 @@ process.stdin.on('end', async () => {
101
65
  }
102
66
  } catch {}
103
67
 
104
- // Cursor uses tool_output, Claude uses tool_response
105
68
  const toolResponse = data.tool_response || data.toolResponse || {};
106
69
  const toolOutput = data.tool_output || data.toolOutput || '';
107
70
  const resultJson = data.result_json ? (typeof data.result_json === 'string' ? data.result_json : JSON.stringify(data.result_json)) : '';
package/hooks/guard.mjs CHANGED
@@ -41,60 +41,30 @@ const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
41
41
  const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
42
42
 
43
43
  // Agent identity from CLI args: node guard.mjs <agent_id> <agent_name>
44
- // Can be overridden at runtime when stdin contains cursor_version or gemini_version
45
- let AGENT_ID = process.argv[2] || 'claude-code';
46
- let AGENT_NAME = process.argv[3] || 'Claude Code';
44
+ const AGENT_ID = process.argv[2] || 'claude-code';
45
+ const AGENT_NAME = process.argv[3] || 'Claude Code';
47
46
 
48
47
  // ── Per-tool block/allow output ──
49
- // Response format depends on BOTH the agent AND the hook event type:
48
+ // Response format depends on the agent:
50
49
  // Claude Code: exit 2 + stderr = BLOCK, exit 0 = ALLOW
51
- // Cursor preToolUse: {"permission": "deny/allow", "user_message": "..."}
52
- // Cursor before*: {"continue": false/true, "userMessage": "..."}
53
50
  // Gemini CLI: {"decision": "deny/allow", "reason": "..."}
54
- let HOOK_EVENT = ''; // set from stdin hook_event_name
55
- const CURSOR_BEFORE_EVENTS = new Set([
56
- 'beforeShellExecution', 'beforeReadFile', 'beforeMCPExecution',
57
- ]);
58
51
 
59
52
  function blockTool(reason) {
60
- if (AGENT_ID === 'cursor') {
61
- if (CURSOR_BEFORE_EVENTS.has(HOOK_EVENT)) {
62
- // Cursor before* events use continue: false
63
- process.stdout.write(JSON.stringify({
64
- continue: false,
65
- userMessage: `[SolonGate] ${reason}`,
66
- agentMessage: `BLOCKED by SolonGate security policy: ${reason}`,
67
- }));
68
- } else {
69
- // Cursor preToolUse uses permission: deny
70
- process.stdout.write(JSON.stringify({
71
- permission: 'deny',
72
- user_message: `[SolonGate] ${reason}`,
73
- agent_message: `BLOCKED by SolonGate security policy: ${reason}`,
74
- }));
75
- }
76
- process.exit(0);
77
- } else if (AGENT_ID === 'gemini-cli') {
53
+ if (AGENT_ID === 'gemini-cli') {
78
54
  process.stdout.write(JSON.stringify({
79
55
  decision: 'deny',
80
56
  reason: `[SolonGate] ${reason}`,
81
57
  }));
82
58
  process.exit(0);
83
59
  } else {
84
- // Claude Code, Antigravity, Perplexity — exit code 2
60
+ // Claude Code — exit code 2
85
61
  process.stderr.write(reason);
86
62
  process.exit(2);
87
63
  }
88
64
  }
89
65
 
90
66
  function allowTool() {
91
- if (AGENT_ID === 'cursor') {
92
- if (CURSOR_BEFORE_EVENTS.has(HOOK_EVENT)) {
93
- process.stdout.write(JSON.stringify({ continue: true }));
94
- } else {
95
- process.stdout.write(JSON.stringify({ permission: 'allow' }));
96
- }
97
- } else if (AGENT_ID === 'gemini-cli') {
67
+ if (AGENT_ID === 'gemini-cli') {
98
68
  process.stdout.write(JSON.stringify({ decision: 'allow' }));
99
69
  }
100
70
  process.exit(0);
@@ -395,63 +365,17 @@ process.stdin.on('end', async () => {
395
365
  try {
396
366
  const raw = JSON.parse(input);
397
367
 
398
- // Auto-detect agent from stdin data (overrides CLI args if detected)
399
- if (raw.cursor_version) {
400
- AGENT_ID = 'cursor';
401
- AGENT_NAME = 'Cursor';
402
- } else if (raw.gemini_version) {
403
- AGENT_ID = 'gemini-cli';
404
- AGENT_NAME = 'Gemini CLI';
405
- }
406
-
407
- // Track hook event name for response format selection
408
- HOOK_EVENT = raw.hook_event_name || '';
409
-
410
368
  // Debug: append guard invocation to debug log
411
369
  try {
412
370
  const { appendFileSync: afs, mkdirSync: mds } = await import('node:fs');
413
371
  mds(resolve('.solongate'), { recursive: true });
414
- const debugLine = JSON.stringify({ ts: new Date().toISOString(), hook: 'guard', argv: process.argv.slice(2), cursor_version: raw.cursor_version || null, tool_name: raw.tool_name || raw.toolName || raw.command, agent_id: AGENT_ID, hook_event_name: HOOK_EVENT }) + '\n';
372
+ const debugLine = JSON.stringify({ ts: new Date().toISOString(), hook: 'guard', argv: process.argv.slice(2), tool_name: raw.tool_name || raw.toolName || raw.command, agent_id: AGENT_ID }) + '\n';
415
373
  afs(resolve('.solongate', '.debug-guard-log'), debugLine);
416
374
  } catch {}
417
375
 
418
- // Map Cursor-specific hook events to standard tool_name/tool_input format
419
- // beforeShellExecution: {command, cwd} → tool_name=Shell, tool_input={command}
420
- // beforeReadFile: {file_path} → tool_name=Read, tool_input={file_path}
421
- // afterFileEdit: {file_path, new_content} → tool_name=Edit, tool_input={file_path}
422
- // beforeMCPExecution: {server_name, tool_name, arguments} → tool_name=<mcp_tool>, tool_input=arguments
423
376
  let mappedToolName = raw.tool_name || raw.toolName || '';
424
377
  let mappedToolInput = raw.tool_input || raw.toolInput || raw.params || {};
425
378
 
426
- if (!mappedToolName && HOOK_EVENT) {
427
- switch (HOOK_EVENT) {
428
- case 'beforeShellExecution':
429
- mappedToolName = 'Shell';
430
- mappedToolInput = { command: raw.command || '', cwd: raw.cwd || '' };
431
- break;
432
- case 'afterShellExecution':
433
- mappedToolName = 'Shell';
434
- mappedToolInput = { command: raw.command || '', cwd: raw.cwd || '' };
435
- break;
436
- case 'beforeReadFile':
437
- mappedToolName = 'Read';
438
- mappedToolInput = { file_path: raw.file_path || '' };
439
- break;
440
- case 'afterFileEdit':
441
- mappedToolName = 'Edit';
442
- mappedToolInput = { file_path: raw.file_path || '', new_content: raw.new_content || '' };
443
- break;
444
- case 'beforeMCPExecution':
445
- mappedToolName = raw.tool_name || raw.toolName || `mcp:${raw.server_name || 'unknown'}`;
446
- mappedToolInput = raw.arguments || raw.tool_input || {};
447
- break;
448
- case 'afterMCPExecution':
449
- mappedToolName = raw.tool_name || raw.toolName || `mcp:${raw.server_name || 'unknown'}`;
450
- mappedToolInput = raw.arguments || raw.tool_input || {};
451
- break;
452
- }
453
- }
454
-
455
379
  // Normalize field names across tools
456
380
  const data = {
457
381
  ...raw,
@@ -467,7 +391,7 @@ process.stdin.on('end', async () => {
467
391
  // Hardcoded, no bypass possible — runs before policy/PI config
468
392
  // Fully protected: block ALL access (read, write, delete, move)
469
393
  const protectedPaths = [
470
- '.solongate', '.claude', '.cursor', '.gemini', '.antigravity', '.openclaw', '.perplexity',
394
+ '.solongate', '.claude', '.gemini', '.openclaw',
471
395
  'policy.json', '.mcp.json',
472
396
  ];
473
397
  // Write-protected: block write/delete/modify, allow read (cat, grep, head, etc.)
package/hooks/stop.mjs CHANGED
@@ -27,9 +27,8 @@ const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
27
27
  const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
28
28
 
29
29
  // Agent identity from CLI args: node stop.mjs <agent_id> <agent_name>
30
- // Can be overridden at runtime when stdin contains cursor_version or gemini_version
31
- let AGENT_ID = process.argv[2] || 'claude-code';
32
- let AGENT_NAME = process.argv[3] || 'Claude Code';
30
+ const AGENT_ID = process.argv[2] || 'claude-code';
31
+ const AGENT_NAME = process.argv[3] || 'Claude Code';
33
32
 
34
33
  if (!API_KEY || !API_KEY.startsWith('sg_live_')) process.exit(0);
35
34
 
@@ -43,13 +42,6 @@ let input = '';
43
42
  process.stdin.on('data', c => input += c);
44
43
  process.stdin.on('end', async () => {
45
44
  try {
46
- // Auto-detect agent from stdin data
47
- try {
48
- const raw = JSON.parse(input);
49
- if (raw.cursor_version) { AGENT_ID = 'cursor'; AGENT_NAME = 'Cursor'; }
50
- else if (raw.gemini_version) { AGENT_ID = 'gemini-cli'; AGENT_NAME = 'Gemini CLI'; }
51
- } catch {}
52
-
53
45
  // Check if tool calls were made in this turn
54
46
  if (existsSync(flagFile)) {
55
47
  // Tool calls happened → audit.mjs already logged them. Clean up flag.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "AI tool security proxy — protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {