@solongate/proxy 0.82.17 → 0.82.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +64 -8
- package/hooks/guard.bundled.mjs +39 -20
- package/hooks/guard.mjs +43 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13964,6 +13964,9 @@ var init_pull_push = __esm({
|
|
|
13964
13964
|
|
|
13965
13965
|
// src/index.ts
|
|
13966
13966
|
init_config();
|
|
13967
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
13968
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13969
|
+
import { dirname as dirname4, join as join17 } from "path";
|
|
13967
13970
|
|
|
13968
13971
|
// src/proxy.ts
|
|
13969
13972
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -16987,7 +16990,8 @@ ${msg.content.text}`;
|
|
|
16987
16990
|
// src/index.ts
|
|
16988
16991
|
init_cli_utils();
|
|
16989
16992
|
var CLI_SUBCOMMANDS = /* @__PURE__ */ new Set(["shield", "create", "inject", "pull", "push", "list", "ls", "logs-server", "local-logs", "policy", "ratelimit", "dlp", "stats", "audit", "agents", "agent", "doctor", "watch", "keys", "mcp", "alerts", "webhooks", "dataroom"]);
|
|
16990
|
-
var
|
|
16993
|
+
var CLI_INFO_ARGS = /* @__PURE__ */ new Set(["login", "help", "--help", "-h", "--version", "-v", "version"]);
|
|
16994
|
+
var IS_HUMAN_CLI = process.argv.length <= 2 || CLI_SUBCOMMANDS.has(process.argv[2] ?? "") || CLI_INFO_ARGS.has(process.argv[2] ?? "");
|
|
16991
16995
|
if (!IS_HUMAN_CLI) {
|
|
16992
16996
|
console.log = (...args) => {
|
|
16993
16997
|
process.stderr.write(`[SolonGate] ${args.map(String).join(" ")}
|
|
@@ -17002,24 +17006,76 @@ if (!IS_HUMAN_CLI) {
|
|
|
17002
17006
|
`);
|
|
17003
17007
|
};
|
|
17004
17008
|
}
|
|
17009
|
+
var PKG_VERSION = (() => {
|
|
17010
|
+
try {
|
|
17011
|
+
const p = join17(dirname4(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
17012
|
+
return JSON.parse(readFileSync15(p, "utf-8")).version || "unknown";
|
|
17013
|
+
} catch {
|
|
17014
|
+
return "unknown";
|
|
17015
|
+
}
|
|
17016
|
+
})();
|
|
17005
17017
|
function printWelcome() {
|
|
17006
17018
|
console.log("");
|
|
17007
|
-
console.log(` ${c.bold}${c.blue4}SolonGate${c.reset} ${c.dim}
|
|
17019
|
+
console.log(` ${c.bold}${c.blue4}SolonGate${c.reset} ${c.dim}secure gateway for your AI agents${c.reset}`);
|
|
17008
17020
|
console.log("");
|
|
17009
17021
|
console.log(" Get started with one command:");
|
|
17010
17022
|
console.log("");
|
|
17011
|
-
console.log(` ${c.cyan}solongate${c.reset}`);
|
|
17023
|
+
console.log(` ${c.cyan}solongate${c.reset} ${c.dim}open the dataroom (login, policies, audit, settings)${c.reset}`);
|
|
17024
|
+
console.log(` ${c.cyan}solongate --help${c.reset} ${c.dim}list every command${c.reset}`);
|
|
17012
17025
|
console.log("");
|
|
17013
|
-
console.log(` ${c.dim}
|
|
17014
|
-
console.log(` ${c.dim}device
|
|
17026
|
+
console.log(` ${c.dim}Open the dataroom, log in from the Accounts panel to pair this${c.reset}`);
|
|
17027
|
+
console.log(` ${c.dim}device, then the guard protects every AI session with your policy.${c.reset}`);
|
|
17015
17028
|
console.log(` ${c.dim}Manage it at ${c.reset}${c.cyan}https://dashboard.solongate.com${c.reset}`);
|
|
17016
17029
|
console.log("");
|
|
17017
|
-
|
|
17018
|
-
|
|
17030
|
+
}
|
|
17031
|
+
function printHelp3() {
|
|
17032
|
+
const row = (name, desc) => console.log(` ${c.cyan}${name.padEnd(13)}${c.reset}${c.dim}${desc}${c.reset}`);
|
|
17033
|
+
console.log("");
|
|
17034
|
+
console.log(` ${c.bold}${c.blue4}SolonGate${c.reset} ${c.dim}secure gateway for your AI agents${c.reset}`);
|
|
17035
|
+
console.log("");
|
|
17036
|
+
console.log(` ${c.dim}Usage:${c.reset} ${c.cyan}solongate${c.reset} ${c.dim}[command]${c.reset}`);
|
|
17037
|
+
console.log(` ${c.dim}With no command, opens the dataroom: accounts, policies, rate limits,${c.reset}`);
|
|
17038
|
+
console.log(` ${c.dim}DLP, audit and settings in one terminal UI.${c.reset}`);
|
|
17039
|
+
console.log("");
|
|
17040
|
+
console.log(` ${c.bold}Setup & status${c.reset}`);
|
|
17041
|
+
row("doctor", "health check: login, active policy, guard, local logs");
|
|
17042
|
+
row("create", "scaffold a guarded MCP server in the current folder");
|
|
17043
|
+
row("logs-server", "serve local audit logs to the dashboard");
|
|
17044
|
+
console.log("");
|
|
17045
|
+
console.log(` ${c.bold}Security config${c.reset}`);
|
|
17046
|
+
row("policy", "view and manage policies");
|
|
17047
|
+
row("ratelimit", "view and manage rate limits");
|
|
17048
|
+
row("dlp", "view and manage DLP (secret) rules");
|
|
17049
|
+
row("alerts", "view and manage alerts");
|
|
17050
|
+
row("webhooks", "view and manage webhooks");
|
|
17051
|
+
console.log("");
|
|
17052
|
+
console.log(` ${c.bold}Monitoring${c.reset}`);
|
|
17053
|
+
row("audit", "browse the audit log of tool-call decisions");
|
|
17054
|
+
row("stats", "usage statistics");
|
|
17055
|
+
row("watch", "live-tail tool calls as they happen");
|
|
17056
|
+
row("agents", "list agents and sessions");
|
|
17057
|
+
console.log("");
|
|
17058
|
+
console.log(` ${c.bold}Advanced${c.reset}`);
|
|
17059
|
+
row("keys", "manage API keys");
|
|
17060
|
+
row("mcp", "manage MCP servers");
|
|
17061
|
+
row("shield", "wrap a command with LLM-path secret redaction: shield -- <cmd>");
|
|
17062
|
+
row("pull", "pull a cloud policy to a local file");
|
|
17063
|
+
row("push", "push a local policy file to the cloud");
|
|
17064
|
+
row("list", "list cloud policies");
|
|
17065
|
+
console.log("");
|
|
17066
|
+
console.log(` ${c.dim}Dashboard: ${c.reset}${c.cyan}https://dashboard.solongate.com${c.reset}`);
|
|
17019
17067
|
console.log("");
|
|
17020
17068
|
}
|
|
17021
17069
|
async function main4() {
|
|
17022
17070
|
const subcommand = process.argv[2];
|
|
17071
|
+
if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
|
|
17072
|
+
printHelp3();
|
|
17073
|
+
return;
|
|
17074
|
+
}
|
|
17075
|
+
if (subcommand === "--version" || subcommand === "-v" || subcommand === "version") {
|
|
17076
|
+
console.log(PKG_VERSION);
|
|
17077
|
+
return;
|
|
17078
|
+
}
|
|
17023
17079
|
if (IS_HUMAN_CLI) {
|
|
17024
17080
|
const tuiBound = subcommand === "dataroom" || process.argv.length <= 2 && process.stdout.isTTY && process.stdin.isTTY;
|
|
17025
17081
|
if (!tuiBound) {
|
|
@@ -17054,7 +17110,7 @@ async function main4() {
|
|
|
17054
17110
|
if (subcommand === "login") {
|
|
17055
17111
|
console.log("");
|
|
17056
17112
|
console.log(` ${c.dim}\`solongate login\` was removed.${c.reset} Run ${c.cyan}solongate${c.reset} and add your`);
|
|
17057
|
-
console.log(` account from the Accounts panel
|
|
17113
|
+
console.log(` account from the Accounts panel. The guard installs and updates from there.`);
|
|
17058
17114
|
console.log("");
|
|
17059
17115
|
return;
|
|
17060
17116
|
}
|
package/hooks/guard.bundled.mjs
CHANGED
|
@@ -6536,7 +6536,7 @@ import { resolve, join, dirname, isAbsolute } from "node:path";
|
|
|
6536
6536
|
import { homedir } from "node:os";
|
|
6537
6537
|
import { gunzipSync } from "node:zlib";
|
|
6538
6538
|
import { createHash } from "node:crypto";
|
|
6539
|
-
var HOOK_VERSION =
|
|
6539
|
+
var HOOK_VERSION = 46;
|
|
6540
6540
|
function localLogsOnly(security) {
|
|
6541
6541
|
if (security && typeof security === "object") {
|
|
6542
6542
|
const l = security.localLogs;
|
|
@@ -6789,6 +6789,7 @@ if (REFRESH_MODE) {
|
|
|
6789
6789
|
}
|
|
6790
6790
|
var SG_DONE = Symbol("sg-done");
|
|
6791
6791
|
var _sgDone = false;
|
|
6792
|
+
var _decisionEmitted = false;
|
|
6792
6793
|
function sgFinish(code) {
|
|
6793
6794
|
if (!_sgDone) {
|
|
6794
6795
|
_sgDone = true;
|
|
@@ -6798,34 +6799,46 @@ function sgFinish(code) {
|
|
|
6798
6799
|
}
|
|
6799
6800
|
function blockTool(reason) {
|
|
6800
6801
|
if (AGENT_TYPE === "antigravity") {
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6802
|
+
if (!_decisionEmitted) {
|
|
6803
|
+
_decisionEmitted = true;
|
|
6804
|
+
process.stdout.write(JSON.stringify({
|
|
6805
|
+
allow_tool: false,
|
|
6806
|
+
deny_reason: `[SolonGate] ${reason}`
|
|
6807
|
+
}));
|
|
6808
|
+
}
|
|
6805
6809
|
sgFinish(0);
|
|
6806
6810
|
} else {
|
|
6807
6811
|
const msg = reason || "[SolonGate] Blocked by policy";
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6812
|
+
if (!_decisionEmitted) {
|
|
6813
|
+
_decisionEmitted = true;
|
|
6814
|
+
process.stdout.write(JSON.stringify({
|
|
6815
|
+
hookSpecificOutput: {
|
|
6816
|
+
hookEventName: "PreToolUse",
|
|
6817
|
+
permissionDecision: "deny",
|
|
6818
|
+
permissionDecisionReason: msg
|
|
6819
|
+
}
|
|
6820
|
+
}));
|
|
6821
|
+
process.stderr.write(msg + "\n");
|
|
6822
|
+
}
|
|
6816
6823
|
sgFinish(2);
|
|
6817
6824
|
}
|
|
6818
6825
|
}
|
|
6819
6826
|
function allowTool() {
|
|
6820
6827
|
if (AGENT_TYPE === "antigravity") {
|
|
6821
|
-
|
|
6828
|
+
if (!_decisionEmitted) {
|
|
6829
|
+
_decisionEmitted = true;
|
|
6830
|
+
process.stdout.write(JSON.stringify({ allow_tool: true }));
|
|
6831
|
+
}
|
|
6822
6832
|
}
|
|
6823
6833
|
sgFinish(0);
|
|
6824
6834
|
}
|
|
6825
6835
|
function rewriteTool(updatedInput) {
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6836
|
+
if (!_decisionEmitted) {
|
|
6837
|
+
_decisionEmitted = true;
|
|
6838
|
+
process.stdout.write(JSON.stringify({
|
|
6839
|
+
hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", updatedInput }
|
|
6840
|
+
}));
|
|
6841
|
+
}
|
|
6829
6842
|
sgFinish(0);
|
|
6830
6843
|
}
|
|
6831
6844
|
function writeDenyFlag(toolName) {
|
|
@@ -7966,11 +7979,17 @@ if (!REFRESH_MODE) {
|
|
|
7966
7979
|
}
|
|
7967
7980
|
await maybeSelfUpdate();
|
|
7968
7981
|
if (AGENT_TYPE === "antigravity") {
|
|
7969
|
-
|
|
7982
|
+
if (!_decisionEmitted) {
|
|
7983
|
+
_decisionEmitted = true;
|
|
7984
|
+
process.stdout.write(JSON.stringify({ allow_tool: false, deny_reason: ghostHit }));
|
|
7985
|
+
}
|
|
7970
7986
|
sgFinish(0);
|
|
7971
7987
|
}
|
|
7972
|
-
|
|
7973
|
-
|
|
7988
|
+
if (!_decisionEmitted) {
|
|
7989
|
+
_decisionEmitted = true;
|
|
7990
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: ghostHit } }));
|
|
7991
|
+
process.stderr.write(ghostHit + "\n");
|
|
7992
|
+
}
|
|
7974
7993
|
sgFinish(2);
|
|
7975
7994
|
}
|
|
7976
7995
|
if (AGENT_TYPE !== "antigravity" && toolName === "Bash") {
|
package/hooks/guard.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import { createHash } from 'node:crypto';
|
|
|
32
32
|
// the installed hook self-updates when the cloud version is higher (see
|
|
33
33
|
// maybeSelfUpdate). This is what makes guard fixes propagate without a manual
|
|
34
34
|
// reinstall — the same trust model as the OPA WASM this hook already runs.
|
|
35
|
-
const HOOK_VERSION =
|
|
35
|
+
const HOOK_VERSION = 46;
|
|
36
36
|
|
|
37
37
|
// True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
|
|
38
38
|
// nothing is sent to the cloud audit log.
|
|
@@ -365,6 +365,14 @@ if (REFRESH_MODE) { try { setTimeout(() => { try { process.exit(process.exitCode
|
|
|
365
365
|
// the threadpool has drained, so exit() is safe.
|
|
366
366
|
const SG_DONE = Symbol('sg-done');
|
|
367
367
|
let _sgDone = false;
|
|
368
|
+
// The decision JSON must be written to stdout AT MOST ONCE. The handler can reach
|
|
369
|
+
// a terminal allowTool() even after a block already emitted its deny (the SG_DONE
|
|
370
|
+
// unwind is swallowed on some paths), which for Claude Code was harmless
|
|
371
|
+
// (allowTool writes nothing there) but for Antigravity appended a second
|
|
372
|
+
// {"allow_tool":true} right after {"allow_tool":false} — agy then parsed the
|
|
373
|
+
// trailing allow (or failed on the double object) and ran the blocked tool. This
|
|
374
|
+
// latch guarantees the FIRST decision is the only one on stdout.
|
|
375
|
+
let _decisionEmitted = false;
|
|
368
376
|
// Terminate WITHOUT ever calling process.exit() on the hot path. On Windows + Node
|
|
369
377
|
// 24, process.exit() called in (or right after) the same tick a cloud fetch()
|
|
370
378
|
// settled aborts with `Assertion failed: !(handle->flags & UV_HANDLE_CLOSING),
|
|
@@ -384,10 +392,13 @@ function sgFinish(code) {
|
|
|
384
392
|
|
|
385
393
|
function blockTool(reason) {
|
|
386
394
|
if (AGENT_TYPE === 'antigravity') {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
395
|
+
if (!_decisionEmitted) {
|
|
396
|
+
_decisionEmitted = true;
|
|
397
|
+
process.stdout.write(JSON.stringify({
|
|
398
|
+
allow_tool: false,
|
|
399
|
+
deny_reason: `[SolonGate] ${reason}`,
|
|
400
|
+
}));
|
|
401
|
+
}
|
|
391
402
|
sgFinish(0);
|
|
392
403
|
} else {
|
|
393
404
|
// Claude Code — emit the JSON PreToolUse deny on stdout AND exit 2 (reason on
|
|
@@ -400,21 +411,27 @@ function blockTool(reason) {
|
|
|
400
411
|
// propagate reliably; Claude Code parses the JSON instead). Reason is never
|
|
401
412
|
// empty — an empty deny becomes an is_error tool_result the API rejects.
|
|
402
413
|
const msg = reason || '[SolonGate] Blocked by policy';
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
414
|
+
if (!_decisionEmitted) {
|
|
415
|
+
_decisionEmitted = true;
|
|
416
|
+
process.stdout.write(JSON.stringify({
|
|
417
|
+
hookSpecificOutput: {
|
|
418
|
+
hookEventName: 'PreToolUse',
|
|
419
|
+
permissionDecision: 'deny',
|
|
420
|
+
permissionDecisionReason: msg,
|
|
421
|
+
},
|
|
422
|
+
}));
|
|
423
|
+
process.stderr.write(msg + '\n');
|
|
424
|
+
}
|
|
411
425
|
sgFinish(2);
|
|
412
426
|
}
|
|
413
427
|
}
|
|
414
428
|
|
|
415
429
|
function allowTool() {
|
|
416
430
|
if (AGENT_TYPE === 'antigravity') {
|
|
417
|
-
|
|
431
|
+
if (!_decisionEmitted) {
|
|
432
|
+
_decisionEmitted = true;
|
|
433
|
+
process.stdout.write(JSON.stringify({ allow_tool: true }));
|
|
434
|
+
}
|
|
418
435
|
}
|
|
419
436
|
sgFinish(0);
|
|
420
437
|
}
|
|
@@ -423,9 +440,12 @@ function allowTool() {
|
|
|
423
440
|
// ghost layer to rewrite a listing command so hidden entries are filtered out of
|
|
424
441
|
// its output — the agent never sees them. Claude Code only.
|
|
425
442
|
function rewriteTool(updatedInput) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
443
|
+
if (!_decisionEmitted) {
|
|
444
|
+
_decisionEmitted = true;
|
|
445
|
+
process.stdout.write(JSON.stringify({
|
|
446
|
+
hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'allow', updatedInput },
|
|
447
|
+
}));
|
|
448
|
+
}
|
|
429
449
|
sgFinish(0);
|
|
430
450
|
}
|
|
431
451
|
|
|
@@ -1821,13 +1841,16 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
|
|
|
1821
1841
|
});
|
|
1822
1842
|
} catch {}
|
|
1823
1843
|
await maybeSelfUpdate();
|
|
1824
|
-
if (AGENT_TYPE === 'antigravity') { process.stdout.write(JSON.stringify({ allow_tool: false, deny_reason: ghostHit })); sgFinish(0); }
|
|
1844
|
+
if (AGENT_TYPE === 'antigravity') { if (!_decisionEmitted) { _decisionEmitted = true; process.stdout.write(JSON.stringify({ allow_tool: false, deny_reason: ghostHit })); } sgFinish(0); }
|
|
1825
1845
|
// Claude Code: JSON deny on stdout + exit 2 (reason on stderr). Exit 2 is
|
|
1826
1846
|
// the only block enforced in auto-accept mode (see blockTool); the JSON is
|
|
1827
1847
|
// the Windows fallback. ghostHit is a bare "No such file or directory", so
|
|
1828
1848
|
// the stderr text keeps the stealth (looks like the path simply isn't there).
|
|
1829
|
-
|
|
1830
|
-
|
|
1849
|
+
if (!_decisionEmitted) {
|
|
1850
|
+
_decisionEmitted = true;
|
|
1851
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ghostHit } }));
|
|
1852
|
+
process.stderr.write(ghostHit + '\n');
|
|
1853
|
+
}
|
|
1831
1854
|
sgFinish(2);
|
|
1832
1855
|
}
|
|
1833
1856
|
// No direct hit: if this is a listing command, rewrite it so hidden
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.19",
|
|
4
4
|
"description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|