@solongate/proxy 0.33.0 → 0.35.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
@@ -274,6 +274,9 @@ function parseArgs(argv) {
274
274
  aiJudgeApiKey = process.env.GROQ_API_KEY;
275
275
  }
276
276
  }
277
+ if (aiJudgeApiKey && (aiJudgeApiKey.includes("your_") || aiJudgeApiKey.includes("_here") || aiJudgeApiKey.length < 10)) {
278
+ aiJudgeApiKey = void 0;
279
+ }
277
280
  const aiJudge = aiJudgeEnabled ? {
278
281
  enabled: true,
279
282
  model: aiJudgeModel,
@@ -533,7 +536,7 @@ function isAlreadyProtected(server) {
533
536
  }
534
537
  return false;
535
538
  }
536
- function wrapServer(serverName, server, policy, agentName) {
539
+ function wrapServer(serverName, server, policy, agentName, aiJudge) {
537
540
  const env = { ...server.env ?? {} };
538
541
  env.SOLONGATE_API_KEY = "${SOLONGATE_API_KEY}";
539
542
  const proxyArgs = ["-y", "@solongate/proxy@latest"];
@@ -543,6 +546,9 @@ function wrapServer(serverName, server, policy, agentName) {
543
546
  if (agentName) {
544
547
  proxyArgs.push("--agent-name", agentName);
545
548
  }
549
+ if (aiJudge) {
550
+ proxyArgs.push("--ai-judge");
551
+ }
546
552
  proxyArgs.push("--verbose", "--", server.command, ...server.args ?? []);
547
553
  return {
548
554
  command: "npx",
@@ -563,7 +569,8 @@ function parseInitArgs(argv) {
563
569
  const args = argv.slice(2);
564
570
  const options = {
565
571
  all: false,
566
- tools: []
572
+ tools: [],
573
+ aiJudge: false
567
574
  };
568
575
  for (let i = 0; i < args.length; i++) {
569
576
  switch (args[i]) {
@@ -588,6 +595,9 @@ function parseInitArgs(argv) {
588
595
  case "--openclaw":
589
596
  options.tools.push("openclaw");
590
597
  break;
598
+ case "--ai-judge":
599
+ options.aiJudge = true;
600
+ break;
591
601
  case "--help":
592
602
  case "-h":
593
603
  printHelp();
@@ -615,8 +625,13 @@ AI TOOL HOOKS (default: all)
615
625
  --gemini Install hooks for Gemini CLI
616
626
  --openclaw Install hooks for OpenClaw
617
627
 
628
+ SECURITY LAYERS
629
+ --ai-judge Enable AI Judge (semantic intent analysis via LLM)
630
+ Requires GROQ_API_KEY in .env (free at https://console.groq.com/keys)
631
+
618
632
  EXAMPLES
619
633
  npx @solongate/proxy init --all # Protect everything, all tools
634
+ npx @solongate/proxy init --all --ai-judge # With AI Judge enabled
620
635
  npx @solongate/proxy init --all --claude-code --gemini # Only Claude Code + Gemini hooks
621
636
  npx @solongate/proxy init --all --policy policy.json # With custom policy
622
637
  `;
@@ -793,10 +808,10 @@ function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
793
808
  }
794
809
  merged.hooks = {
795
810
  BeforeTool: [
796
- { matcher: ".*", command: guardCmd }
811
+ { matcher: ".*", hooks: [{ type: "command", command: guardCmd }] }
797
812
  ],
798
813
  AfterTool: [
799
- { matcher: ".*", command: auditCmd }
814
+ { matcher: ".*", hooks: [{ type: "command", command: auditCmd }] }
800
815
  ]
801
816
  };
802
817
  const mergedStr = JSON.stringify(merged, null, 2) + "\n";
@@ -1046,7 +1061,7 @@ async function main() {
1046
1061
  const newConfig = { mcpServers: {} };
1047
1062
  for (const name of serverNames) {
1048
1063
  if (toProtect.includes(name)) {
1049
- newConfig.mcpServers[name] = wrapServer(name, config.mcpServers[name], policyValue);
1064
+ newConfig.mcpServers[name] = wrapServer(name, config.mcpServers[name], policyValue, void 0, options.aiJudge);
1050
1065
  } else {
1051
1066
  newConfig.mcpServers[name] = config.mcpServers[name];
1052
1067
  }
@@ -5671,6 +5686,12 @@ CRITICAL: Only DENY access to files EXPLICITLY in the protected_files list. "cat
5671
5686
 
5672
5687
  Respond with ONLY valid JSON, no markdown, no explanation outside the JSON:
5673
5688
  {"decision": "ALLOW" or "DENY", "reason": "brief one-line explanation", "confidence": 0.0 to 1.0}`;
5689
+ var AuthError = class extends Error {
5690
+ constructor(message) {
5691
+ super(message);
5692
+ this.name = "AuthError";
5693
+ }
5694
+ };
5674
5695
  var AiJudge = class _AiJudge {
5675
5696
  config;
5676
5697
  protectedFiles;
@@ -5727,6 +5748,13 @@ var AiJudge = class _AiJudge {
5727
5748
  this.consecutiveFailures++;
5728
5749
  this.lastFailureTime = Date.now();
5729
5750
  const message = err instanceof Error ? err.message : String(err);
5751
+ if (err instanceof AuthError) {
5752
+ return {
5753
+ decision: "ALLOW",
5754
+ reason: `AI Judge auth error (skipping): ${message.slice(0, 100)}`,
5755
+ confidence: 0.5
5756
+ };
5757
+ }
5730
5758
  return {
5731
5759
  decision: "DENY",
5732
5760
  reason: `AI Judge error (fail-closed): ${message.slice(0, 100)}`,
@@ -5808,6 +5836,9 @@ var AiJudge = class _AiJudge {
5808
5836
  });
5809
5837
  if (!res.ok) {
5810
5838
  const errBody = await res.text().catch(() => "");
5839
+ if (res.status === 401 || res.status === 403) {
5840
+ throw new AuthError(`LLM auth failed (${res.status}): ${errBody.slice(0, 200)}`);
5841
+ }
5811
5842
  throw new Error(`LLM endpoint returned ${res.status}: ${errBody.slice(0, 200)}`);
5812
5843
  }
5813
5844
  const data = await res.json();
@@ -6048,6 +6079,9 @@ var SolonGateProxy = class {
6048
6079
  if (match) groqKey = match[1].trim();
6049
6080
  }
6050
6081
  if (!groqKey) groqKey = process.env.GROQ_API_KEY;
6082
+ if (groqKey && (groqKey.includes("your_") || groqKey.includes("_here") || groqKey.length < 10)) {
6083
+ groqKey = void 0;
6084
+ }
6051
6085
  if (groqKey) {
6052
6086
  this.config.aiJudge = {
6053
6087
  enabled: true,
package/dist/init.js CHANGED
@@ -117,7 +117,7 @@ function isAlreadyProtected(server) {
117
117
  }
118
118
  return false;
119
119
  }
120
- function wrapServer(serverName, server, policy, agentName) {
120
+ function wrapServer(serverName, server, policy, agentName, aiJudge) {
121
121
  const env = { ...server.env ?? {} };
122
122
  env.SOLONGATE_API_KEY = "${SOLONGATE_API_KEY}";
123
123
  const proxyArgs = ["-y", "@solongate/proxy@latest"];
@@ -127,6 +127,9 @@ function wrapServer(serverName, server, policy, agentName) {
127
127
  if (agentName) {
128
128
  proxyArgs.push("--agent-name", agentName);
129
129
  }
130
+ if (aiJudge) {
131
+ proxyArgs.push("--ai-judge");
132
+ }
130
133
  proxyArgs.push("--verbose", "--", server.command, ...server.args ?? []);
131
134
  return {
132
135
  command: "npx",
@@ -147,7 +150,8 @@ function parseInitArgs(argv) {
147
150
  const args = argv.slice(2);
148
151
  const options = {
149
152
  all: false,
150
- tools: []
153
+ tools: [],
154
+ aiJudge: false
151
155
  };
152
156
  for (let i = 0; i < args.length; i++) {
153
157
  switch (args[i]) {
@@ -172,6 +176,9 @@ function parseInitArgs(argv) {
172
176
  case "--openclaw":
173
177
  options.tools.push("openclaw");
174
178
  break;
179
+ case "--ai-judge":
180
+ options.aiJudge = true;
181
+ break;
175
182
  case "--help":
176
183
  case "-h":
177
184
  printHelp();
@@ -199,8 +206,13 @@ AI TOOL HOOKS (default: all)
199
206
  --gemini Install hooks for Gemini CLI
200
207
  --openclaw Install hooks for OpenClaw
201
208
 
209
+ SECURITY LAYERS
210
+ --ai-judge Enable AI Judge (semantic intent analysis via LLM)
211
+ Requires GROQ_API_KEY in .env (free at https://console.groq.com/keys)
212
+
202
213
  EXAMPLES
203
214
  npx @solongate/proxy init --all # Protect everything, all tools
215
+ npx @solongate/proxy init --all --ai-judge # With AI Judge enabled
204
216
  npx @solongate/proxy init --all --claude-code --gemini # Only Claude Code + Gemini hooks
205
217
  npx @solongate/proxy init --all --policy policy.json # With custom policy
206
218
  `;
@@ -379,10 +391,10 @@ function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
379
391
  }
380
392
  merged.hooks = {
381
393
  BeforeTool: [
382
- { matcher: ".*", command: guardCmd }
394
+ { matcher: ".*", hooks: [{ type: "command", command: guardCmd }] }
383
395
  ],
384
396
  AfterTool: [
385
- { matcher: ".*", command: auditCmd }
397
+ { matcher: ".*", hooks: [{ type: "command", command: auditCmd }] }
386
398
  ]
387
399
  };
388
400
  const mergedStr = JSON.stringify(merged, null, 2) + "\n";
@@ -632,7 +644,7 @@ async function main() {
632
644
  const newConfig = { mcpServers: {} };
633
645
  for (const name of serverNames) {
634
646
  if (toProtect.includes(name)) {
635
- newConfig.mcpServers[name] = wrapServer(name, config.mcpServers[name], policyValue);
647
+ newConfig.mcpServers[name] = wrapServer(name, config.mcpServers[name], policyValue, void 0, options.aiJudge);
636
648
  } else {
637
649
  newConfig.mcpServers[name] = config.mcpServers[name];
638
650
  }
package/dist/lib.js CHANGED
@@ -3973,6 +3973,12 @@ CRITICAL: Only DENY access to files EXPLICITLY in the protected_files list. "cat
3973
3973
 
3974
3974
  Respond with ONLY valid JSON, no markdown, no explanation outside the JSON:
3975
3975
  {"decision": "ALLOW" or "DENY", "reason": "brief one-line explanation", "confidence": 0.0 to 1.0}`;
3976
+ var AuthError = class extends Error {
3977
+ constructor(message) {
3978
+ super(message);
3979
+ this.name = "AuthError";
3980
+ }
3981
+ };
3976
3982
  var AiJudge = class _AiJudge {
3977
3983
  config;
3978
3984
  protectedFiles;
@@ -4029,6 +4035,13 @@ var AiJudge = class _AiJudge {
4029
4035
  this.consecutiveFailures++;
4030
4036
  this.lastFailureTime = Date.now();
4031
4037
  const message = err instanceof Error ? err.message : String(err);
4038
+ if (err instanceof AuthError) {
4039
+ return {
4040
+ decision: "ALLOW",
4041
+ reason: `AI Judge auth error (skipping): ${message.slice(0, 100)}`,
4042
+ confidence: 0.5
4043
+ };
4044
+ }
4032
4045
  return {
4033
4046
  decision: "DENY",
4034
4047
  reason: `AI Judge error (fail-closed): ${message.slice(0, 100)}`,
@@ -4110,6 +4123,9 @@ var AiJudge = class _AiJudge {
4110
4123
  });
4111
4124
  if (!res.ok) {
4112
4125
  const errBody = await res.text().catch(() => "");
4126
+ if (res.status === 401 || res.status === 403) {
4127
+ throw new AuthError(`LLM auth failed (${res.status}): ${errBody.slice(0, 200)}`);
4128
+ }
4113
4129
  throw new Error(`LLM endpoint returned ${res.status}: ${errBody.slice(0, 200)}`);
4114
4130
  }
4115
4131
  const data = await res.json();
@@ -4350,6 +4366,9 @@ var SolonGateProxy = class {
4350
4366
  if (match) groqKey = match[1].trim();
4351
4367
  }
4352
4368
  if (!groqKey) groqKey = process.env.GROQ_API_KEY;
4369
+ if (groqKey && (groqKey.includes("your_") || groqKey.includes("_here") || groqKey.length < 10)) {
4370
+ groqKey = void 0;
4371
+ }
4353
4372
  if (groqKey) {
4354
4373
  this.config.aiJudge = {
4355
4374
  enabled: true,
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 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
 
@@ -45,12 +44,6 @@ process.stdin.on('end', async () => {
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.gemini_version) {
50
- AGENT_ID = 'gemini-cli';
51
- AGENT_NAME = 'Gemini CLI';
52
- }
53
-
54
47
  let toolName = data.tool_name || data.toolName || '';
55
48
  let toolInput = data.tool_input || data.toolInput || data.params || {};
56
49
  if (!toolName) toolName = 'unknown';
package/hooks/guard.mjs CHANGED
@@ -41,9 +41,8 @@ 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 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
48
  // Response format depends on the agent:
@@ -366,12 +365,6 @@ process.stdin.on('end', async () => {
366
365
  try {
367
366
  const raw = JSON.parse(input);
368
367
 
369
- // Auto-detect agent from stdin data (overrides CLI args if detected)
370
- if (raw.gemini_version) {
371
- AGENT_ID = 'gemini-cli';
372
- AGENT_NAME = 'Gemini CLI';
373
- }
374
-
375
368
  // Debug: append guard invocation to debug log
376
369
  try {
377
370
  const { appendFileSync: afs, mkdirSync: mds } = await import('node:fs');
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 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,12 +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.gemini_version) { AGENT_ID = 'gemini-cli'; AGENT_NAME = 'Gemini CLI'; }
50
- } catch {}
51
-
52
45
  // Check if tool calls were made in this turn
53
46
  if (existsSync(flagFile)) {
54
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.33.0",
3
+ "version": "0.35.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": {