@solongate/proxy 0.49.1 → 0.50.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/README.md +72 -72
- package/dist/index.js +79 -782
- package/dist/login.js +2 -3
- package/hooks/audit.mjs +379 -293
- package/hooks/guard.bundled.mjs +7740 -7738
- package/hooks/guard.mjs +3 -1
- package/hooks/stop.mjs +75 -75
- package/package.json +76 -77
- package/dist/init.d.ts +0 -18
- package/dist/init.js +0 -910
package/dist/index.js
CHANGED
|
@@ -3412,12 +3412,12 @@ ${ctx.indent}`;
|
|
|
3412
3412
|
for (const {
|
|
3413
3413
|
format,
|
|
3414
3414
|
test,
|
|
3415
|
-
resolve:
|
|
3415
|
+
resolve: resolve7
|
|
3416
3416
|
} of tags) {
|
|
3417
3417
|
if (test) {
|
|
3418
3418
|
const match = str.match(test);
|
|
3419
3419
|
if (match) {
|
|
3420
|
-
let res =
|
|
3420
|
+
let res = resolve7.apply(null, match);
|
|
3421
3421
|
if (!(res instanceof Scalar)) res = new Scalar(res);
|
|
3422
3422
|
if (format) res.format = format;
|
|
3423
3423
|
return res;
|
|
@@ -6775,707 +6775,10 @@ var init_global_install = __esm({
|
|
|
6775
6775
|
}
|
|
6776
6776
|
});
|
|
6777
6777
|
|
|
6778
|
-
// src/init.ts
|
|
6779
|
-
var init_exports = {};
|
|
6780
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
6781
|
-
import { resolve as resolve4, join as join5, dirname as dirname3 } from "path";
|
|
6782
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6783
|
-
import { execFileSync as execFileSync3 } from "child_process";
|
|
6784
|
-
import { createInterface as createInterface2 } from "readline";
|
|
6785
|
-
function startSpinner(message) {
|
|
6786
|
-
spinnerFrame = 0;
|
|
6787
|
-
process.stderr.write(` ${SPINNER_FRAMES[0]} ${message}`);
|
|
6788
|
-
spinnerInterval = setInterval(() => {
|
|
6789
|
-
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
6790
|
-
process.stderr.write(`\r ${SPINNER_FRAMES[spinnerFrame]} ${message}`);
|
|
6791
|
-
}, 80);
|
|
6792
|
-
}
|
|
6793
|
-
function stopSpinner(result) {
|
|
6794
|
-
if (spinnerInterval) {
|
|
6795
|
-
clearInterval(spinnerInterval);
|
|
6796
|
-
spinnerInterval = null;
|
|
6797
|
-
}
|
|
6798
|
-
process.stderr.write(`\r ${result}
|
|
6799
|
-
`);
|
|
6800
|
-
}
|
|
6801
|
-
function findConfigFile(explicitPath, createIfMissing = false) {
|
|
6802
|
-
if (explicitPath) {
|
|
6803
|
-
if (existsSync5(explicitPath)) {
|
|
6804
|
-
return { path: resolve4(explicitPath), type: "mcp" };
|
|
6805
|
-
}
|
|
6806
|
-
return null;
|
|
6807
|
-
}
|
|
6808
|
-
for (const searchPath of SEARCH_PATHS) {
|
|
6809
|
-
const full = resolve4(searchPath);
|
|
6810
|
-
if (existsSync5(full)) return { path: full, type: "mcp" };
|
|
6811
|
-
}
|
|
6812
|
-
if (createIfMissing) {
|
|
6813
|
-
const starterConfig = {
|
|
6814
|
-
mcpServers: {
|
|
6815
|
-
"filesystem": {
|
|
6816
|
-
command: "npx",
|
|
6817
|
-
args: ["-y", "@modelcontextprotocol/server-filesystem@latest", "."]
|
|
6818
|
-
},
|
|
6819
|
-
"playwright": {
|
|
6820
|
-
command: "npx",
|
|
6821
|
-
args: ["-y", "@playwright/mcp@latest"]
|
|
6822
|
-
}
|
|
6823
|
-
}
|
|
6824
|
-
};
|
|
6825
|
-
const starterPath = resolve4(".mcp.json");
|
|
6826
|
-
writeFileSync4(starterPath, JSON.stringify(starterConfig, null, 2) + "\n");
|
|
6827
|
-
console.log(" Created .mcp.json with starter servers (filesystem, playwright).");
|
|
6828
|
-
console.log("");
|
|
6829
|
-
return { path: starterPath, type: "mcp", created: true };
|
|
6830
|
-
}
|
|
6831
|
-
for (const desktopPath of CLAUDE_DESKTOP_PATHS) {
|
|
6832
|
-
if (existsSync5(desktopPath)) return { path: desktopPath, type: "claude-desktop" };
|
|
6833
|
-
}
|
|
6834
|
-
return null;
|
|
6835
|
-
}
|
|
6836
|
-
function readConfig(filePath) {
|
|
6837
|
-
const content = readFileSync6(filePath, "utf-8");
|
|
6838
|
-
const parsed = JSON.parse(content);
|
|
6839
|
-
if (parsed.mcpServers) return parsed;
|
|
6840
|
-
throw new Error(`Unrecognized config format in ${filePath}`);
|
|
6841
|
-
}
|
|
6842
|
-
function isAlreadyProtected(server) {
|
|
6843
|
-
const args = server.args ?? [];
|
|
6844
|
-
if (server.command === "solongate-proxy") return true;
|
|
6845
|
-
if (server.command === "@solongate/proxy") return true;
|
|
6846
|
-
for (const arg of args) {
|
|
6847
|
-
if (arg === "-y" || arg === "--yes") continue;
|
|
6848
|
-
if (arg === "@solongate/proxy" || arg === "@solongate/proxy@latest" || arg === "solongate-proxy") return true;
|
|
6849
|
-
if (/[/\\]packages[/\\]proxy[/\\]dist[/\\]index\.js$/.test(arg)) return true;
|
|
6850
|
-
break;
|
|
6851
|
-
}
|
|
6852
|
-
return false;
|
|
6853
|
-
}
|
|
6854
|
-
function wrapServer(serverName, server, policy, agentName) {
|
|
6855
|
-
const env = { ...server.env ?? {} };
|
|
6856
|
-
env.SOLONGATE_API_KEY = "${SOLONGATE_API_KEY}";
|
|
6857
|
-
const proxyArgs = ["-y", "@solongate/proxy@latest"];
|
|
6858
|
-
if (policy) {
|
|
6859
|
-
proxyArgs.push("--policy", policy);
|
|
6860
|
-
}
|
|
6861
|
-
if (agentName) {
|
|
6862
|
-
proxyArgs.push("--agent-name", agentName);
|
|
6863
|
-
}
|
|
6864
|
-
proxyArgs.push("--verbose", "--", server.command, ...server.args ?? []);
|
|
6865
|
-
return {
|
|
6866
|
-
command: "npx",
|
|
6867
|
-
args: proxyArgs,
|
|
6868
|
-
env
|
|
6869
|
-
};
|
|
6870
|
-
}
|
|
6871
|
-
async function prompt(question) {
|
|
6872
|
-
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
6873
|
-
return new Promise((res) => {
|
|
6874
|
-
rl.question(question, (answer) => {
|
|
6875
|
-
rl.close();
|
|
6876
|
-
res(answer.trim());
|
|
6877
|
-
});
|
|
6878
|
-
});
|
|
6879
|
-
}
|
|
6880
|
-
function parseInitArgs(argv) {
|
|
6881
|
-
const args = argv.slice(2);
|
|
6882
|
-
const options = {
|
|
6883
|
-
all: false,
|
|
6884
|
-
tools: [],
|
|
6885
|
-
global: false,
|
|
6886
|
-
restore: false
|
|
6887
|
-
};
|
|
6888
|
-
for (let i = 0; i < args.length; i++) {
|
|
6889
|
-
switch (args[i]) {
|
|
6890
|
-
case "--config":
|
|
6891
|
-
options.configPath = args[++i];
|
|
6892
|
-
break;
|
|
6893
|
-
case "--policy":
|
|
6894
|
-
options.policy = args[++i];
|
|
6895
|
-
break;
|
|
6896
|
-
case "--api-key":
|
|
6897
|
-
options.apiKey = args[++i];
|
|
6898
|
-
break;
|
|
6899
|
-
case "--all":
|
|
6900
|
-
options.all = true;
|
|
6901
|
-
break;
|
|
6902
|
-
case "--global":
|
|
6903
|
-
case "-g":
|
|
6904
|
-
options.global = true;
|
|
6905
|
-
break;
|
|
6906
|
-
case "--restore":
|
|
6907
|
-
case "--uninstall":
|
|
6908
|
-
options.restore = true;
|
|
6909
|
-
break;
|
|
6910
|
-
case "--claude-code":
|
|
6911
|
-
options.tools.push("claude-code");
|
|
6912
|
-
break;
|
|
6913
|
-
case "--gemini":
|
|
6914
|
-
options.tools.push("gemini");
|
|
6915
|
-
break;
|
|
6916
|
-
case "--help":
|
|
6917
|
-
case "-h":
|
|
6918
|
-
printHelp();
|
|
6919
|
-
process.exit(0);
|
|
6920
|
-
}
|
|
6921
|
-
}
|
|
6922
|
-
return options;
|
|
6923
|
-
}
|
|
6924
|
-
function printHelp() {
|
|
6925
|
-
const help = `
|
|
6926
|
-
SolonGate Init \u2014 Protect your MCP servers in seconds
|
|
6927
|
-
|
|
6928
|
-
USAGE
|
|
6929
|
-
npx @solongate/proxy init --all
|
|
6930
|
-
npx @solongate/proxy init --global # system-wide (every project)
|
|
6931
|
-
|
|
6932
|
-
OPTIONS
|
|
6933
|
-
--config <path> Path to MCP config file (default: auto-detect)
|
|
6934
|
-
--policy <file> Custom policy JSON file (auto-detects policy.json)
|
|
6935
|
-
--api-key <key> SolonGate API key (sg_live_... or sg_test_...)
|
|
6936
|
-
--all Protect all servers without prompting
|
|
6937
|
-
--global, -g Install a SYSTEM-WIDE Claude Code hook (~/.claude) that
|
|
6938
|
-
guards EVERY session on the machine with your cloud
|
|
6939
|
-
policy (OPA WASM) \u2014 like the air-gapped product.
|
|
6940
|
-
--restore With --global: undo the system-wide install.
|
|
6941
|
-
-h, --help Show this help message
|
|
6942
|
-
|
|
6943
|
-
AI TOOL HOOKS (default: all)
|
|
6944
|
-
--claude-code Install hooks for Claude Code
|
|
6945
|
-
--gemini Install hooks for Gemini CLI
|
|
6946
|
-
|
|
6947
|
-
EXAMPLES
|
|
6948
|
-
npx @solongate/proxy init --all # Protect everything, all tools
|
|
6949
|
-
npx @solongate/proxy init --all --claude-code # Only Claude Code hooks
|
|
6950
|
-
npx @solongate/proxy init --all --policy policy.json # With custom policy
|
|
6951
|
-
npx @solongate/proxy init --global --api-key sg_live_\u2026 # System-wide enforcement
|
|
6952
|
-
npx @solongate/proxy init --global --restore # Undo system-wide install
|
|
6953
|
-
`;
|
|
6954
|
-
console.log(help);
|
|
6955
|
-
}
|
|
6956
|
-
function readHookScript(filename) {
|
|
6957
|
-
return readFileSync6(join5(HOOKS_DIR2, filename), "utf-8");
|
|
6958
|
-
}
|
|
6959
|
-
function readGuardForGlobal() {
|
|
6960
|
-
const bundled = join5(HOOKS_DIR2, "guard.bundled.mjs");
|
|
6961
|
-
if (existsSync5(bundled)) return readFileSync6(bundled, "utf-8");
|
|
6962
|
-
return readFileSync6(join5(HOOKS_DIR2, "guard.mjs"), "utf-8");
|
|
6963
|
-
}
|
|
6964
|
-
function unlockProtectedDirs() {
|
|
6965
|
-
const dirs = [".solongate", ".claude", ".gemini"];
|
|
6966
|
-
for (const dir of dirs) {
|
|
6967
|
-
const fullDir = resolve4(dir);
|
|
6968
|
-
if (!existsSync5(fullDir)) continue;
|
|
6969
|
-
try {
|
|
6970
|
-
if (process.platform === "win32") {
|
|
6971
|
-
try {
|
|
6972
|
-
execFileSync3("icacls", [fullDir, "/remove:d", "*S-1-1-0", "/T", "/Q"], { stdio: "ignore" });
|
|
6973
|
-
} catch {
|
|
6974
|
-
}
|
|
6975
|
-
try {
|
|
6976
|
-
execFileSync3("attrib", ["-R", "/S", "/D", fullDir], { stdio: "ignore" });
|
|
6977
|
-
} catch {
|
|
6978
|
-
}
|
|
6979
|
-
} else {
|
|
6980
|
-
try {
|
|
6981
|
-
execFileSync3("chattr", ["-i", "-R", fullDir], { stdio: "ignore" });
|
|
6982
|
-
} catch {
|
|
6983
|
-
}
|
|
6984
|
-
execFileSync3("chmod", ["-R", "u+w", fullDir], { stdio: "ignore" });
|
|
6985
|
-
}
|
|
6986
|
-
} catch {
|
|
6987
|
-
}
|
|
6988
|
-
}
|
|
6989
|
-
const protectedFiles = [".env", ".gitignore", ".mcp.json", "policy.json"];
|
|
6990
|
-
for (const file of protectedFiles) {
|
|
6991
|
-
const fullPath = resolve4(file);
|
|
6992
|
-
if (!existsSync5(fullPath)) continue;
|
|
6993
|
-
try {
|
|
6994
|
-
if (process.platform === "win32") {
|
|
6995
|
-
try {
|
|
6996
|
-
execFileSync3("icacls", [fullPath, "/remove:d", "*S-1-1-0", "/Q"], { stdio: "ignore" });
|
|
6997
|
-
} catch {
|
|
6998
|
-
}
|
|
6999
|
-
try {
|
|
7000
|
-
execFileSync3("attrib", ["-R", fullPath], { stdio: "ignore" });
|
|
7001
|
-
} catch {
|
|
7002
|
-
}
|
|
7003
|
-
} else {
|
|
7004
|
-
try {
|
|
7005
|
-
execFileSync3("chattr", ["-i", fullPath], { stdio: "ignore" });
|
|
7006
|
-
} catch {
|
|
7007
|
-
}
|
|
7008
|
-
execFileSync3("chmod", ["u+w", fullPath], { stdio: "ignore" });
|
|
7009
|
-
}
|
|
7010
|
-
} catch {
|
|
7011
|
-
}
|
|
7012
|
-
}
|
|
7013
|
-
}
|
|
7014
|
-
function installHooks(selectedTools = [], wrappedMcpConfig) {
|
|
7015
|
-
unlockProtectedDirs();
|
|
7016
|
-
const hooksDir = resolve4(".solongate", "hooks");
|
|
7017
|
-
mkdirSync5(hooksDir, { recursive: true });
|
|
7018
|
-
const hookFiles = ["guard.mjs", "audit.mjs", "stop.mjs"];
|
|
7019
|
-
let hooksUpdated = 0;
|
|
7020
|
-
let hooksSkipped = 0;
|
|
7021
|
-
for (const filename of hookFiles) {
|
|
7022
|
-
const hookPath = join5(hooksDir, filename);
|
|
7023
|
-
const latest = filename === "guard.mjs" ? readGuardForGlobal() : readHookScript(filename);
|
|
7024
|
-
let needsWrite = true;
|
|
7025
|
-
if (existsSync5(hookPath)) {
|
|
7026
|
-
const current = readFileSync6(hookPath, "utf-8");
|
|
7027
|
-
if (current === latest) {
|
|
7028
|
-
needsWrite = false;
|
|
7029
|
-
hooksSkipped++;
|
|
7030
|
-
}
|
|
7031
|
-
}
|
|
7032
|
-
if (needsWrite) {
|
|
7033
|
-
writeFileSync4(hookPath, latest);
|
|
7034
|
-
hooksUpdated++;
|
|
7035
|
-
console.log(` ${existsSync5(hookPath) ? "Updated" : "Created"} ${hookPath}`);
|
|
7036
|
-
}
|
|
7037
|
-
}
|
|
7038
|
-
if (hooksSkipped > 0 && hooksUpdated === 0) {
|
|
7039
|
-
console.log(` Hook files already up to date`);
|
|
7040
|
-
}
|
|
7041
|
-
const allClients = [
|
|
7042
|
-
{ name: "Claude Code", dir: ".claude", key: "claude-code", agentId: "claude-code", agentName: "Claude Code" },
|
|
7043
|
-
{ name: "Gemini CLI", dir: ".gemini", key: "gemini", agentId: "gemini-cli", agentName: "Gemini CLI" }
|
|
7044
|
-
];
|
|
7045
|
-
const clients = selectedTools.length > 0 ? allClients.filter((c3) => selectedTools.includes(c3.key)) : allClients;
|
|
7046
|
-
const activatedNames = [];
|
|
7047
|
-
const skippedNames = [];
|
|
7048
|
-
for (const client of clients) {
|
|
7049
|
-
const clientDir = resolve4(client.dir);
|
|
7050
|
-
mkdirSync5(clientDir, { recursive: true });
|
|
7051
|
-
const nodeBin = process.execPath.replace(/\\/g, "/");
|
|
7052
|
-
const guardCmd = `"${nodeBin}" .solongate/hooks/guard.mjs ${client.agentId} "${client.agentName}"`;
|
|
7053
|
-
const auditCmd = `"${nodeBin}" .solongate/hooks/audit.mjs ${client.agentId} "${client.agentName}"`;
|
|
7054
|
-
const stopCmd = `"${nodeBin}" .solongate/hooks/stop.mjs ${client.agentId} "${client.agentName}"`;
|
|
7055
|
-
if (client.key === "gemini") {
|
|
7056
|
-
const result = installGeminiConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
|
|
7057
|
-
if (result === "skipped") skippedNames.push(client.name);
|
|
7058
|
-
else activatedNames.push(client.name);
|
|
7059
|
-
} else {
|
|
7060
|
-
const result = installStandardHookConfig(clientDir, client.name, guardCmd, auditCmd, stopCmd);
|
|
7061
|
-
if (result === "skipped") skippedNames.push(client.name);
|
|
7062
|
-
else activatedNames.push(client.name);
|
|
7063
|
-
}
|
|
7064
|
-
}
|
|
7065
|
-
console.log("");
|
|
7066
|
-
if (activatedNames.length > 0) {
|
|
7067
|
-
console.log(" Hooks installed:");
|
|
7068
|
-
console.log(" guard.mjs \u2192 blocks policy-violating calls (pre-execution)");
|
|
7069
|
-
console.log(" audit.mjs \u2192 logs all calls to dashboard (post-execution)");
|
|
7070
|
-
console.log(" stop.mjs \u2192 tracks text-only responses (no tool calls)");
|
|
7071
|
-
console.log(` Activated for: ${activatedNames.join(", ")}`);
|
|
7072
|
-
}
|
|
7073
|
-
if (skippedNames.length > 0) {
|
|
7074
|
-
console.log(` Already configured: ${skippedNames.join(", ")}`);
|
|
7075
|
-
}
|
|
7076
|
-
}
|
|
7077
|
-
function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, stopCmd) {
|
|
7078
|
-
const settingsPath = join5(clientDir, "settings.json");
|
|
7079
|
-
const hookSettings = {
|
|
7080
|
-
PreToolUse: [
|
|
7081
|
-
{ matcher: "", hooks: [{ type: "command", command: guardCmd }] }
|
|
7082
|
-
],
|
|
7083
|
-
PostToolUse: [
|
|
7084
|
-
{ matcher: "", hooks: [{ type: "command", command: auditCmd }] }
|
|
7085
|
-
],
|
|
7086
|
-
Stop: [
|
|
7087
|
-
{ matcher: "", hooks: [{ type: "command", command: stopCmd }] }
|
|
7088
|
-
]
|
|
7089
|
-
};
|
|
7090
|
-
let existing = {};
|
|
7091
|
-
try {
|
|
7092
|
-
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
7093
|
-
} catch {
|
|
7094
|
-
}
|
|
7095
|
-
const merged = { ...existing, hooks: hookSettings };
|
|
7096
|
-
const mergedStr = JSON.stringify(merged, null, 2) + "\n";
|
|
7097
|
-
const existingStr = existsSync5(settingsPath) ? readFileSync6(settingsPath, "utf-8") : "";
|
|
7098
|
-
if (mergedStr === existingStr) return "skipped";
|
|
7099
|
-
writeFileSync4(settingsPath, mergedStr);
|
|
7100
|
-
console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
|
|
7101
|
-
return "installed";
|
|
7102
|
-
}
|
|
7103
|
-
function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
|
|
7104
|
-
const settingsPath = join5(clientDir, "settings.json");
|
|
7105
|
-
let existing = {};
|
|
7106
|
-
try {
|
|
7107
|
-
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
7108
|
-
} catch {
|
|
7109
|
-
}
|
|
7110
|
-
const merged = { ...existing };
|
|
7111
|
-
if (wrappedMcpConfig) {
|
|
7112
|
-
const geminiMcp = {};
|
|
7113
|
-
for (const [name, server] of Object.entries(wrappedMcpConfig.mcpServers)) {
|
|
7114
|
-
const args = [...server.args ?? []];
|
|
7115
|
-
const agentIdx = args.indexOf("--agent-name");
|
|
7116
|
-
if (agentIdx >= 0 && agentIdx + 1 < args.length) {
|
|
7117
|
-
args[agentIdx + 1] = "gemini-cli";
|
|
7118
|
-
} else {
|
|
7119
|
-
const sepIdx = args.indexOf("--");
|
|
7120
|
-
if (sepIdx >= 0) args.splice(sepIdx, 0, "--agent-name", "gemini-cli");
|
|
7121
|
-
}
|
|
7122
|
-
geminiMcp[name] = { ...server, args };
|
|
7123
|
-
}
|
|
7124
|
-
merged.mcpServers = geminiMcp;
|
|
7125
|
-
}
|
|
7126
|
-
merged.hooks = {
|
|
7127
|
-
BeforeTool: [
|
|
7128
|
-
{ matcher: ".*", hooks: [{ type: "command", command: guardCmd }] }
|
|
7129
|
-
],
|
|
7130
|
-
AfterTool: [
|
|
7131
|
-
{ matcher: ".*", hooks: [{ type: "command", command: auditCmd }] }
|
|
7132
|
-
]
|
|
7133
|
-
};
|
|
7134
|
-
const mergedStr = JSON.stringify(merged, null, 2) + "\n";
|
|
7135
|
-
const existingStr = existsSync5(settingsPath) ? readFileSync6(settingsPath, "utf-8") : "";
|
|
7136
|
-
if (mergedStr === existingStr) return "skipped";
|
|
7137
|
-
writeFileSync4(settingsPath, mergedStr);
|
|
7138
|
-
console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
|
|
7139
|
-
return "installed";
|
|
7140
|
-
}
|
|
7141
|
-
function ensureEnvFile() {
|
|
7142
|
-
let envChanged = false;
|
|
7143
|
-
let gitignoreChanged = false;
|
|
7144
|
-
const envPath = resolve4(".env");
|
|
7145
|
-
if (!existsSync5(envPath)) {
|
|
7146
|
-
const envContent = `# SolonGate Configuration
|
|
7147
|
-
# IMPORTANT: Never commit this file to git!
|
|
7148
|
-
|
|
7149
|
-
# Your SolonGate API key \u2014 get one at https://dashboard.solongate.com
|
|
7150
|
-
SOLONGATE_API_KEY=sg_live_your_key_here
|
|
7151
|
-
|
|
7152
|
-
# AI Judge (semantic security layer) \u2014 get a free key at https://console.groq.com/keys
|
|
7153
|
-
# Enable with: npx @solongate/proxy --ai-judge ...
|
|
7154
|
-
GROQ_API_KEY=gsk_your_groq_key_here
|
|
7155
|
-
`;
|
|
7156
|
-
writeFileSync4(envPath, envContent);
|
|
7157
|
-
console.log(` Created .env`);
|
|
7158
|
-
console.log(` \u2192 Set your API key in .env (get one at https://dashboard.solongate.com)`);
|
|
7159
|
-
envChanged = true;
|
|
7160
|
-
} else {
|
|
7161
|
-
const existingEnv = readFileSync6(envPath, "utf-8");
|
|
7162
|
-
if (!existingEnv.includes("SOLONGATE_API_KEY")) {
|
|
7163
|
-
const separator = existingEnv.endsWith("\n") ? "" : "\n";
|
|
7164
|
-
const appendContent = `${separator}
|
|
7165
|
-
# SolonGate API key \u2014 get one at https://dashboard.solongate.com
|
|
7166
|
-
SOLONGATE_API_KEY=sg_live_your_key_here
|
|
7167
|
-
`;
|
|
7168
|
-
writeFileSync4(envPath, existingEnv + appendContent);
|
|
7169
|
-
console.log(` Updated .env (added SOLONGATE_API_KEY)`);
|
|
7170
|
-
console.log(` \u2192 Set your API key in .env (get one at https://dashboard.solongate.com)`);
|
|
7171
|
-
envChanged = true;
|
|
7172
|
-
}
|
|
7173
|
-
}
|
|
7174
|
-
const gitignorePath = resolve4(".gitignore");
|
|
7175
|
-
const requiredLines = [
|
|
7176
|
-
".env",
|
|
7177
|
-
".env.local",
|
|
7178
|
-
".mcp.json",
|
|
7179
|
-
".solongate/**",
|
|
7180
|
-
".claude/**",
|
|
7181
|
-
".gemini/**"
|
|
7182
|
-
];
|
|
7183
|
-
if (existsSync5(gitignorePath)) {
|
|
7184
|
-
let gitignore = readFileSync6(gitignorePath, "utf-8");
|
|
7185
|
-
const existingLines = new Set(gitignore.split("\n").map((l) => l.trim()));
|
|
7186
|
-
const missing = requiredLines.filter((line) => !existingLines.has(line));
|
|
7187
|
-
if (missing.length > 0) {
|
|
7188
|
-
gitignore = gitignore.trimEnd() + "\n\n# SolonGate\n" + missing.join("\n") + "\n";
|
|
7189
|
-
writeFileSync4(gitignorePath, gitignore);
|
|
7190
|
-
console.log(` Updated .gitignore (+${missing.length} entries)`);
|
|
7191
|
-
gitignoreChanged = true;
|
|
7192
|
-
}
|
|
7193
|
-
} else {
|
|
7194
|
-
const content = "# SolonGate\n" + requiredLines.join("\n") + "\nnode_modules/\n";
|
|
7195
|
-
writeFileSync4(gitignorePath, content);
|
|
7196
|
-
console.log(` Created .gitignore`);
|
|
7197
|
-
gitignoreChanged = true;
|
|
7198
|
-
}
|
|
7199
|
-
if (!envChanged && !gitignoreChanged) {
|
|
7200
|
-
console.log(` .env and .gitignore already up to date`);
|
|
7201
|
-
}
|
|
7202
|
-
return { envChanged, gitignoreChanged };
|
|
7203
|
-
}
|
|
7204
|
-
async function main() {
|
|
7205
|
-
const options = parseInitArgs(process.argv);
|
|
7206
|
-
if (options.global) {
|
|
7207
|
-
console.log("");
|
|
7208
|
-
console.log(` ${c.bold}SolonGate \u2014 Global Install${c.reset}`);
|
|
7209
|
-
console.log("");
|
|
7210
|
-
if (options.restore) {
|
|
7211
|
-
runGlobalRestore();
|
|
7212
|
-
} else {
|
|
7213
|
-
await runGlobalInstall({ apiKey: options.apiKey });
|
|
7214
|
-
console.log("");
|
|
7215
|
-
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");
|
|
7216
|
-
console.log(" \u2502 Global enforcement active. \u2502");
|
|
7217
|
-
console.log(" \u2502 Every Claude Code session on this machine is \u2502");
|
|
7218
|
-
console.log(" \u2502 now guarded by your cloud policy (OPA WASM). \u2502");
|
|
7219
|
-
console.log(" \u2502 \u2502");
|
|
7220
|
-
console.log(" \u2502 Undo: init --global --restore \u2502");
|
|
7221
|
-
console.log(" \u2502 Logs: https://dashboard.solongate.com \u2502");
|
|
7222
|
-
console.log(" \u2502 Restart Claude Code to apply. \u2502");
|
|
7223
|
-
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");
|
|
7224
|
-
}
|
|
7225
|
-
console.log("");
|
|
7226
|
-
return;
|
|
7227
|
-
}
|
|
7228
|
-
const fullBanner = BANNER_FULL;
|
|
7229
|
-
const mediumBanner = [
|
|
7230
|
-
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
7231
|
-
" \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551",
|
|
7232
|
-
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551",
|
|
7233
|
-
" \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551",
|
|
7234
|
-
" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551",
|
|
7235
|
-
" \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",
|
|
7236
|
-
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
7237
|
-
" \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D",
|
|
7238
|
-
" \u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
7239
|
-
" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D ",
|
|
7240
|
-
" \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
7241
|
-
" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
|
|
7242
|
-
];
|
|
7243
|
-
const smallBanner = [
|
|
7244
|
-
" \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513",
|
|
7245
|
-
" \u2503 \u2554\u2550\u2557\u2554\u2550\u2557\u2566 \u2554\u2550\u2557\u2554\u2557\u2554\u2554\u2550\u2557 \u2503",
|
|
7246
|
-
" \u2503 \u255A\u2550\u2557\u2551 \u2551\u2551 \u2551 \u2551\u2551\u2551\u2551\u2551 \u2566 \u2503",
|
|
7247
|
-
" \u2503 \u255A\u2550\u255D\u255A\u2550\u255D\u2569\u2550\u255D\u255A\u2550\u255D\u255D\u255A\u255D\u255A\u2550\u255D \u2503",
|
|
7248
|
-
" \u2503 \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557\u2554\u2550\u2557 \u2503",
|
|
7249
|
-
" \u2503 \u2551 \u2566\u2560\u2550\u2563 \u2551 \u2551\u2563 \u2503",
|
|
7250
|
-
" \u2503 \u255A\u2550\u255D\u2569 \u2569 \u2569 \u255A\u2550\u255D \u2503",
|
|
7251
|
-
" \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251B"
|
|
7252
|
-
];
|
|
7253
|
-
const cols = process.stdout.columns || 80;
|
|
7254
|
-
const bannerLines = cols >= 82 ? fullBanner : cols >= 50 ? mediumBanner : smallBanner;
|
|
7255
|
-
const bannerColors = BANNER_COLORS;
|
|
7256
|
-
console.log("");
|
|
7257
|
-
for (let i = 0; i < bannerLines.length; i++) {
|
|
7258
|
-
console.log(`${c.bold}${bannerColors[i % bannerColors.length]}${bannerLines[i]}${c.reset}`);
|
|
7259
|
-
}
|
|
7260
|
-
console.log("");
|
|
7261
|
-
console.log(` ${c.dim}${c.italic}Init Setup${c.reset}`);
|
|
7262
|
-
console.log("");
|
|
7263
|
-
startSpinner("Scanning for MCP config...");
|
|
7264
|
-
await sleep(400);
|
|
7265
|
-
const configInfo = findConfigFile(options.configPath, true);
|
|
7266
|
-
if (!configInfo) {
|
|
7267
|
-
stopSpinner("\u2717 No MCP config found and could not create one.");
|
|
7268
|
-
process.exit(1);
|
|
7269
|
-
}
|
|
7270
|
-
stopSpinner(`\u2713 Found config: ${configInfo.path}`);
|
|
7271
|
-
console.log(` Type: ${configInfo.type === "claude-desktop" ? "Claude Desktop" : "MCP JSON"}`);
|
|
7272
|
-
console.log("");
|
|
7273
|
-
const config = readConfig(configInfo.path);
|
|
7274
|
-
const serverNames = Object.keys(config.mcpServers);
|
|
7275
|
-
if (serverNames.length === 0) {
|
|
7276
|
-
console.log(" No MCP servers found in config.");
|
|
7277
|
-
process.exit(1);
|
|
7278
|
-
}
|
|
7279
|
-
startSpinner("Analyzing servers...");
|
|
7280
|
-
await sleep(300);
|
|
7281
|
-
const toProtect = [];
|
|
7282
|
-
const alreadyProtected = [];
|
|
7283
|
-
const skipped = [];
|
|
7284
|
-
for (const name of serverNames) {
|
|
7285
|
-
const server = config.mcpServers[name];
|
|
7286
|
-
if (isAlreadyProtected(server)) {
|
|
7287
|
-
alreadyProtected.push(name);
|
|
7288
|
-
} else {
|
|
7289
|
-
if (options.all) {
|
|
7290
|
-
toProtect.push(name);
|
|
7291
|
-
}
|
|
7292
|
-
}
|
|
7293
|
-
}
|
|
7294
|
-
stopSpinner(`\u2713 Found ${serverNames.length} MCP server(s)`);
|
|
7295
|
-
console.log("");
|
|
7296
|
-
for (const name of serverNames) {
|
|
7297
|
-
const server = config.mcpServers[name];
|
|
7298
|
-
if (alreadyProtected.includes(name)) {
|
|
7299
|
-
console.log(` [protected] ${name}`);
|
|
7300
|
-
} else {
|
|
7301
|
-
console.log(` [exposed] ${name} \u2192 ${server.command} ${(server.args ?? []).join(" ")}`);
|
|
7302
|
-
}
|
|
7303
|
-
}
|
|
7304
|
-
console.log("");
|
|
7305
|
-
if (alreadyProtected.length === serverNames.length) {
|
|
7306
|
-
console.log(" All servers are already protected by SolonGate!");
|
|
7307
|
-
ensureEnvFile();
|
|
7308
|
-
console.log("");
|
|
7309
|
-
installHooks(options.tools, config);
|
|
7310
|
-
process.exit(0);
|
|
7311
|
-
}
|
|
7312
|
-
if (!options.all) {
|
|
7313
|
-
const exposed = serverNames.filter((n) => !alreadyProtected.includes(n));
|
|
7314
|
-
for (const name of exposed) {
|
|
7315
|
-
const answer = await prompt(` Protect "${name}"? [Y/n] `);
|
|
7316
|
-
if (answer === "" || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
7317
|
-
toProtect.push(name);
|
|
7318
|
-
} else {
|
|
7319
|
-
skipped.push(name);
|
|
7320
|
-
}
|
|
7321
|
-
}
|
|
7322
|
-
}
|
|
7323
|
-
if (toProtect.length === 0) {
|
|
7324
|
-
console.log(" No servers selected for protection.");
|
|
7325
|
-
ensureEnvFile();
|
|
7326
|
-
process.exit(0);
|
|
7327
|
-
}
|
|
7328
|
-
console.log("");
|
|
7329
|
-
startSpinner("Setting up environment...");
|
|
7330
|
-
await sleep(300);
|
|
7331
|
-
stopSpinner("\u2713 Environment");
|
|
7332
|
-
ensureEnvFile();
|
|
7333
|
-
console.log("");
|
|
7334
|
-
let apiKey = options.apiKey || process.env.SOLONGATE_API_KEY || "";
|
|
7335
|
-
if (!apiKey) {
|
|
7336
|
-
const envPath = resolve4(".env");
|
|
7337
|
-
if (existsSync5(envPath)) {
|
|
7338
|
-
const envContent = readFileSync6(envPath, "utf-8");
|
|
7339
|
-
const match = envContent.match(/^SOLONGATE_API_KEY=(sg_(?:live|test)_\w+)/m);
|
|
7340
|
-
if (match) apiKey = match[1];
|
|
7341
|
-
}
|
|
7342
|
-
}
|
|
7343
|
-
if (!apiKey || apiKey === "sg_live_your_key_here") {
|
|
7344
|
-
if (options.all) {
|
|
7345
|
-
apiKey = "sg_test_demo_init_key";
|
|
7346
|
-
console.log(" No API key found \u2014 using demo test key.");
|
|
7347
|
-
console.log(" Set your real key in .env for cloud features.");
|
|
7348
|
-
console.log("");
|
|
7349
|
-
} else {
|
|
7350
|
-
apiKey = await prompt(" Enter your SolonGate API key (from https://dashboard.solongate.com): ");
|
|
7351
|
-
}
|
|
7352
|
-
if (!apiKey) {
|
|
7353
|
-
console.log(" API key is required. Get one at https://dashboard.solongate.com");
|
|
7354
|
-
process.exit(1);
|
|
7355
|
-
}
|
|
7356
|
-
}
|
|
7357
|
-
if (!apiKey.startsWith("sg_live_") && !apiKey.startsWith("sg_test_")) {
|
|
7358
|
-
console.log(" Invalid API key format. Must start with sg_live_ or sg_test_");
|
|
7359
|
-
process.exit(1);
|
|
7360
|
-
}
|
|
7361
|
-
let policyValue;
|
|
7362
|
-
if (options.policy) {
|
|
7363
|
-
const policyPath = resolve4(options.policy);
|
|
7364
|
-
if (existsSync5(policyPath)) {
|
|
7365
|
-
policyValue = `./${options.policy}`;
|
|
7366
|
-
console.log(` Policy: ${policyPath}`);
|
|
7367
|
-
} else {
|
|
7368
|
-
console.log(` Policy file not found: ${options.policy}`);
|
|
7369
|
-
process.exit(1);
|
|
7370
|
-
}
|
|
7371
|
-
} else {
|
|
7372
|
-
const defaultPolicy = resolve4("policy.json");
|
|
7373
|
-
if (existsSync5(defaultPolicy)) {
|
|
7374
|
-
policyValue = "./policy.json";
|
|
7375
|
-
console.log(` Policy: ${defaultPolicy} (auto-detected)`);
|
|
7376
|
-
} else {
|
|
7377
|
-
console.log(` Policy: cloud-managed (fetched via API key)`);
|
|
7378
|
-
}
|
|
7379
|
-
}
|
|
7380
|
-
console.log(` API Key: ${apiKey.slice(0, 12)}...${apiKey.slice(-4)}`);
|
|
7381
|
-
console.log(` Protecting: ${toProtect.join(", ")}`);
|
|
7382
|
-
console.log("");
|
|
7383
|
-
startSpinner("Wrapping MCP servers with proxy...");
|
|
7384
|
-
const newConfig = { mcpServers: {} };
|
|
7385
|
-
for (const name of serverNames) {
|
|
7386
|
-
if (toProtect.includes(name)) {
|
|
7387
|
-
newConfig.mcpServers[name] = wrapServer(name, config.mcpServers[name], policyValue);
|
|
7388
|
-
} else {
|
|
7389
|
-
newConfig.mcpServers[name] = config.mcpServers[name];
|
|
7390
|
-
}
|
|
7391
|
-
}
|
|
7392
|
-
const currentContent = readFileSync6(configInfo.path, "utf-8");
|
|
7393
|
-
let newContent;
|
|
7394
|
-
if (configInfo.type === "claude-desktop") {
|
|
7395
|
-
const original = JSON.parse(currentContent);
|
|
7396
|
-
original.mcpServers = newConfig.mcpServers;
|
|
7397
|
-
newContent = JSON.stringify(original, null, 2) + "\n";
|
|
7398
|
-
} else {
|
|
7399
|
-
newContent = JSON.stringify(newConfig, null, 2) + "\n";
|
|
7400
|
-
}
|
|
7401
|
-
await sleep(400);
|
|
7402
|
-
if (newContent === currentContent) {
|
|
7403
|
-
stopSpinner("\u2713 Config already up to date");
|
|
7404
|
-
} else {
|
|
7405
|
-
writeFileSync4(configInfo.path, newContent);
|
|
7406
|
-
stopSpinner("\u2713 Config updated");
|
|
7407
|
-
}
|
|
7408
|
-
console.log("");
|
|
7409
|
-
startSpinner("Installing hooks...");
|
|
7410
|
-
await sleep(300);
|
|
7411
|
-
stopSpinner("\u2713 Hooks");
|
|
7412
|
-
installHooks(options.tools, newConfig);
|
|
7413
|
-
console.log("");
|
|
7414
|
-
const allUpToDate = toProtect.length === 0 && alreadyProtected.length === serverNames.length;
|
|
7415
|
-
console.log(" \u2500\u2500 Summary \u2500\u2500");
|
|
7416
|
-
console.log("");
|
|
7417
|
-
for (const name of toProtect) {
|
|
7418
|
-
console.log(` \u2713 ${name} \u2014 protected`);
|
|
7419
|
-
}
|
|
7420
|
-
for (const name of alreadyProtected) {
|
|
7421
|
-
console.log(` \u25CF ${name} \u2014 already protected`);
|
|
7422
|
-
}
|
|
7423
|
-
for (const name of skipped) {
|
|
7424
|
-
console.log(` \u25CB ${name} \u2014 skipped`);
|
|
7425
|
-
}
|
|
7426
|
-
console.log("");
|
|
7427
|
-
if (allUpToDate) {
|
|
7428
|
-
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");
|
|
7429
|
-
console.log(" \u2502 Everything is already up to date! \u2502");
|
|
7430
|
-
console.log(" \u2502 \u2502");
|
|
7431
|
-
console.log(" \u2502 All MCP servers protected, hooks configured. \u2502");
|
|
7432
|
-
console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
|
|
7433
|
-
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");
|
|
7434
|
-
} else {
|
|
7435
|
-
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");
|
|
7436
|
-
console.log(" \u2502 Setup complete! \u2502");
|
|
7437
|
-
console.log(" \u2502 \u2502");
|
|
7438
|
-
console.log(" \u2502 MCP servers \u2192 Protected via proxy \u2502");
|
|
7439
|
-
console.log(" \u2502 AI tools \u2192 Guarded via hooks \u2502");
|
|
7440
|
-
console.log(" \u2502 Claude Code, Gemini CLI \u2502");
|
|
7441
|
-
console.log(" \u2502 API key \u2192 Set in .env \u2502");
|
|
7442
|
-
console.log(" \u2502 \u2502");
|
|
7443
|
-
console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
|
|
7444
|
-
console.log(" \u2502 \u2502");
|
|
7445
|
-
console.log(" \u2502 Restart your MCP client to apply changes. \u2502");
|
|
7446
|
-
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");
|
|
7447
|
-
}
|
|
7448
|
-
console.log("");
|
|
7449
|
-
}
|
|
7450
|
-
var SEARCH_PATHS, CLAUDE_DESKTOP_PATHS, sleep, SPINNER_FRAMES, spinnerInterval, spinnerFrame, __dirname2, HOOKS_DIR2;
|
|
7451
|
-
var init_init = __esm({
|
|
7452
|
-
"src/init.ts"() {
|
|
7453
|
-
"use strict";
|
|
7454
|
-
init_cli_utils();
|
|
7455
|
-
init_global_install();
|
|
7456
|
-
SEARCH_PATHS = [
|
|
7457
|
-
".mcp.json",
|
|
7458
|
-
"mcp.json",
|
|
7459
|
-
".claude/mcp.json"
|
|
7460
|
-
];
|
|
7461
|
-
CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join5(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join5(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join5(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
|
|
7462
|
-
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7463
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7464
|
-
spinnerInterval = null;
|
|
7465
|
-
spinnerFrame = 0;
|
|
7466
|
-
__dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
7467
|
-
HOOKS_DIR2 = resolve4(__dirname2, "..", "hooks");
|
|
7468
|
-
main().catch((err) => {
|
|
7469
|
-
console.log(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
7470
|
-
process.exit(1);
|
|
7471
|
-
});
|
|
7472
|
-
}
|
|
7473
|
-
});
|
|
7474
|
-
|
|
7475
6778
|
// src/login.ts
|
|
7476
6779
|
var login_exports = {};
|
|
7477
6780
|
import { spawn } from "child_process";
|
|
7478
|
-
function
|
|
6781
|
+
function startSpinner(text) {
|
|
7479
6782
|
if (!process.stderr.isTTY) {
|
|
7480
6783
|
process.stderr.write(` ${text}
|
|
7481
6784
|
`);
|
|
@@ -7485,7 +6788,7 @@ function startSpinner2(text) {
|
|
|
7485
6788
|
}
|
|
7486
6789
|
let i = 0;
|
|
7487
6790
|
const draw = () => {
|
|
7488
|
-
const frame =
|
|
6791
|
+
const frame = SPINNER_FRAMES[i = (i + 1) % SPINNER_FRAMES.length];
|
|
7489
6792
|
process.stderr.write(`\r ${c.cyan}${frame}${c.reset} ${c.dim}${text}${c.reset}\x1B[K`);
|
|
7490
6793
|
};
|
|
7491
6794
|
draw();
|
|
@@ -7517,7 +6820,7 @@ function openBrowser(url) {
|
|
|
7517
6820
|
} catch {
|
|
7518
6821
|
}
|
|
7519
6822
|
}
|
|
7520
|
-
async function
|
|
6823
|
+
async function main() {
|
|
7521
6824
|
const { apiUrl, install } = parseArgs2(process.argv);
|
|
7522
6825
|
console.log("");
|
|
7523
6826
|
console.log(` ${c.bold}${c.blue4}SolonGate${c.reset} ${c.dim}\u2014 Login${c.reset}`);
|
|
@@ -7544,9 +6847,9 @@ async function main2() {
|
|
|
7544
6847
|
const pollMs = Math.max(2, Number(interval) || 3) * 1e3;
|
|
7545
6848
|
const deadline = Date.now() + (Number(expires_in) || 600) * 1e3;
|
|
7546
6849
|
let apiKey = "";
|
|
7547
|
-
const
|
|
6850
|
+
const stopSpinner = startSpinner("Waiting for authorization\u2026");
|
|
7548
6851
|
while (Date.now() < deadline) {
|
|
7549
|
-
await
|
|
6852
|
+
await sleep(pollMs);
|
|
7550
6853
|
try {
|
|
7551
6854
|
const res = await fetch(`${apiUrl}/api/v1/auth/device/poll`, {
|
|
7552
6855
|
method: "POST",
|
|
@@ -7556,7 +6859,7 @@ async function main2() {
|
|
|
7556
6859
|
const data = await res.json().catch(() => ({}));
|
|
7557
6860
|
if (data?.status === "approved" && data?.api_key) {
|
|
7558
6861
|
apiKey = data.api_key;
|
|
7559
|
-
|
|
6862
|
+
stopSpinner();
|
|
7560
6863
|
console.log("");
|
|
7561
6864
|
const who = data?.user?.name || data?.user?.email;
|
|
7562
6865
|
const mail = data?.user?.email;
|
|
@@ -7572,19 +6875,19 @@ async function main2() {
|
|
|
7572
6875
|
break;
|
|
7573
6876
|
}
|
|
7574
6877
|
if (data?.status === "expired" || data?.status === "not_found") {
|
|
7575
|
-
|
|
6878
|
+
stopSpinner(` ${c.red}\u2717${c.reset} Code expired. Run \`login\` again.`);
|
|
7576
6879
|
process.exit(1);
|
|
7577
6880
|
}
|
|
7578
6881
|
} catch {
|
|
7579
6882
|
}
|
|
7580
6883
|
}
|
|
7581
6884
|
if (!apiKey) {
|
|
7582
|
-
|
|
6885
|
+
stopSpinner(` ${c.red}\u2717${c.reset} Timed out waiting for authorization. Run \`login\` again.`);
|
|
7583
6886
|
process.exit(1);
|
|
7584
6887
|
}
|
|
7585
6888
|
if (!install) {
|
|
7586
6889
|
console.log("");
|
|
7587
|
-
console.log(" Logged in.
|
|
6890
|
+
console.log(" Logged in. Re-run `login` to enable system-wide enforcement.");
|
|
7588
6891
|
return;
|
|
7589
6892
|
}
|
|
7590
6893
|
console.log("");
|
|
@@ -7595,8 +6898,7 @@ async function main2() {
|
|
|
7595
6898
|
console.log(" \u2502 Every Claude Code session on this machine is \u2502");
|
|
7596
6899
|
console.log(" \u2502 now guarded by your cloud policy (OPA WASM). \u2502");
|
|
7597
6900
|
console.log(" \u2502 \u2502");
|
|
7598
|
-
console.log(" \u2502 Undo: npx @solongate/proxy
|
|
7599
|
-
console.log(" \u2502 --restore \u2502");
|
|
6901
|
+
console.log(" \u2502 Undo: npx @solongate/proxy logout \u2502");
|
|
7600
6902
|
console.log(" \u2502 Manage policy: https://dashboard.solongate.com \u2502");
|
|
7601
6903
|
console.log(" \u2502 Restart Claude Code to apply. \u2502");
|
|
7602
6904
|
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");
|
|
@@ -7608,15 +6910,15 @@ async function main2() {
|
|
|
7608
6910
|
console.log(` there for the guard + audit logging to take effect.${c.reset}`);
|
|
7609
6911
|
console.log("");
|
|
7610
6912
|
}
|
|
7611
|
-
var
|
|
6913
|
+
var sleep, SPINNER_FRAMES;
|
|
7612
6914
|
var init_login = __esm({
|
|
7613
6915
|
"src/login.ts"() {
|
|
7614
6916
|
"use strict";
|
|
7615
6917
|
init_cli_utils();
|
|
7616
6918
|
init_global_install();
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
6919
|
+
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
6920
|
+
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
6921
|
+
main().catch((err) => {
|
|
7620
6922
|
console.log(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
7621
6923
|
process.exit(1);
|
|
7622
6924
|
});
|
|
@@ -7625,8 +6927,8 @@ var init_login = __esm({
|
|
|
7625
6927
|
|
|
7626
6928
|
// src/inject.ts
|
|
7627
6929
|
var inject_exports = {};
|
|
7628
|
-
import { readFileSync as
|
|
7629
|
-
import { resolve as
|
|
6930
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync5, copyFileSync } from "fs";
|
|
6931
|
+
import { resolve as resolve4 } from "path";
|
|
7630
6932
|
import { execSync } from "child_process";
|
|
7631
6933
|
function parseInjectArgs(argv) {
|
|
7632
6934
|
const args = argv.slice(2);
|
|
@@ -7651,13 +6953,13 @@ function parseInjectArgs(argv) {
|
|
|
7651
6953
|
break;
|
|
7652
6954
|
case "--help":
|
|
7653
6955
|
case "-h":
|
|
7654
|
-
|
|
6956
|
+
printHelp();
|
|
7655
6957
|
process.exit(0);
|
|
7656
6958
|
}
|
|
7657
6959
|
}
|
|
7658
6960
|
return opts;
|
|
7659
6961
|
}
|
|
7660
|
-
function
|
|
6962
|
+
function printHelp() {
|
|
7661
6963
|
log3(`
|
|
7662
6964
|
SolonGate Inject \u2014 Add security to your MCP server in seconds
|
|
7663
6965
|
|
|
@@ -7683,9 +6985,9 @@ WHAT IT DOES
|
|
|
7683
6985
|
`);
|
|
7684
6986
|
}
|
|
7685
6987
|
function detectProject() {
|
|
7686
|
-
if (!
|
|
6988
|
+
if (!existsSync5(resolve4("package.json"))) return false;
|
|
7687
6989
|
try {
|
|
7688
|
-
const pkg = JSON.parse(
|
|
6990
|
+
const pkg = JSON.parse(readFileSync6(resolve4("package.json"), "utf-8"));
|
|
7689
6991
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7690
6992
|
return !!(allDeps["@modelcontextprotocol/sdk"] || allDeps["@modelcontextprotocol/server"]);
|
|
7691
6993
|
} catch {
|
|
@@ -7694,18 +6996,18 @@ function detectProject() {
|
|
|
7694
6996
|
}
|
|
7695
6997
|
function findTsEntryFile() {
|
|
7696
6998
|
try {
|
|
7697
|
-
const pkg = JSON.parse(
|
|
6999
|
+
const pkg = JSON.parse(readFileSync6(resolve4("package.json"), "utf-8"));
|
|
7698
7000
|
if (pkg.bin) {
|
|
7699
7001
|
const binPath = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
7700
7002
|
if (typeof binPath === "string") {
|
|
7701
7003
|
const srcPath = binPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
|
|
7702
|
-
if (
|
|
7703
|
-
if (
|
|
7004
|
+
if (existsSync5(resolve4(srcPath))) return resolve4(srcPath);
|
|
7005
|
+
if (existsSync5(resolve4(binPath))) return resolve4(binPath);
|
|
7704
7006
|
}
|
|
7705
7007
|
}
|
|
7706
7008
|
if (pkg.main) {
|
|
7707
7009
|
const srcPath = pkg.main.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts");
|
|
7708
|
-
if (
|
|
7010
|
+
if (existsSync5(resolve4(srcPath))) return resolve4(srcPath);
|
|
7709
7011
|
}
|
|
7710
7012
|
} catch {
|
|
7711
7013
|
}
|
|
@@ -7718,10 +7020,10 @@ function findTsEntryFile() {
|
|
|
7718
7020
|
"main.ts"
|
|
7719
7021
|
];
|
|
7720
7022
|
for (const c3 of candidates) {
|
|
7721
|
-
const full =
|
|
7722
|
-
if (
|
|
7023
|
+
const full = resolve4(c3);
|
|
7024
|
+
if (existsSync5(full)) {
|
|
7723
7025
|
try {
|
|
7724
|
-
const content =
|
|
7026
|
+
const content = readFileSync6(full, "utf-8");
|
|
7725
7027
|
if (content.includes("McpServer") || content.includes("McpServer")) {
|
|
7726
7028
|
return full;
|
|
7727
7029
|
}
|
|
@@ -7730,18 +7032,18 @@ function findTsEntryFile() {
|
|
|
7730
7032
|
}
|
|
7731
7033
|
}
|
|
7732
7034
|
for (const c3 of candidates) {
|
|
7733
|
-
if (
|
|
7035
|
+
if (existsSync5(resolve4(c3))) return resolve4(c3);
|
|
7734
7036
|
}
|
|
7735
7037
|
return null;
|
|
7736
7038
|
}
|
|
7737
7039
|
function detectPackageManager() {
|
|
7738
|
-
if (
|
|
7739
|
-
if (
|
|
7040
|
+
if (existsSync5(resolve4("pnpm-lock.yaml"))) return "pnpm";
|
|
7041
|
+
if (existsSync5(resolve4("yarn.lock"))) return "yarn";
|
|
7740
7042
|
return "npm";
|
|
7741
7043
|
}
|
|
7742
7044
|
function installSdk() {
|
|
7743
7045
|
try {
|
|
7744
|
-
const pkg = JSON.parse(
|
|
7046
|
+
const pkg = JSON.parse(readFileSync6(resolve4("package.json"), "utf-8"));
|
|
7745
7047
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7746
7048
|
if (allDeps["@solongate/proxy"]) {
|
|
7747
7049
|
log3(" @solongate/proxy already installed");
|
|
@@ -7762,7 +7064,7 @@ function installSdk() {
|
|
|
7762
7064
|
}
|
|
7763
7065
|
}
|
|
7764
7066
|
function injectTypeScript(filePath) {
|
|
7765
|
-
const original =
|
|
7067
|
+
const original = readFileSync6(filePath, "utf-8");
|
|
7766
7068
|
const changes = [];
|
|
7767
7069
|
let modified = original;
|
|
7768
7070
|
if (modified.includes("SecureMcpServer")) {
|
|
@@ -7866,7 +7168,7 @@ function showDiff(result) {
|
|
|
7866
7168
|
}
|
|
7867
7169
|
log3("");
|
|
7868
7170
|
}
|
|
7869
|
-
async function
|
|
7171
|
+
async function main2() {
|
|
7870
7172
|
const opts = parseInjectArgs(process.argv);
|
|
7871
7173
|
printBanner("Inject SDK");
|
|
7872
7174
|
if (!detectProject()) {
|
|
@@ -7878,8 +7180,8 @@ async function main3() {
|
|
|
7878
7180
|
process.exit(1);
|
|
7879
7181
|
}
|
|
7880
7182
|
log3(" Language: TypeScript");
|
|
7881
|
-
const entryFile = opts.file ?
|
|
7882
|
-
if (!entryFile || !
|
|
7183
|
+
const entryFile = opts.file ? resolve4(opts.file) : findTsEntryFile();
|
|
7184
|
+
if (!entryFile || !existsSync5(entryFile)) {
|
|
7883
7185
|
log3(` Could not find entry file.${opts.file ? ` File not found: ${opts.file}` : ""}`);
|
|
7884
7186
|
log3("");
|
|
7885
7187
|
log3(" Specify it manually: --file <path>");
|
|
@@ -7892,7 +7194,7 @@ async function main3() {
|
|
|
7892
7194
|
log3("");
|
|
7893
7195
|
const backupPath = entryFile + ".solongate-backup";
|
|
7894
7196
|
if (opts.restore) {
|
|
7895
|
-
if (!
|
|
7197
|
+
if (!existsSync5(backupPath)) {
|
|
7896
7198
|
log3(" No backup found. Nothing to restore.");
|
|
7897
7199
|
process.exit(1);
|
|
7898
7200
|
}
|
|
@@ -7928,12 +7230,12 @@ async function main3() {
|
|
|
7928
7230
|
log3(" To apply: npx @solongate/proxy inject");
|
|
7929
7231
|
process.exit(0);
|
|
7930
7232
|
}
|
|
7931
|
-
if (!
|
|
7233
|
+
if (!existsSync5(backupPath)) {
|
|
7932
7234
|
copyFileSync(entryFile, backupPath);
|
|
7933
7235
|
log3("");
|
|
7934
7236
|
log3(` Backup: ${backupPath}`);
|
|
7935
7237
|
}
|
|
7936
|
-
|
|
7238
|
+
writeFileSync4(entryFile, result.modified);
|
|
7937
7239
|
log3("");
|
|
7938
7240
|
log3(" \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\u2510");
|
|
7939
7241
|
log3(" \u2502 SolonGate SDK injected successfully! \u2502");
|
|
@@ -7953,7 +7255,7 @@ var init_inject = __esm({
|
|
|
7953
7255
|
"src/inject.ts"() {
|
|
7954
7256
|
"use strict";
|
|
7955
7257
|
init_cli_utils();
|
|
7956
|
-
|
|
7258
|
+
main2().catch((err) => {
|
|
7957
7259
|
log3(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
7958
7260
|
process.exit(1);
|
|
7959
7261
|
});
|
|
@@ -7962,8 +7264,8 @@ var init_inject = __esm({
|
|
|
7962
7264
|
|
|
7963
7265
|
// src/create.ts
|
|
7964
7266
|
var create_exports = {};
|
|
7965
|
-
import { mkdirSync as
|
|
7966
|
-
import { resolve as
|
|
7267
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, existsSync as existsSync6 } from "fs";
|
|
7268
|
+
import { resolve as resolve5, join as join5 } from "path";
|
|
7967
7269
|
import { execSync as execSync2 } from "child_process";
|
|
7968
7270
|
function withSpinner(message, fn) {
|
|
7969
7271
|
const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
|
|
@@ -8002,7 +7304,7 @@ function parseCreateArgs(argv) {
|
|
|
8002
7304
|
break;
|
|
8003
7305
|
case "--help":
|
|
8004
7306
|
case "-h":
|
|
8005
|
-
|
|
7307
|
+
printHelp2();
|
|
8006
7308
|
process.exit(0);
|
|
8007
7309
|
break;
|
|
8008
7310
|
default:
|
|
@@ -8024,7 +7326,7 @@ function parseCreateArgs(argv) {
|
|
|
8024
7326
|
}
|
|
8025
7327
|
return opts;
|
|
8026
7328
|
}
|
|
8027
|
-
function
|
|
7329
|
+
function printHelp2() {
|
|
8028
7330
|
log3(`
|
|
8029
7331
|
SolonGate Create \u2014 Scaffold a secure MCP server in seconds
|
|
8030
7332
|
|
|
@@ -8042,8 +7344,8 @@ EXAMPLES
|
|
|
8042
7344
|
`);
|
|
8043
7345
|
}
|
|
8044
7346
|
function createProject(dir, name, _policy) {
|
|
8045
|
-
|
|
8046
|
-
|
|
7347
|
+
writeFileSync5(
|
|
7348
|
+
join5(dir, "package.json"),
|
|
8047
7349
|
JSON.stringify(
|
|
8048
7350
|
{
|
|
8049
7351
|
name,
|
|
@@ -8072,8 +7374,8 @@ function createProject(dir, name, _policy) {
|
|
|
8072
7374
|
2
|
|
8073
7375
|
) + "\n"
|
|
8074
7376
|
);
|
|
8075
|
-
|
|
8076
|
-
|
|
7377
|
+
writeFileSync5(
|
|
7378
|
+
join5(dir, "tsconfig.json"),
|
|
8077
7379
|
JSON.stringify(
|
|
8078
7380
|
{
|
|
8079
7381
|
compilerOptions: {
|
|
@@ -8093,9 +7395,9 @@ function createProject(dir, name, _policy) {
|
|
|
8093
7395
|
2
|
|
8094
7396
|
) + "\n"
|
|
8095
7397
|
);
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
7398
|
+
mkdirSync5(join5(dir, "src"), { recursive: true });
|
|
7399
|
+
writeFileSync5(
|
|
7400
|
+
join5(dir, "src", "index.ts"),
|
|
8099
7401
|
`#!/usr/bin/env node
|
|
8100
7402
|
|
|
8101
7403
|
console.log = (...args: unknown[]) => {
|
|
@@ -8136,8 +7438,8 @@ console.log('');
|
|
|
8136
7438
|
console.log('Press Ctrl+C to stop.');
|
|
8137
7439
|
`
|
|
8138
7440
|
);
|
|
8139
|
-
|
|
8140
|
-
|
|
7441
|
+
writeFileSync5(
|
|
7442
|
+
join5(dir, ".mcp.json"),
|
|
8141
7443
|
JSON.stringify(
|
|
8142
7444
|
{
|
|
8143
7445
|
mcpServers: {
|
|
@@ -8154,13 +7456,13 @@ console.log('Press Ctrl+C to stop.');
|
|
|
8154
7456
|
2
|
|
8155
7457
|
) + "\n"
|
|
8156
7458
|
);
|
|
8157
|
-
|
|
8158
|
-
|
|
7459
|
+
writeFileSync5(
|
|
7460
|
+
join5(dir, ".env"),
|
|
8159
7461
|
`SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
|
|
8160
7462
|
`
|
|
8161
7463
|
);
|
|
8162
|
-
|
|
8163
|
-
|
|
7464
|
+
writeFileSync5(
|
|
7465
|
+
join5(dir, ".gitignore"),
|
|
8164
7466
|
`node_modules/
|
|
8165
7467
|
dist/
|
|
8166
7468
|
*.solongate-backup
|
|
@@ -8170,16 +7472,16 @@ dist/
|
|
|
8170
7472
|
`
|
|
8171
7473
|
);
|
|
8172
7474
|
}
|
|
8173
|
-
async function
|
|
7475
|
+
async function main3() {
|
|
8174
7476
|
const opts = parseCreateArgs(process.argv);
|
|
8175
|
-
const dir =
|
|
7477
|
+
const dir = resolve5(opts.name);
|
|
8176
7478
|
printBanner("Create MCP Server");
|
|
8177
|
-
if (
|
|
7479
|
+
if (existsSync6(dir)) {
|
|
8178
7480
|
log3(` ${c.red}Error:${c.reset} Directory "${opts.name}" already exists.`);
|
|
8179
7481
|
process.exit(1);
|
|
8180
7482
|
}
|
|
8181
7483
|
withSpinner(`Setting up ${opts.name}...`, () => {
|
|
8182
|
-
|
|
7484
|
+
mkdirSync5(dir, { recursive: true });
|
|
8183
7485
|
createProject(dir, opts.name, opts.policy);
|
|
8184
7486
|
});
|
|
8185
7487
|
if (!opts.noInstall) {
|
|
@@ -8244,7 +7546,7 @@ var init_create = __esm({
|
|
|
8244
7546
|
"src/create.ts"() {
|
|
8245
7547
|
"use strict";
|
|
8246
7548
|
init_cli_utils();
|
|
8247
|
-
|
|
7549
|
+
main3().catch((err) => {
|
|
8248
7550
|
log3(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
8249
7551
|
process.exit(1);
|
|
8250
7552
|
});
|
|
@@ -8253,14 +7555,14 @@ var init_create = __esm({
|
|
|
8253
7555
|
|
|
8254
7556
|
// src/pull-push.ts
|
|
8255
7557
|
var pull_push_exports = {};
|
|
8256
|
-
import { readFileSync as
|
|
8257
|
-
import { resolve as
|
|
7558
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7 } from "fs";
|
|
7559
|
+
import { resolve as resolve6 } from "path";
|
|
8258
7560
|
function loadEnv() {
|
|
8259
7561
|
if (process.env.SOLONGATE_API_KEY) return;
|
|
8260
|
-
const envPath =
|
|
8261
|
-
if (!
|
|
7562
|
+
const envPath = resolve6(".env");
|
|
7563
|
+
if (!existsSync7(envPath)) return;
|
|
8262
7564
|
try {
|
|
8263
|
-
const content =
|
|
7565
|
+
const content = readFileSync7(envPath, "utf-8");
|
|
8264
7566
|
for (const line of content.split("\n")) {
|
|
8265
7567
|
const trimmed = line.trim();
|
|
8266
7568
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -8315,7 +7617,7 @@ function parseCliArgs() {
|
|
|
8315
7617
|
log4(red("ERROR: Pull/push/list requires a live API key (sg_live_...)."));
|
|
8316
7618
|
process.exit(1);
|
|
8317
7619
|
}
|
|
8318
|
-
return { command, apiKey, file:
|
|
7620
|
+
return { command, apiKey, file: resolve6(file), policyId };
|
|
8319
7621
|
}
|
|
8320
7622
|
async function listPolicies(apiKey) {
|
|
8321
7623
|
const res = await fetch(`${API_URL}/api/v1/policies`, {
|
|
@@ -8448,7 +7750,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
8448
7750
|
const policy = await fetchCloudPolicy(apiKey, API_URL, policyId);
|
|
8449
7751
|
const { id: _id, ...policyWithoutId } = policy;
|
|
8450
7752
|
const json = JSON.stringify(policyWithoutId, null, 2) + "\n";
|
|
8451
|
-
|
|
7753
|
+
writeFileSync6(file, json, "utf-8");
|
|
8452
7754
|
log4("");
|
|
8453
7755
|
log4(green(" Saved to: ") + file);
|
|
8454
7756
|
log4(` ${dim("Name:")} ${policy.name}`);
|
|
@@ -8460,7 +7762,7 @@ async function pull(apiKey, file, policyId) {
|
|
|
8460
7762
|
log4("");
|
|
8461
7763
|
}
|
|
8462
7764
|
async function push(apiKey, file, policyId) {
|
|
8463
|
-
if (!
|
|
7765
|
+
if (!existsSync7(file)) {
|
|
8464
7766
|
log4(red(`ERROR: File not found: ${file}`));
|
|
8465
7767
|
process.exit(1);
|
|
8466
7768
|
}
|
|
@@ -8477,7 +7779,7 @@ async function push(apiKey, file, policyId) {
|
|
|
8477
7779
|
log4(" solongate-proxy list");
|
|
8478
7780
|
process.exit(1);
|
|
8479
7781
|
}
|
|
8480
|
-
const content =
|
|
7782
|
+
const content = readFileSync7(file, "utf-8");
|
|
8481
7783
|
let policy;
|
|
8482
7784
|
try {
|
|
8483
7785
|
policy = JSON.parse(content);
|
|
@@ -8520,7 +7822,7 @@ async function push(apiKey, file, policyId) {
|
|
|
8520
7822
|
log4(` ${dim("Method:")} ${method === "PUT" ? "Updated existing" : "Created new"}`);
|
|
8521
7823
|
log4("");
|
|
8522
7824
|
}
|
|
8523
|
-
async function
|
|
7825
|
+
async function main4() {
|
|
8524
7826
|
const { command, apiKey, file, policyId } = parseCliArgs();
|
|
8525
7827
|
try {
|
|
8526
7828
|
if (command === "pull") {
|
|
@@ -8565,7 +7867,7 @@ var init_pull_push = __esm({
|
|
|
8565
7867
|
cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
8566
7868
|
magenta = (s) => `\x1B[35m${s}\x1B[0m`;
|
|
8567
7869
|
API_URL = "https://api.solongate.com";
|
|
8568
|
-
|
|
7870
|
+
main4();
|
|
8569
7871
|
}
|
|
8570
7872
|
});
|
|
8571
7873
|
|
|
@@ -10994,7 +10296,7 @@ var Mutex = class {
|
|
|
10994
10296
|
this.locked = true;
|
|
10995
10297
|
return;
|
|
10996
10298
|
}
|
|
10997
|
-
return new Promise((
|
|
10299
|
+
return new Promise((resolve7, reject) => {
|
|
10998
10300
|
const timer = setTimeout(() => {
|
|
10999
10301
|
const idx = this.queue.indexOf(onReady);
|
|
11000
10302
|
if (idx !== -1) this.queue.splice(idx, 1);
|
|
@@ -11002,7 +10304,7 @@ var Mutex = class {
|
|
|
11002
10304
|
}, timeoutMs);
|
|
11003
10305
|
const onReady = () => {
|
|
11004
10306
|
clearTimeout(timer);
|
|
11005
|
-
|
|
10307
|
+
resolve7();
|
|
11006
10308
|
};
|
|
11007
10309
|
this.queue.push(onReady);
|
|
11008
10310
|
});
|
|
@@ -11622,7 +10924,7 @@ ${msg.content.text}`;
|
|
|
11622
10924
|
|
|
11623
10925
|
// src/index.ts
|
|
11624
10926
|
init_cli_utils();
|
|
11625
|
-
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["
|
|
10927
|
+
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["login", "logout", "create", "inject", "pull", "push", "list", "ls"]);
|
|
11626
10928
|
var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "");
|
|
11627
10929
|
if (!IS_HUMAN_CLI) {
|
|
11628
10930
|
console.log = (...args) => {
|
|
@@ -11651,17 +10953,12 @@ function printWelcome() {
|
|
|
11651
10953
|
console.log(` ${c.cyan}https://dashboard.solongate.com${c.reset}`);
|
|
11652
10954
|
console.log("");
|
|
11653
10955
|
}
|
|
11654
|
-
async function
|
|
10956
|
+
async function main5() {
|
|
11655
10957
|
const subcommand = process.argv[2];
|
|
11656
10958
|
if (process.argv.length <= 2) {
|
|
11657
10959
|
printWelcome();
|
|
11658
10960
|
return;
|
|
11659
10961
|
}
|
|
11660
|
-
if (subcommand === "init") {
|
|
11661
|
-
process.argv.splice(2, 1);
|
|
11662
|
-
await Promise.resolve().then(() => (init_init(), init_exports));
|
|
11663
|
-
return;
|
|
11664
|
-
}
|
|
11665
10962
|
if (subcommand === "login") {
|
|
11666
10963
|
await Promise.resolve().then(() => (init_login(), login_exports));
|
|
11667
10964
|
return;
|
|
@@ -11696,4 +10993,4 @@ async function main6() {
|
|
|
11696
10993
|
process.exit(1);
|
|
11697
10994
|
}
|
|
11698
10995
|
}
|
|
11699
|
-
|
|
10996
|
+
main5();
|