@solongate/proxy 0.4.9 → 0.5.1

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.
Files changed (3) hide show
  1. package/dist/index.js +106 -92
  2. package/dist/init.js +104 -90
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -542,9 +542,9 @@ function findConfigFile(explicitPath, createIfMissing = false) {
542
542
  };
543
543
  const starterPath = resolve2(".mcp.json");
544
544
  writeFileSync2(starterPath, JSON.stringify(starterConfig, null, 2) + "\n");
545
- console.error(" Created .mcp.json with starter servers (filesystem, playwright).");
546
- console.error("");
547
- return { path: starterPath, type: "mcp" };
545
+ console.log(" Created .mcp.json with starter servers (filesystem, playwright).");
546
+ console.log("");
547
+ return { path: starterPath, type: "mcp", created: true };
548
548
  }
549
549
  for (const desktopPath of CLAUDE_DESKTOP_PATHS) {
550
550
  if (existsSync3(desktopPath)) return { path: desktopPath, type: "claude-desktop" };
@@ -569,10 +569,8 @@ function isAlreadyProtected(server) {
569
569
  }
570
570
  return false;
571
571
  }
572
- function wrapServer(server, policy, apiKey) {
573
- const env = { ...server.env ?? {} };
574
- env.SOLONGATE_API_KEY = apiKey;
575
- return {
572
+ function wrapServer(server, policy) {
573
+ const result = {
576
574
  command: "npx",
577
575
  args: [
578
576
  "-y",
@@ -583,9 +581,12 @@ function wrapServer(server, policy, apiKey) {
583
581
  "--",
584
582
  server.command,
585
583
  ...server.args ?? []
586
- ],
587
- env
584
+ ]
588
585
  };
586
+ if (server.env && Object.keys(server.env).length > 0) {
587
+ result.env = { ...server.env };
588
+ }
589
+ return result;
589
590
  }
590
591
  async function prompt(question) {
591
592
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -632,8 +633,8 @@ function parseInitArgs(argv) {
632
633
  }
633
634
  if (!POLICY_PRESETS.includes(options.policy)) {
634
635
  if (!existsSync3(resolve2(options.policy))) {
635
- console.error(`Unknown policy: ${options.policy}`);
636
- console.error(`Available presets: ${POLICY_PRESETS.join(", ")}`);
636
+ console.log(`Unknown policy: ${options.policy}`);
637
+ console.log(`Available presets: ${POLICY_PRESETS.join(", ")}`);
637
638
  process.exit(1);
638
639
  }
639
640
  }
@@ -669,17 +670,17 @@ POLICY PRESETS
669
670
  permissive Allow everything (monitoring + audit only)
670
671
  deny-all Block all tool calls
671
672
  `;
672
- console.error(help);
673
+ console.log(help);
673
674
  }
674
675
  function installClaudeCodeHooks(apiKey) {
675
676
  const hooksDir = resolve2(".claude", "hooks");
676
677
  mkdirSync(hooksDir, { recursive: true });
677
678
  const guardPath = join(hooksDir, "guard.mjs");
678
679
  writeFileSync2(guardPath, GUARD_SCRIPT);
679
- console.error(` Created ${guardPath}`);
680
+ console.log(` Created ${guardPath}`);
680
681
  const auditPath = join(hooksDir, "audit.mjs");
681
682
  writeFileSync2(auditPath, AUDIT_SCRIPT);
682
- console.error(` Created ${auditPath}`);
683
+ console.log(` Created ${auditPath}`);
683
684
  const settingsPath = resolve2(".claude", "settings.json");
684
685
  let settings = {};
685
686
  if (existsSync3(settingsPath)) {
@@ -719,29 +720,26 @@ function installClaudeCodeHooks(apiKey) {
719
720
  envObj.SOLONGATE_API_KEY = apiKey;
720
721
  settings.env = envObj;
721
722
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
722
- console.error(` Created ${settingsPath}`);
723
- console.error("");
724
- console.error(" Claude Code hooks installed!");
725
- console.error(" PreToolUse \u2192 guard.mjs (blocks dangerous calls)");
726
- console.error(" PostToolUse \u2192 audit.mjs (logs all calls to dashboard)");
723
+ console.log(` Created ${settingsPath}`);
724
+ console.log("");
725
+ console.log(" Claude Code hooks installed!");
726
+ console.log(" PreToolUse \u2192 guard.mjs (blocks dangerous calls)");
727
+ console.log(" PostToolUse \u2192 audit.mjs (logs all calls to dashboard)");
727
728
  }
728
729
  function ensureEnvFile() {
729
730
  const envPath = resolve2(".env");
730
731
  if (!existsSync3(envPath)) {
731
- const envContent = `# SolonGate API Keys
732
- # Get your keys from the dashboard: https://solongate.com
732
+ const envContent = `# SolonGate Configuration
733
+ # Get your API key from: https://dashboard.solongate.com/api-keys
733
734
  # IMPORTANT: Never commit this file to git!
734
735
 
735
- # Live key \u2014 enables cloud policy sync + audit logging to dashboard
736
+ # Your SolonGate API key \u2014 the proxy reads this automatically
736
737
  SOLONGATE_API_KEY=sg_live_your_key_here
737
-
738
- # Test key \u2014 local-only, no cloud connection (for development)
739
- # SOLONGATE_API_KEY=sg_test_your_key_here
740
738
  `;
741
739
  writeFileSync2(envPath, envContent);
742
- console.error(` Created .env with placeholder API keys`);
743
- console.error(` \u2192 Edit .env and replace with your real keys from https://solongate.com`);
744
- console.error("");
740
+ console.log(` Created .env`);
741
+ console.log(` \u2192 Set your API key in .env (get one at https://dashboard.solongate.com)`);
742
+ console.log("");
745
743
  }
746
744
  const gitignorePath = resolve2(".gitignore");
747
745
  if (existsSync3(gitignorePath)) {
@@ -757,11 +755,11 @@ SOLONGATE_API_KEY=sg_live_your_key_here
757
755
  }
758
756
  if (updated) {
759
757
  writeFileSync2(gitignorePath, gitignore);
760
- console.error(` Updated .gitignore (added .env + .mcp.json)`);
758
+ console.log(` Updated .gitignore (added .env + .mcp.json)`);
761
759
  }
762
760
  } else {
763
761
  writeFileSync2(gitignorePath, ".env\n.env.local\n.mcp.json\nnode_modules/\n");
764
- console.error(` Created .gitignore (with .env and .mcp.json excluded)`);
762
+ console.log(` Created .gitignore (with .env and .mcp.json excluded)`);
765
763
  }
766
764
  }
767
765
  async function main() {
@@ -787,57 +785,60 @@ async function main() {
787
785
  " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
788
786
  ];
789
787
  const bannerColors = [_c.blue1, _c.blue2, _c.blue3, _c.blue4, _c.blue5, _c.blue6];
790
- console.error("");
788
+ console.log("");
791
789
  for (let i = 0; i < bannerLines.length; i++) {
792
- console.error(`${_c.bold}${bannerColors[i]}${bannerLines[i]}${_c.reset}`);
790
+ console.log(`${_c.bold}${bannerColors[i]}${bannerLines[i]}${_c.reset}`);
793
791
  }
794
- console.error("");
795
- console.error(` ${_c.dim}${_c.italic}Init Setup${_c.reset}`);
796
- console.error("");
792
+ console.log("");
793
+ console.log(` ${_c.dim}${_c.italic}Init Setup${_c.reset}`);
794
+ console.log("");
795
+ await sleep(400);
797
796
  const configInfo = findConfigFile(options.configPath, true);
798
797
  if (!configInfo) {
799
- console.error(" No MCP config found and could not create one.");
798
+ console.log(" No MCP config found and could not create one.");
800
799
  process.exit(1);
801
800
  }
802
- console.error(` Config: ${configInfo.path}`);
803
- console.error(` Type: ${configInfo.type === "claude-desktop" ? "Claude Desktop" : "MCP JSON"}`);
804
- console.error("");
801
+ console.log(` Config: ${configInfo.path}`);
802
+ console.log(` Type: ${configInfo.type === "claude-desktop" ? "Claude Desktop" : "MCP JSON"}`);
803
+ console.log("");
805
804
  const backupPath = configInfo.path + ".solongate-backup";
806
805
  if (options.restore) {
807
806
  if (!existsSync3(backupPath)) {
808
- console.error(" No backup found. Nothing to restore.");
807
+ console.log(" No backup found. Nothing to restore.");
809
808
  process.exit(1);
810
809
  }
811
810
  copyFileSync(backupPath, configInfo.path);
812
- console.error(" Restored original config from backup.");
811
+ console.log(" Restored original config from backup.");
813
812
  process.exit(0);
814
813
  }
815
814
  const config = readConfig(configInfo.path);
816
815
  const serverNames = Object.keys(config.mcpServers);
817
816
  if (serverNames.length === 0) {
818
- console.error(" No MCP servers found in config.");
817
+ console.log(" No MCP servers found in config.");
819
818
  process.exit(1);
820
819
  }
821
- console.error(` Found ${serverNames.length} MCP server(s):`);
822
- console.error("");
820
+ await sleep(300);
821
+ console.log(` Found ${serverNames.length} MCP server(s):`);
822
+ console.log("");
823
823
  const toProtect = [];
824
824
  const alreadyProtected = [];
825
825
  const skipped = [];
826
826
  for (const name of serverNames) {
827
827
  const server = config.mcpServers[name];
828
+ await sleep(200);
828
829
  if (isAlreadyProtected(server)) {
829
830
  alreadyProtected.push(name);
830
- console.error(` [protected] ${name}`);
831
+ console.log(` [protected] ${name}`);
831
832
  } else {
832
- console.error(` [exposed] ${name} \u2192 ${server.command} ${(server.args ?? []).join(" ")}`);
833
+ console.log(` [exposed] ${name} \u2192 ${server.command} ${(server.args ?? []).join(" ")}`);
833
834
  if (options.all) {
834
835
  toProtect.push(name);
835
836
  }
836
837
  }
837
838
  }
838
- console.error("");
839
+ console.log("");
839
840
  if (alreadyProtected.length === serverNames.length) {
840
- console.error(" All servers are already protected by SolonGate!");
841
+ console.log(" All servers are already protected by SolonGate!");
841
842
  ensureEnvFile();
842
843
  process.exit(0);
843
844
  }
@@ -853,11 +854,14 @@ async function main() {
853
854
  }
854
855
  }
855
856
  if (toProtect.length === 0) {
856
- console.error(" No servers selected for protection.");
857
+ console.log(" No servers selected for protection.");
857
858
  ensureEnvFile();
858
859
  process.exit(0);
859
860
  }
860
- console.error("");
861
+ console.log("");
862
+ await sleep(300);
863
+ ensureEnvFile();
864
+ console.log("");
861
865
  let apiKey = options.apiKey || process.env.SOLONGATE_API_KEY || "";
862
866
  if (!apiKey) {
863
867
  const envPath = resolve2(".env");
@@ -870,43 +874,47 @@ async function main() {
870
874
  if (!apiKey || apiKey === "sg_live_your_key_here") {
871
875
  if (options.all) {
872
876
  apiKey = "sg_test_demo_init_key";
873
- console.error(" No API key found \u2014 using demo test key.");
874
- console.error(" For cloud features, set your key in .env or pass --api-key");
875
- console.error("");
877
+ console.log(" No API key found \u2014 using demo test key.");
878
+ console.log(" Set your real key in .env for cloud features.");
879
+ console.log("");
876
880
  } else {
877
881
  apiKey = await prompt(" Enter your SolonGate API key (from https://dashboard.solongate.com): ");
878
882
  }
879
883
  if (!apiKey) {
880
- console.error(" API key is required. Get one at https://dashboard.solongate.com");
884
+ console.log(" API key is required. Get one at https://dashboard.solongate.com");
881
885
  process.exit(1);
882
886
  }
883
887
  }
884
888
  if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
885
- console.error(" Invalid API key format. Must start with sg_live_ or sg_test_");
889
+ console.log(" Invalid API key format. Must start with sg_live_ or sg_test_");
886
890
  process.exit(1);
887
891
  }
888
- console.error(` Policy: ${options.policy}`);
889
- console.error(` API Key: ${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`);
890
- console.error(` Protecting: ${toProtect.join(", ")}`);
891
- console.error("");
892
+ await sleep(300);
893
+ console.log(` Policy: ${options.policy}`);
894
+ await sleep(150);
895
+ console.log(` API Key: ${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`);
896
+ await sleep(150);
897
+ console.log(` Protecting: ${toProtect.join(", ")}`);
898
+ console.log("");
892
899
  const newConfig = { mcpServers: {} };
893
900
  for (const name of serverNames) {
894
901
  if (toProtect.includes(name)) {
895
- newConfig.mcpServers[name] = wrapServer(config.mcpServers[name], options.policy, apiKey);
902
+ newConfig.mcpServers[name] = wrapServer(config.mcpServers[name], options.policy);
896
903
  } else {
897
904
  newConfig.mcpServers[name] = config.mcpServers[name];
898
905
  }
899
906
  }
900
907
  if (options.dryRun) {
901
- console.error(" --- DRY RUN (no changes written) ---");
902
- console.error("");
903
- console.error(" New config:");
904
- console.error(JSON.stringify(newConfig, null, 2));
908
+ console.log(" --- DRY RUN (no changes written) ---");
909
+ console.log("");
910
+ console.log(" New config:");
911
+ console.log(JSON.stringify(newConfig, null, 2));
905
912
  process.exit(0);
906
913
  }
907
- if (!existsSync3(backupPath)) {
914
+ await sleep(400);
915
+ if (!configInfo.created && !existsSync3(backupPath)) {
908
916
  copyFileSync(configInfo.path, backupPath);
909
- console.error(` Backup: ${backupPath}`);
917
+ console.log(` Backup: ${backupPath}`);
910
918
  }
911
919
  if (configInfo.type === "claude-desktop") {
912
920
  const original = JSON.parse(readFileSync3(configInfo.path, "utf-8"));
@@ -915,38 +923,43 @@ async function main() {
915
923
  } else {
916
924
  writeFileSync2(configInfo.path, JSON.stringify(newConfig, null, 2) + "\n");
917
925
  }
918
- console.error(" Config updated!");
919
- console.error("");
926
+ await sleep(300);
927
+ console.log(" Config updated!");
928
+ console.log("");
929
+ await sleep(500);
920
930
  installClaudeCodeHooks(apiKey);
921
- console.error("");
922
- ensureEnvFile();
923
- console.error("");
924
- console.error(" \u2500\u2500 Summary \u2500\u2500");
925
- console.error("");
931
+ console.log("");
932
+ await sleep(400);
933
+ console.log(" \u2500\u2500 Summary \u2500\u2500");
934
+ console.log("");
926
935
  for (const name of toProtect) {
927
- console.error(` \u2713 ${name} \u2014 protected (${options.policy})`);
936
+ await sleep(200);
937
+ console.log(` \u2713 ${name} \u2014 protected (${options.policy})`);
928
938
  }
929
939
  for (const name of alreadyProtected) {
930
- console.error(` \u25CF ${name} \u2014 already protected`);
940
+ await sleep(200);
941
+ console.log(` \u25CF ${name} \u2014 already protected`);
931
942
  }
932
943
  for (const name of skipped) {
933
- console.error(` \u25CB ${name} \u2014 skipped`);
934
- }
935
- console.error("");
936
- console.error(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
937
- console.error(" \u2502 Setup complete! \u2502");
938
- console.error(" \u2502 \u2502");
939
- console.error(" \u2502 MCP tools \u2192 Proxy audit logging \u2502");
940
- console.error(" \u2502 Built-in tools \u2192 Hook audit logging \u2502");
941
- console.error(" \u2502 \u2502");
942
- console.error(" \u2502 View logs: https://dashboard.solongate.com \u2502");
943
- console.error(" \u2502 To undo: solongate-init --restore \u2502");
944
- console.error(" \u2502 \u2502");
945
- console.error(" \u2502 Restart your MCP client to apply changes. \u2502");
946
- console.error(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
947
- console.error("");
944
+ await sleep(200);
945
+ console.log(` \u25CB ${name} \u2014 skipped`);
946
+ }
947
+ console.log("");
948
+ await sleep(500);
949
+ console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
950
+ console.log(" \u2502 Setup complete! \u2502");
951
+ console.log(" \u2502 \u2502");
952
+ console.log(" \u2502 MCP tools \u2192 Proxy audit logging \u2502");
953
+ console.log(" \u2502 Built-in tools \u2192 Hook audit logging \u2502");
954
+ console.log(" \u2502 \u2502");
955
+ console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
956
+ console.log(" \u2502 To undo: solongate-init --restore \u2502");
957
+ console.log(" \u2502 \u2502");
958
+ console.log(" \u2502 Restart your MCP client to apply changes. \u2502");
959
+ console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
960
+ console.log("");
948
961
  }
949
- var POLICY_PRESETS, SEARCH_PATHS, CLAUDE_DESKTOP_PATHS, GUARD_SCRIPT, AUDIT_SCRIPT;
962
+ var POLICY_PRESETS, SEARCH_PATHS, CLAUDE_DESKTOP_PATHS, sleep, GUARD_SCRIPT, AUDIT_SCRIPT;
950
963
  var init_init = __esm({
951
964
  "src/init.ts"() {
952
965
  "use strict";
@@ -957,6 +970,7 @@ var init_init = __esm({
957
970
  ".claude/mcp.json"
958
971
  ];
959
972
  CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
973
+ sleep = (ms) => new Promise((r) => setTimeout(r, ms));
960
974
  GUARD_SCRIPT = `#!/usr/bin/env node
961
975
  /**
962
976
  * SolonGate Guard Hook for Claude Code (PreToolUse)
@@ -1164,7 +1178,7 @@ process.stdin.on('end', async () => {
1164
1178
  });
1165
1179
  `;
1166
1180
  main().catch((err) => {
1167
- console.error(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
1181
+ console.log(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
1168
1182
  process.exit(1);
1169
1183
  });
1170
1184
  }
package/dist/init.js CHANGED
@@ -11,6 +11,7 @@ var SEARCH_PATHS = [
11
11
  ".claude/mcp.json"
12
12
  ];
13
13
  var CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
14
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
14
15
  function findConfigFile(explicitPath, createIfMissing = false) {
15
16
  if (explicitPath) {
16
17
  if (existsSync(explicitPath)) {
@@ -37,9 +38,9 @@ function findConfigFile(explicitPath, createIfMissing = false) {
37
38
  };
38
39
  const starterPath = resolve(".mcp.json");
39
40
  writeFileSync(starterPath, JSON.stringify(starterConfig, null, 2) + "\n");
40
- console.error(" Created .mcp.json with starter servers (filesystem, playwright).");
41
- console.error("");
42
- return { path: starterPath, type: "mcp" };
41
+ console.log(" Created .mcp.json with starter servers (filesystem, playwright).");
42
+ console.log("");
43
+ return { path: starterPath, type: "mcp", created: true };
43
44
  }
44
45
  for (const desktopPath of CLAUDE_DESKTOP_PATHS) {
45
46
  if (existsSync(desktopPath)) return { path: desktopPath, type: "claude-desktop" };
@@ -64,10 +65,8 @@ function isAlreadyProtected(server) {
64
65
  }
65
66
  return false;
66
67
  }
67
- function wrapServer(server, policy, apiKey) {
68
- const env = { ...server.env ?? {} };
69
- env.SOLONGATE_API_KEY = apiKey;
70
- return {
68
+ function wrapServer(server, policy) {
69
+ const result = {
71
70
  command: "npx",
72
71
  args: [
73
72
  "-y",
@@ -78,9 +77,12 @@ function wrapServer(server, policy, apiKey) {
78
77
  "--",
79
78
  server.command,
80
79
  ...server.args ?? []
81
- ],
82
- env
80
+ ]
83
81
  };
82
+ if (server.env && Object.keys(server.env).length > 0) {
83
+ result.env = { ...server.env };
84
+ }
85
+ return result;
84
86
  }
85
87
  async function prompt(question) {
86
88
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -127,8 +129,8 @@ function parseInitArgs(argv) {
127
129
  }
128
130
  if (!POLICY_PRESETS.includes(options.policy)) {
129
131
  if (!existsSync(resolve(options.policy))) {
130
- console.error(`Unknown policy: ${options.policy}`);
131
- console.error(`Available presets: ${POLICY_PRESETS.join(", ")}`);
132
+ console.log(`Unknown policy: ${options.policy}`);
133
+ console.log(`Available presets: ${POLICY_PRESETS.join(", ")}`);
132
134
  process.exit(1);
133
135
  }
134
136
  }
@@ -164,7 +166,7 @@ POLICY PRESETS
164
166
  permissive Allow everything (monitoring + audit only)
165
167
  deny-all Block all tool calls
166
168
  `;
167
- console.error(help);
169
+ console.log(help);
168
170
  }
169
171
  var GUARD_SCRIPT = `#!/usr/bin/env node
170
172
  /**
@@ -377,10 +379,10 @@ function installClaudeCodeHooks(apiKey) {
377
379
  mkdirSync(hooksDir, { recursive: true });
378
380
  const guardPath = join(hooksDir, "guard.mjs");
379
381
  writeFileSync(guardPath, GUARD_SCRIPT);
380
- console.error(` Created ${guardPath}`);
382
+ console.log(` Created ${guardPath}`);
381
383
  const auditPath = join(hooksDir, "audit.mjs");
382
384
  writeFileSync(auditPath, AUDIT_SCRIPT);
383
- console.error(` Created ${auditPath}`);
385
+ console.log(` Created ${auditPath}`);
384
386
  const settingsPath = resolve(".claude", "settings.json");
385
387
  let settings = {};
386
388
  if (existsSync(settingsPath)) {
@@ -420,29 +422,26 @@ function installClaudeCodeHooks(apiKey) {
420
422
  envObj.SOLONGATE_API_KEY = apiKey;
421
423
  settings.env = envObj;
422
424
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
423
- console.error(` Created ${settingsPath}`);
424
- console.error("");
425
- console.error(" Claude Code hooks installed!");
426
- console.error(" PreToolUse \u2192 guard.mjs (blocks dangerous calls)");
427
- console.error(" PostToolUse \u2192 audit.mjs (logs all calls to dashboard)");
425
+ console.log(` Created ${settingsPath}`);
426
+ console.log("");
427
+ console.log(" Claude Code hooks installed!");
428
+ console.log(" PreToolUse \u2192 guard.mjs (blocks dangerous calls)");
429
+ console.log(" PostToolUse \u2192 audit.mjs (logs all calls to dashboard)");
428
430
  }
429
431
  function ensureEnvFile() {
430
432
  const envPath = resolve(".env");
431
433
  if (!existsSync(envPath)) {
432
- const envContent = `# SolonGate API Keys
433
- # Get your keys from the dashboard: https://solongate.com
434
+ const envContent = `# SolonGate Configuration
435
+ # Get your API key from: https://dashboard.solongate.com/api-keys
434
436
  # IMPORTANT: Never commit this file to git!
435
437
 
436
- # Live key \u2014 enables cloud policy sync + audit logging to dashboard
438
+ # Your SolonGate API key \u2014 the proxy reads this automatically
437
439
  SOLONGATE_API_KEY=sg_live_your_key_here
438
-
439
- # Test key \u2014 local-only, no cloud connection (for development)
440
- # SOLONGATE_API_KEY=sg_test_your_key_here
441
440
  `;
442
441
  writeFileSync(envPath, envContent);
443
- console.error(` Created .env with placeholder API keys`);
444
- console.error(` \u2192 Edit .env and replace with your real keys from https://solongate.com`);
445
- console.error("");
442
+ console.log(` Created .env`);
443
+ console.log(` \u2192 Set your API key in .env (get one at https://dashboard.solongate.com)`);
444
+ console.log("");
446
445
  }
447
446
  const gitignorePath = resolve(".gitignore");
448
447
  if (existsSync(gitignorePath)) {
@@ -458,11 +457,11 @@ SOLONGATE_API_KEY=sg_live_your_key_here
458
457
  }
459
458
  if (updated) {
460
459
  writeFileSync(gitignorePath, gitignore);
461
- console.error(` Updated .gitignore (added .env + .mcp.json)`);
460
+ console.log(` Updated .gitignore (added .env + .mcp.json)`);
462
461
  }
463
462
  } else {
464
463
  writeFileSync(gitignorePath, ".env\n.env.local\n.mcp.json\nnode_modules/\n");
465
- console.error(` Created .gitignore (with .env and .mcp.json excluded)`);
464
+ console.log(` Created .gitignore (with .env and .mcp.json excluded)`);
466
465
  }
467
466
  }
468
467
  async function main() {
@@ -488,57 +487,60 @@ async function main() {
488
487
  " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
489
488
  ];
490
489
  const bannerColors = [_c.blue1, _c.blue2, _c.blue3, _c.blue4, _c.blue5, _c.blue6];
491
- console.error("");
490
+ console.log("");
492
491
  for (let i = 0; i < bannerLines.length; i++) {
493
- console.error(`${_c.bold}${bannerColors[i]}${bannerLines[i]}${_c.reset}`);
492
+ console.log(`${_c.bold}${bannerColors[i]}${bannerLines[i]}${_c.reset}`);
494
493
  }
495
- console.error("");
496
- console.error(` ${_c.dim}${_c.italic}Init Setup${_c.reset}`);
497
- console.error("");
494
+ console.log("");
495
+ console.log(` ${_c.dim}${_c.italic}Init Setup${_c.reset}`);
496
+ console.log("");
497
+ await sleep(400);
498
498
  const configInfo = findConfigFile(options.configPath, true);
499
499
  if (!configInfo) {
500
- console.error(" No MCP config found and could not create one.");
500
+ console.log(" No MCP config found and could not create one.");
501
501
  process.exit(1);
502
502
  }
503
- console.error(` Config: ${configInfo.path}`);
504
- console.error(` Type: ${configInfo.type === "claude-desktop" ? "Claude Desktop" : "MCP JSON"}`);
505
- console.error("");
503
+ console.log(` Config: ${configInfo.path}`);
504
+ console.log(` Type: ${configInfo.type === "claude-desktop" ? "Claude Desktop" : "MCP JSON"}`);
505
+ console.log("");
506
506
  const backupPath = configInfo.path + ".solongate-backup";
507
507
  if (options.restore) {
508
508
  if (!existsSync(backupPath)) {
509
- console.error(" No backup found. Nothing to restore.");
509
+ console.log(" No backup found. Nothing to restore.");
510
510
  process.exit(1);
511
511
  }
512
512
  copyFileSync(backupPath, configInfo.path);
513
- console.error(" Restored original config from backup.");
513
+ console.log(" Restored original config from backup.");
514
514
  process.exit(0);
515
515
  }
516
516
  const config = readConfig(configInfo.path);
517
517
  const serverNames = Object.keys(config.mcpServers);
518
518
  if (serverNames.length === 0) {
519
- console.error(" No MCP servers found in config.");
519
+ console.log(" No MCP servers found in config.");
520
520
  process.exit(1);
521
521
  }
522
- console.error(` Found ${serverNames.length} MCP server(s):`);
523
- console.error("");
522
+ await sleep(300);
523
+ console.log(` Found ${serverNames.length} MCP server(s):`);
524
+ console.log("");
524
525
  const toProtect = [];
525
526
  const alreadyProtected = [];
526
527
  const skipped = [];
527
528
  for (const name of serverNames) {
528
529
  const server = config.mcpServers[name];
530
+ await sleep(200);
529
531
  if (isAlreadyProtected(server)) {
530
532
  alreadyProtected.push(name);
531
- console.error(` [protected] ${name}`);
533
+ console.log(` [protected] ${name}`);
532
534
  } else {
533
- console.error(` [exposed] ${name} \u2192 ${server.command} ${(server.args ?? []).join(" ")}`);
535
+ console.log(` [exposed] ${name} \u2192 ${server.command} ${(server.args ?? []).join(" ")}`);
534
536
  if (options.all) {
535
537
  toProtect.push(name);
536
538
  }
537
539
  }
538
540
  }
539
- console.error("");
541
+ console.log("");
540
542
  if (alreadyProtected.length === serverNames.length) {
541
- console.error(" All servers are already protected by SolonGate!");
543
+ console.log(" All servers are already protected by SolonGate!");
542
544
  ensureEnvFile();
543
545
  process.exit(0);
544
546
  }
@@ -554,11 +556,14 @@ async function main() {
554
556
  }
555
557
  }
556
558
  if (toProtect.length === 0) {
557
- console.error(" No servers selected for protection.");
559
+ console.log(" No servers selected for protection.");
558
560
  ensureEnvFile();
559
561
  process.exit(0);
560
562
  }
561
- console.error("");
563
+ console.log("");
564
+ await sleep(300);
565
+ ensureEnvFile();
566
+ console.log("");
562
567
  let apiKey = options.apiKey || process.env.SOLONGATE_API_KEY || "";
563
568
  if (!apiKey) {
564
569
  const envPath = resolve(".env");
@@ -571,43 +576,47 @@ async function main() {
571
576
  if (!apiKey || apiKey === "sg_live_your_key_here") {
572
577
  if (options.all) {
573
578
  apiKey = "sg_test_demo_init_key";
574
- console.error(" No API key found \u2014 using demo test key.");
575
- console.error(" For cloud features, set your key in .env or pass --api-key");
576
- console.error("");
579
+ console.log(" No API key found \u2014 using demo test key.");
580
+ console.log(" Set your real key in .env for cloud features.");
581
+ console.log("");
577
582
  } else {
578
583
  apiKey = await prompt(" Enter your SolonGate API key (from https://dashboard.solongate.com): ");
579
584
  }
580
585
  if (!apiKey) {
581
- console.error(" API key is required. Get one at https://dashboard.solongate.com");
586
+ console.log(" API key is required. Get one at https://dashboard.solongate.com");
582
587
  process.exit(1);
583
588
  }
584
589
  }
585
590
  if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
586
- console.error(" Invalid API key format. Must start with sg_live_ or sg_test_");
591
+ console.log(" Invalid API key format. Must start with sg_live_ or sg_test_");
587
592
  process.exit(1);
588
593
  }
589
- console.error(` Policy: ${options.policy}`);
590
- console.error(` API Key: ${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`);
591
- console.error(` Protecting: ${toProtect.join(", ")}`);
592
- console.error("");
594
+ await sleep(300);
595
+ console.log(` Policy: ${options.policy}`);
596
+ await sleep(150);
597
+ console.log(` API Key: ${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`);
598
+ await sleep(150);
599
+ console.log(` Protecting: ${toProtect.join(", ")}`);
600
+ console.log("");
593
601
  const newConfig = { mcpServers: {} };
594
602
  for (const name of serverNames) {
595
603
  if (toProtect.includes(name)) {
596
- newConfig.mcpServers[name] = wrapServer(config.mcpServers[name], options.policy, apiKey);
604
+ newConfig.mcpServers[name] = wrapServer(config.mcpServers[name], options.policy);
597
605
  } else {
598
606
  newConfig.mcpServers[name] = config.mcpServers[name];
599
607
  }
600
608
  }
601
609
  if (options.dryRun) {
602
- console.error(" --- DRY RUN (no changes written) ---");
603
- console.error("");
604
- console.error(" New config:");
605
- console.error(JSON.stringify(newConfig, null, 2));
610
+ console.log(" --- DRY RUN (no changes written) ---");
611
+ console.log("");
612
+ console.log(" New config:");
613
+ console.log(JSON.stringify(newConfig, null, 2));
606
614
  process.exit(0);
607
615
  }
608
- if (!existsSync(backupPath)) {
616
+ await sleep(400);
617
+ if (!configInfo.created && !existsSync(backupPath)) {
609
618
  copyFileSync(configInfo.path, backupPath);
610
- console.error(` Backup: ${backupPath}`);
619
+ console.log(` Backup: ${backupPath}`);
611
620
  }
612
621
  if (configInfo.type === "claude-desktop") {
613
622
  const original = JSON.parse(readFileSync(configInfo.path, "utf-8"));
@@ -616,38 +625,43 @@ async function main() {
616
625
  } else {
617
626
  writeFileSync(configInfo.path, JSON.stringify(newConfig, null, 2) + "\n");
618
627
  }
619
- console.error(" Config updated!");
620
- console.error("");
628
+ await sleep(300);
629
+ console.log(" Config updated!");
630
+ console.log("");
631
+ await sleep(500);
621
632
  installClaudeCodeHooks(apiKey);
622
- console.error("");
623
- ensureEnvFile();
624
- console.error("");
625
- console.error(" \u2500\u2500 Summary \u2500\u2500");
626
- console.error("");
633
+ console.log("");
634
+ await sleep(400);
635
+ console.log(" \u2500\u2500 Summary \u2500\u2500");
636
+ console.log("");
627
637
  for (const name of toProtect) {
628
- console.error(` \u2713 ${name} \u2014 protected (${options.policy})`);
638
+ await sleep(200);
639
+ console.log(` \u2713 ${name} \u2014 protected (${options.policy})`);
629
640
  }
630
641
  for (const name of alreadyProtected) {
631
- console.error(` \u25CF ${name} \u2014 already protected`);
642
+ await sleep(200);
643
+ console.log(` \u25CF ${name} \u2014 already protected`);
632
644
  }
633
645
  for (const name of skipped) {
634
- console.error(` \u25CB ${name} \u2014 skipped`);
646
+ await sleep(200);
647
+ console.log(` \u25CB ${name} \u2014 skipped`);
635
648
  }
636
- console.error("");
637
- console.error(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
638
- console.error(" \u2502 Setup complete! \u2502");
639
- console.error(" \u2502 \u2502");
640
- console.error(" \u2502 MCP tools \u2192 Proxy audit logging \u2502");
641
- console.error(" \u2502 Built-in tools \u2192 Hook audit logging \u2502");
642
- console.error(" \u2502 \u2502");
643
- console.error(" \u2502 View logs: https://dashboard.solongate.com \u2502");
644
- console.error(" \u2502 To undo: solongate-init --restore \u2502");
645
- console.error(" \u2502 \u2502");
646
- console.error(" \u2502 Restart your MCP client to apply changes. \u2502");
647
- console.error(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
648
- console.error("");
649
+ console.log("");
650
+ await sleep(500);
651
+ console.log(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
652
+ console.log(" \u2502 Setup complete! \u2502");
653
+ console.log(" \u2502 \u2502");
654
+ console.log(" \u2502 MCP tools \u2192 Proxy audit logging \u2502");
655
+ console.log(" \u2502 Built-in tools \u2192 Hook audit logging \u2502");
656
+ console.log(" \u2502 \u2502");
657
+ console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
658
+ console.log(" \u2502 To undo: solongate-init --restore \u2502");
659
+ console.log(" \u2502 \u2502");
660
+ console.log(" \u2502 Restart your MCP client to apply changes. \u2502");
661
+ console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
662
+ console.log("");
649
663
  }
650
664
  main().catch((err) => {
651
- console.error(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
665
+ console.log(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
652
666
  process.exit(1);
653
667
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.4.9",
3
+ "version": "0.5.1",
4
4
  "description": "MCP security proxy — protect any MCP server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {