fullcourtdefense-cli 1.3.1 → 1.3.3
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/api.d.ts +13 -0
- package/dist/api.js +21 -2
- package/dist/commands/configure.d.ts +3 -0
- package/dist/commands/configure.js +56 -11
- package/dist/commands/discover.d.ts +2 -0
- package/dist/commands/discover.js +218 -102
- package/dist/commands/discoverAgentFiles.d.ts +15 -0
- package/dist/commands/discoverAgentFiles.js +204 -0
- package/dist/commands/discoverBlastRadius.d.ts +30 -0
- package/dist/commands/discoverBlastRadius.js +139 -0
- package/dist/commands/discoverSecrets.d.ts +24 -0
- package/dist/commands/discoverSecrets.js +286 -0
- package/dist/commands/hook.js +9 -27
- package/dist/commands/installAll.d.ts +11 -0
- package/dist/commands/installAll.js +55 -0
- package/dist/commands/installCursorHook.js +16 -27
- package/dist/commands/mcpGateway.d.ts +23 -0
- package/dist/commands/mcpGateway.js +313 -67
- package/dist/config.d.ts +26 -0
- package/dist/config.js +92 -16
- package/dist/index.js +191 -120
- package/dist/version.json +3 -0
- package/package.json +1 -1
|
@@ -33,19 +33,44 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ALL_GATEWAY_CLIENTS = void 0;
|
|
36
37
|
exports.mcpGatewayCommand = mcpGatewayCommand;
|
|
37
38
|
exports.installCursorMcpGatewayCommand = installCursorMcpGatewayCommand;
|
|
38
39
|
exports.installMcpGatewayCommand = installMcpGatewayCommand;
|
|
39
40
|
exports.installClaudeCodeMcpGatewayCommand = installClaudeCodeMcpGatewayCommand;
|
|
40
41
|
exports.installClaudeDesktopMcpGatewayCommand = installClaudeDesktopMcpGatewayCommand;
|
|
42
|
+
exports.installCodexMcpGatewayCommand = installCodexMcpGatewayCommand;
|
|
43
|
+
exports.installGeminiMcpGatewayCommand = installGeminiMcpGatewayCommand;
|
|
44
|
+
exports.installWindsurfMcpGatewayCommand = installWindsurfMcpGatewayCommand;
|
|
45
|
+
exports.installVscodeMcpGatewayCommand = installVscodeMcpGatewayCommand;
|
|
41
46
|
const fs = __importStar(require("fs"));
|
|
42
47
|
const os = __importStar(require("os"));
|
|
43
48
|
const path = __importStar(require("path"));
|
|
44
49
|
const child_process_1 = require("child_process");
|
|
50
|
+
const config_1 = require("../config");
|
|
45
51
|
const discoverPaths_1 = require("./discoverPaths");
|
|
46
52
|
const discover_1 = require("./discover");
|
|
47
53
|
const DEFAULT_API_URL = 'https://api.fullcourtdefense.ai';
|
|
48
54
|
const MANAGED_SERVER_NAME = 'agentguard-gateway';
|
|
55
|
+
const INSTALL_GATEWAY_ARGS = { omitSavedCredentials: true };
|
|
56
|
+
exports.ALL_GATEWAY_CLIENTS = [
|
|
57
|
+
'cursor',
|
|
58
|
+
'claude-code',
|
|
59
|
+
'claude-desktop',
|
|
60
|
+
'codex',
|
|
61
|
+
'gemini-cli',
|
|
62
|
+
'windsurf',
|
|
63
|
+
'vscode',
|
|
64
|
+
];
|
|
65
|
+
const CLIENT_LABELS = {
|
|
66
|
+
cursor: 'Cursor',
|
|
67
|
+
'claude-code': 'Claude Code',
|
|
68
|
+
'claude-desktop': 'Claude Desktop',
|
|
69
|
+
codex: 'Codex',
|
|
70
|
+
'gemini-cli': 'Gemini CLI',
|
|
71
|
+
windsurf: 'Windsurf',
|
|
72
|
+
vscode: 'VS Code',
|
|
73
|
+
};
|
|
49
74
|
function parseArgsList(value) {
|
|
50
75
|
if (!value?.trim())
|
|
51
76
|
return [];
|
|
@@ -67,28 +92,19 @@ function parseArgsList(value) {
|
|
|
67
92
|
}
|
|
68
93
|
return trimmed.match(/(?:[^\s"]+|"[^"]*")+/g)?.map(part => part.replace(/^"|"$/g, '')) ?? [];
|
|
69
94
|
}
|
|
70
|
-
function loadHomeShield() {
|
|
71
|
-
try {
|
|
72
|
-
for (const name of ['.fullcourtdefense.yml', '.fullcourtdefense.yaml']) {
|
|
73
|
-
const file = path.join(os.homedir(), name);
|
|
74
|
-
if (!fs.existsSync(file))
|
|
75
|
-
continue;
|
|
76
|
-
const text = fs.readFileSync(file, 'utf8');
|
|
77
|
-
const pick = (key) => {
|
|
78
|
-
const match = text.match(new RegExp(`^\\s*${key}\\s*:\\s*(.+)\\s*$`, 'm'));
|
|
79
|
-
return match?.[1]?.replace(/^["']|["']$/g, '').trim() || undefined;
|
|
80
|
-
};
|
|
81
|
-
return { shieldId: pick('shieldId'), shieldKey: pick('shieldKey'), apiUrl: pick('apiUrl') };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
// Ignore config read errors; command-line flags/env can still provide config.
|
|
86
|
-
}
|
|
87
|
-
return {};
|
|
88
|
-
}
|
|
89
95
|
function normalizeAgentClient(value, fallback) {
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
const map = {
|
|
97
|
+
cursor: 'cursor',
|
|
98
|
+
'claude-code': 'claude-code',
|
|
99
|
+
'claude-desktop': 'claude-desktop',
|
|
100
|
+
codex: 'codex',
|
|
101
|
+
'gemini-cli': 'gemini-cli',
|
|
102
|
+
gemini: 'gemini-cli',
|
|
103
|
+
windsurf: 'windsurf',
|
|
104
|
+
vscode: 'vscode',
|
|
105
|
+
};
|
|
106
|
+
if (value && map[value])
|
|
107
|
+
return map[value];
|
|
92
108
|
return fallback;
|
|
93
109
|
}
|
|
94
110
|
/**
|
|
@@ -140,16 +156,20 @@ function defaultAgentName(agentClient, developerName, defaults) {
|
|
|
140
156
|
return `${safeIdentityPart(developerName)}-${prefix}`;
|
|
141
157
|
}
|
|
142
158
|
function resolveGatewayConfig(args, config, defaults = {}) {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
const creds = (0, config_1.resolveCliCredentials)(config, {
|
|
160
|
+
shieldId: args.shieldId,
|
|
161
|
+
shieldKey: args.shieldKey,
|
|
162
|
+
apiUrl: args.apiUrl,
|
|
163
|
+
});
|
|
164
|
+
if (!creds.shieldId) {
|
|
165
|
+
throw new Error('Missing Shield ID. Run `fullcourtdefense setup` once (saves org, API key, Shield ID, and Shield key to ~/.fullcourtdefense.yml).');
|
|
166
|
+
}
|
|
147
167
|
const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT || detectRuntimeAgentClient(), defaults.agentClient || 'cursor');
|
|
148
168
|
const developerName = detectedDeveloperName(args);
|
|
149
169
|
return {
|
|
150
|
-
shieldId,
|
|
151
|
-
shieldKey:
|
|
152
|
-
apiUrl:
|
|
170
|
+
shieldId: creds.shieldId,
|
|
171
|
+
shieldKey: creds.shieldKey,
|
|
172
|
+
apiUrl: creds.apiUrl,
|
|
153
173
|
agentName: args.agentName || process.env.FCD_AGENT_NAME || defaultAgentName(agentClient, developerName, defaults),
|
|
154
174
|
agentClient,
|
|
155
175
|
developerName,
|
|
@@ -648,7 +668,6 @@ function buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArg
|
|
|
648
668
|
'--agent-name', gatewayConfig.agentName,
|
|
649
669
|
'--agent-client', gatewayConfig.agentClient,
|
|
650
670
|
'--developer-name', gatewayConfig.developerName,
|
|
651
|
-
'--shield-id', gatewayConfig.shieldId,
|
|
652
671
|
'--user-objective', gatewayConfig.userObjective || 'Developer requested this MCP action.',
|
|
653
672
|
'--authority', gatewayConfig.authority || 'developer',
|
|
654
673
|
'--approval-mode', gatewayConfig.approvalMode,
|
|
@@ -657,10 +676,14 @@ function buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArg
|
|
|
657
676
|
'--scan-response', String(gatewayConfig.scanResponse),
|
|
658
677
|
'--fail-closed', String(gatewayConfig.failClosed),
|
|
659
678
|
];
|
|
660
|
-
if (
|
|
661
|
-
commandArgs.push('--shield-
|
|
662
|
-
|
|
663
|
-
|
|
679
|
+
if (!options.omitSavedCredentials) {
|
|
680
|
+
commandArgs.push('--shield-id', gatewayConfig.shieldId);
|
|
681
|
+
if (gatewayConfig.shieldKey && options.includeShieldKey !== false) {
|
|
682
|
+
commandArgs.push('--shield-key', gatewayConfig.shieldKey);
|
|
683
|
+
}
|
|
684
|
+
if (gatewayConfig.apiUrl)
|
|
685
|
+
commandArgs.push('--api-url', gatewayConfig.apiUrl);
|
|
686
|
+
}
|
|
664
687
|
if (gatewayConfig.environment)
|
|
665
688
|
commandArgs.push('--environment', gatewayConfig.environment);
|
|
666
689
|
return commandArgs;
|
|
@@ -674,6 +697,86 @@ function writeMcpServerConfig(file, serverName, nodeExe, commandArgs) {
|
|
|
674
697
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
675
698
|
fs.writeFileSync(file, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
676
699
|
}
|
|
700
|
+
function readJsonObject(file) {
|
|
701
|
+
if (!fs.existsSync(file))
|
|
702
|
+
return {};
|
|
703
|
+
try {
|
|
704
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
705
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
706
|
+
}
|
|
707
|
+
catch {
|
|
708
|
+
return {};
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
function writeJsonMcpServer(file, serverName, nodeExe, commandArgs) {
|
|
712
|
+
const json = readJsonObject(file);
|
|
713
|
+
if (!json.mcpServers || typeof json.mcpServers !== 'object')
|
|
714
|
+
json.mcpServers = {};
|
|
715
|
+
json.mcpServers[serverName] = { command: nodeExe, args: commandArgs };
|
|
716
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
717
|
+
fs.writeFileSync(file, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
718
|
+
}
|
|
719
|
+
function removeTomlSection(content, tableName) {
|
|
720
|
+
const lines = content.split(/\r?\n/);
|
|
721
|
+
const out = [];
|
|
722
|
+
let skipping = false;
|
|
723
|
+
for (const line of lines) {
|
|
724
|
+
const trimmed = line.trim();
|
|
725
|
+
if (/^\[/.test(trimmed)) {
|
|
726
|
+
skipping = trimmed === `[${tableName}]` || trimmed.startsWith(`[${tableName}.`);
|
|
727
|
+
if (!skipping)
|
|
728
|
+
out.push(line);
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
if (!skipping)
|
|
732
|
+
out.push(line);
|
|
733
|
+
}
|
|
734
|
+
return out.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
735
|
+
}
|
|
736
|
+
function writeCodexMcpServer(configPath, serverName, nodeExe, commandArgs) {
|
|
737
|
+
const existing = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
|
|
738
|
+
const cleaned = removeTomlSection(existing, `mcp_servers.${serverName}`);
|
|
739
|
+
const argsToml = commandArgs.map(arg => JSON.stringify(arg)).join(', ');
|
|
740
|
+
const block = [
|
|
741
|
+
`[mcp_servers.${serverName}]`,
|
|
742
|
+
`command = ${JSON.stringify(nodeExe)}`,
|
|
743
|
+
`args = [${argsToml}]`,
|
|
744
|
+
].join('\n');
|
|
745
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
746
|
+
fs.writeFileSync(configPath, `${cleaned ? `${cleaned}\n\n` : ''}${block}\n`, 'utf8');
|
|
747
|
+
}
|
|
748
|
+
function codexConfigPath(projectScope) {
|
|
749
|
+
return projectScope
|
|
750
|
+
? path.join(process.cwd(), '.codex', 'config.toml')
|
|
751
|
+
: path.join(os.homedir(), '.codex', 'config.toml');
|
|
752
|
+
}
|
|
753
|
+
function geminiSettingsPath(projectScope) {
|
|
754
|
+
const xdg = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
755
|
+
return projectScope
|
|
756
|
+
? path.join(process.cwd(), '.gemini', 'settings.json')
|
|
757
|
+
: fs.existsSync(path.join(os.homedir(), '.gemini', 'settings.json'))
|
|
758
|
+
? path.join(os.homedir(), '.gemini', 'settings.json')
|
|
759
|
+
: path.join(xdg, 'gemini', 'settings.json');
|
|
760
|
+
}
|
|
761
|
+
function windsurfConfigPath() {
|
|
762
|
+
return path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
|
|
763
|
+
}
|
|
764
|
+
function vscodeMcpPath(projectScope) {
|
|
765
|
+
return projectScope
|
|
766
|
+
? path.join(process.cwd(), '.vscode', 'mcp.json')
|
|
767
|
+
: path.join(process.cwd(), '.vscode', 'mcp.json');
|
|
768
|
+
}
|
|
769
|
+
function vscodeUserSettingsPath() {
|
|
770
|
+
const home = os.homedir();
|
|
771
|
+
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
772
|
+
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
773
|
+
const candidates = [
|
|
774
|
+
path.join(appData, 'Code', 'User', 'settings.json'),
|
|
775
|
+
path.join(home, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
|
776
|
+
path.join(xdg, 'Code', 'User', 'settings.json'),
|
|
777
|
+
];
|
|
778
|
+
return candidates.find(candidate => fs.existsSync(path.dirname(candidate)));
|
|
779
|
+
}
|
|
677
780
|
function normalizeClaudeCodeScope(scope) {
|
|
678
781
|
if (scope === 'project' || scope === 'user' || scope === 'local')
|
|
679
782
|
return scope;
|
|
@@ -691,14 +794,21 @@ function parseClientList(value) {
|
|
|
691
794
|
'claude-desktop': 'claude-desktop',
|
|
692
795
|
claude_desktop: 'claude-desktop',
|
|
693
796
|
desktop: 'claude-desktop',
|
|
797
|
+
codex: 'codex',
|
|
798
|
+
'gemini-cli': 'gemini-cli',
|
|
799
|
+
gemini: 'gemini-cli',
|
|
800
|
+
windsurf: 'windsurf',
|
|
801
|
+
vscode: 'vscode',
|
|
802
|
+
'vs-code': 'vscode',
|
|
694
803
|
};
|
|
695
804
|
const requested = raw === 'all'
|
|
696
|
-
?
|
|
805
|
+
? exports.ALL_GATEWAY_CLIENTS.slice()
|
|
697
806
|
: raw.split(',').map(part => part.trim()).filter(Boolean);
|
|
698
807
|
const clients = requested.map(client => aliases[client]);
|
|
699
|
-
const unknown = requested.filter((
|
|
700
|
-
if (unknown.length > 0)
|
|
701
|
-
throw new Error(`Unknown client(s): ${unknown.join(', ')}. Use
|
|
808
|
+
const unknown = requested.filter((part, index) => !clients[index]);
|
|
809
|
+
if (unknown.length > 0) {
|
|
810
|
+
throw new Error(`Unknown client(s): ${unknown.join(', ')}. Use all, auto, or: ${exports.ALL_GATEWAY_CLIENTS.join(', ')}.`);
|
|
811
|
+
}
|
|
702
812
|
return Array.from(new Set(clients));
|
|
703
813
|
}
|
|
704
814
|
function commandAvailable(command) {
|
|
@@ -709,8 +819,9 @@ function commandAvailable(command) {
|
|
|
709
819
|
return result.status === 0;
|
|
710
820
|
}
|
|
711
821
|
function detectInstallClients(cursorProject) {
|
|
822
|
+
const home = os.homedir();
|
|
712
823
|
const clients = [];
|
|
713
|
-
const cursorGlobal = path.join(
|
|
824
|
+
const cursorGlobal = path.join(home, '.cursor');
|
|
714
825
|
const cursorProjectDir = path.join(process.cwd(), '.cursor');
|
|
715
826
|
if (fs.existsSync(cursorGlobal) || fs.existsSync(cursorProjectDir) || cursorProject)
|
|
716
827
|
clients.push('cursor');
|
|
@@ -718,6 +829,14 @@ function detectInstallClients(cursorProject) {
|
|
|
718
829
|
clients.push('claude-code');
|
|
719
830
|
if ((0, discoverPaths_1.claudeDesktopLikelyInstalled)())
|
|
720
831
|
clients.push('claude-desktop');
|
|
832
|
+
if (fs.existsSync(path.join(home, '.codex')) || commandAvailable('codex'))
|
|
833
|
+
clients.push('codex');
|
|
834
|
+
if (fs.existsSync(path.join(home, '.gemini')) || commandAvailable('gemini'))
|
|
835
|
+
clients.push('gemini-cli');
|
|
836
|
+
if (fs.existsSync(windsurfConfigPath()))
|
|
837
|
+
clients.push('windsurf');
|
|
838
|
+
if (fs.existsSync(path.join(process.cwd(), '.vscode')) || vscodeUserSettingsPath())
|
|
839
|
+
clients.push('vscode');
|
|
721
840
|
return clients;
|
|
722
841
|
}
|
|
723
842
|
function clientInstallArgs(args) {
|
|
@@ -754,7 +873,7 @@ async function installCursorMcpGatewayCommand(args, config) {
|
|
|
754
873
|
if (!downstreamCommand)
|
|
755
874
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
756
875
|
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
757
|
-
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs);
|
|
876
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
758
877
|
writeMcpServerConfig(file, serverName, nodeExe, commandArgs);
|
|
759
878
|
console.log(`AgentGuard MCP Gateway installed for Cursor (${projectScope ? 'project' : 'global'}).`);
|
|
760
879
|
console.log(`Config: ${file}`);
|
|
@@ -763,57 +882,56 @@ async function installCursorMcpGatewayCommand(args, config) {
|
|
|
763
882
|
console.log('Restart Cursor or reload MCP servers to use the protected tools.');
|
|
764
883
|
}
|
|
765
884
|
async function installMcpGatewayCommand(args, config) {
|
|
885
|
+
(0, config_1.requireCliSetup)(config, {
|
|
886
|
+
apiKey: args.apiKey,
|
|
887
|
+
shieldId: args.shieldId,
|
|
888
|
+
shieldKey: args.shieldKey,
|
|
889
|
+
apiUrl: args.apiUrl,
|
|
890
|
+
}, { requireOrganizationId: false });
|
|
766
891
|
const cursorProject = args.cursorProject === 'true';
|
|
767
892
|
const selection = parseClientList(args.clients);
|
|
768
893
|
const clients = selection === 'auto'
|
|
769
894
|
? detectInstallClients(cursorProject)
|
|
770
895
|
: selection;
|
|
771
896
|
if (clients.length === 0) {
|
|
772
|
-
throw new Error('No supported MCP clients detected.
|
|
897
|
+
throw new Error('No supported MCP clients detected. Run `fullcourtdefense install-all --clients all` or pick clients: '
|
|
898
|
+
+ exports.ALL_GATEWAY_CLIENTS.join(', '));
|
|
773
899
|
}
|
|
774
|
-
const baseArgs = clientInstallArgs(args);
|
|
775
900
|
const successes = [];
|
|
776
901
|
const failures = [];
|
|
902
|
+
console.log(`Installing MCP gateway for: ${clients.map(c => CLIENT_LABELS[c]).join(', ')}\n`);
|
|
777
903
|
for (const client of clients) {
|
|
778
904
|
try {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
}
|
|
782
|
-
else if (client === 'claude-code') {
|
|
783
|
-
await installClaudeCodeMcpGatewayCommand({ ...baseArgs, scope: args.claudeCodeScope, serverName: args.serverName }, config);
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
await installClaudeDesktopMcpGatewayCommand({ ...baseArgs, serverName: args.serverName }, config);
|
|
787
|
-
}
|
|
788
|
-
successes.push(client);
|
|
905
|
+
await installGatewayClient(client, args, config, cursorProject);
|
|
906
|
+
successes.push(CLIENT_LABELS[client]);
|
|
789
907
|
}
|
|
790
908
|
catch (error) {
|
|
791
|
-
failures.push(`${client}: ${error instanceof Error ? error.message : String(error)}`);
|
|
909
|
+
failures.push(`${CLIENT_LABELS[client]}: ${error instanceof Error ? error.message : String(error)}`);
|
|
792
910
|
}
|
|
911
|
+
if (clients.length > 1)
|
|
912
|
+
console.log('');
|
|
793
913
|
}
|
|
794
|
-
console.log('');
|
|
795
|
-
console.log('
|
|
796
|
-
console.log(`Installed: ${successes.length > 0 ? successes.join(', ') : 'none'}`);
|
|
914
|
+
console.log('AgentGuard gateway install summary:');
|
|
915
|
+
console.log(` ✓ ${successes.length > 0 ? successes.join(', ') : 'none'}`);
|
|
797
916
|
if (failures.length > 0) {
|
|
798
|
-
console.log('Failed:');
|
|
917
|
+
console.log(' ✗ Failed:');
|
|
799
918
|
for (const failure of failures)
|
|
800
|
-
console.log(
|
|
919
|
+
console.log(` - ${failure}`);
|
|
801
920
|
}
|
|
802
|
-
console.log(`Runtime identity: ${detectedDeveloperName(args)}`);
|
|
803
|
-
console.log('Restart/reload each client to use
|
|
804
|
-
console.log('Smoke test prompt: "Use lookup_customer to look up customer C-1001."');
|
|
921
|
+
console.log(` Runtime identity: ${detectedDeveloperName(args)}`);
|
|
922
|
+
console.log('Restart/reload each client to use protected MCP tools.');
|
|
805
923
|
if (args.upload === 'true') {
|
|
806
|
-
console.log('');
|
|
807
|
-
|
|
924
|
+
console.log('\nUploading desktop inventory to AI Fleet…');
|
|
925
|
+
const creds = (0, config_1.resolveCliCredentials)(config, { apiKey: args.apiKey, apiUrl: args.apiUrl });
|
|
808
926
|
await (0, discover_1.runDiscoverUpload)({
|
|
809
|
-
apiKey:
|
|
810
|
-
apiUrl:
|
|
927
|
+
apiKey: creds.apiKey,
|
|
928
|
+
apiUrl: creds.apiUrl,
|
|
811
929
|
userEmail: args.developerName,
|
|
812
930
|
silent: false,
|
|
813
931
|
}, config);
|
|
814
932
|
}
|
|
815
933
|
else {
|
|
816
|
-
console.log('
|
|
934
|
+
console.log('Add --upload to refresh AI Fleet after install.');
|
|
817
935
|
}
|
|
818
936
|
}
|
|
819
937
|
async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
@@ -830,7 +948,10 @@ async function installClaudeCodeMcpGatewayCommand(args, config) {
|
|
|
830
948
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
831
949
|
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
832
950
|
const includeShieldKey = scope !== 'project' || Boolean(args.shieldKey);
|
|
833
|
-
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, {
|
|
951
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, {
|
|
952
|
+
...INSTALL_GATEWAY_ARGS,
|
|
953
|
+
includeShieldKey,
|
|
954
|
+
});
|
|
834
955
|
const claudeArgs = ['mcp', 'add', '--scope', scope, '--transport', 'stdio', serverName, '--', nodeExe, ...commandArgs];
|
|
835
956
|
let result = (0, child_process_1.spawnSync)('claude', claudeArgs, {
|
|
836
957
|
stdio: 'inherit',
|
|
@@ -886,7 +1007,7 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
886
1007
|
if (!downstreamCommand)
|
|
887
1008
|
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
888
1009
|
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
889
|
-
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs);
|
|
1010
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
890
1011
|
for (const target of targets) {
|
|
891
1012
|
writeMcpServerConfig(target.file, serverName, nodeExe, commandArgs);
|
|
892
1013
|
}
|
|
@@ -898,3 +1019,128 @@ async function installClaudeDesktopMcpGatewayCommand(args, config) {
|
|
|
898
1019
|
console.log(`Downstream: ${downstreamCommand} ${downstreamArgs.join(' ')}`);
|
|
899
1020
|
console.log('Restart Claude Desktop to load the protected tools.');
|
|
900
1021
|
}
|
|
1022
|
+
async function installCodexMcpGatewayCommand(args, config) {
|
|
1023
|
+
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
1024
|
+
agentClient: 'codex',
|
|
1025
|
+
agentNamePrefix: 'codex',
|
|
1026
|
+
userObjective: 'Developer requested this action from Codex.',
|
|
1027
|
+
});
|
|
1028
|
+
const projectScope = args.project === 'true';
|
|
1029
|
+
const file = codexConfigPath(projectScope);
|
|
1030
|
+
const nodeExe = process.execPath;
|
|
1031
|
+
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1032
|
+
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1033
|
+
if (!downstreamCommand)
|
|
1034
|
+
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
1035
|
+
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
1036
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
1037
|
+
writeCodexMcpServer(file, serverName, nodeExe, commandArgs);
|
|
1038
|
+
console.log(`AgentGuard MCP Gateway installed for Codex (${projectScope ? 'project' : 'global'}).`);
|
|
1039
|
+
console.log(`Config: ${file}`);
|
|
1040
|
+
console.log(`Server: ${serverName}`);
|
|
1041
|
+
console.log('Restart Codex or reload MCP servers. Project config requires a trusted project.');
|
|
1042
|
+
}
|
|
1043
|
+
async function installGeminiMcpGatewayCommand(args, config) {
|
|
1044
|
+
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
1045
|
+
agentClient: 'gemini-cli',
|
|
1046
|
+
agentNamePrefix: 'gemini',
|
|
1047
|
+
userObjective: 'Developer requested this action from Gemini CLI.',
|
|
1048
|
+
});
|
|
1049
|
+
const projectScope = args.project === 'true';
|
|
1050
|
+
const file = geminiSettingsPath(projectScope);
|
|
1051
|
+
const nodeExe = process.execPath;
|
|
1052
|
+
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1053
|
+
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1054
|
+
if (!downstreamCommand)
|
|
1055
|
+
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
1056
|
+
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
1057
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
1058
|
+
writeJsonMcpServer(file, serverName, nodeExe, commandArgs);
|
|
1059
|
+
console.log(`AgentGuard MCP Gateway installed for Gemini CLI (${projectScope ? 'project' : 'user'}).`);
|
|
1060
|
+
console.log(`Config: ${file}`);
|
|
1061
|
+
console.log(`Server: ${serverName}`);
|
|
1062
|
+
console.log('Restart Gemini CLI to load the protected tools.');
|
|
1063
|
+
}
|
|
1064
|
+
async function installWindsurfMcpGatewayCommand(args, config) {
|
|
1065
|
+
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
1066
|
+
agentClient: 'windsurf',
|
|
1067
|
+
agentNamePrefix: 'windsurf',
|
|
1068
|
+
userObjective: 'Developer requested this action from Windsurf.',
|
|
1069
|
+
});
|
|
1070
|
+
const file = windsurfConfigPath();
|
|
1071
|
+
const nodeExe = process.execPath;
|
|
1072
|
+
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1073
|
+
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1074
|
+
if (!downstreamCommand)
|
|
1075
|
+
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
1076
|
+
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
1077
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
1078
|
+
writeJsonMcpServer(file, serverName, nodeExe, commandArgs);
|
|
1079
|
+
console.log('AgentGuard MCP Gateway installed for Windsurf.');
|
|
1080
|
+
console.log(`Config: ${file}`);
|
|
1081
|
+
console.log(`Server: ${serverName}`);
|
|
1082
|
+
console.log('Restart Windsurf to load the protected tools.');
|
|
1083
|
+
}
|
|
1084
|
+
async function installVscodeMcpGatewayCommand(args, config) {
|
|
1085
|
+
const gatewayConfig = resolveGatewayConfig(args, config, {
|
|
1086
|
+
agentClient: 'vscode',
|
|
1087
|
+
agentNamePrefix: 'vscode',
|
|
1088
|
+
userObjective: 'Developer requested this action from VS Code.',
|
|
1089
|
+
});
|
|
1090
|
+
const projectScope = args.project !== 'false';
|
|
1091
|
+
const nodeExe = process.execPath;
|
|
1092
|
+
const serverName = args.serverName || MANAGED_SERVER_NAME;
|
|
1093
|
+
const downstreamCommand = args.mcpCommand || process.env.FCD_MCP_COMMAND;
|
|
1094
|
+
if (!downstreamCommand)
|
|
1095
|
+
throw new Error('Pass --mcp-command for the real MCP server you want to protect.');
|
|
1096
|
+
const downstreamArgs = parseArgsList(args.mcpArgs || process.env.FCD_MCP_ARGS);
|
|
1097
|
+
const commandArgs = buildGatewayCommandArgs(gatewayConfig, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
1098
|
+
const targets = [];
|
|
1099
|
+
if (projectScope)
|
|
1100
|
+
targets.push(vscodeMcpPath(true));
|
|
1101
|
+
const userSettings = vscodeUserSettingsPath();
|
|
1102
|
+
if (userSettings)
|
|
1103
|
+
targets.push(userSettings);
|
|
1104
|
+
if (targets.length === 0)
|
|
1105
|
+
targets.push(vscodeMcpPath(true));
|
|
1106
|
+
for (const file of targets) {
|
|
1107
|
+
if (file.endsWith('settings.json'))
|
|
1108
|
+
writeJsonMcpServer(file, serverName, nodeExe, commandArgs);
|
|
1109
|
+
else
|
|
1110
|
+
writeMcpServerConfig(file, serverName, nodeExe, commandArgs);
|
|
1111
|
+
}
|
|
1112
|
+
console.log('AgentGuard MCP Gateway installed for VS Code.');
|
|
1113
|
+
for (const file of targets)
|
|
1114
|
+
console.log(`Config: ${file}`);
|
|
1115
|
+
console.log(`Server: ${serverName}`);
|
|
1116
|
+
console.log('Reload VS Code window or restart to pick up MCP changes.');
|
|
1117
|
+
}
|
|
1118
|
+
async function installGatewayClient(client, args, config, cursorProject) {
|
|
1119
|
+
const baseArgs = clientInstallArgs(args);
|
|
1120
|
+
const serverName = args.serverName;
|
|
1121
|
+
switch (client) {
|
|
1122
|
+
case 'cursor':
|
|
1123
|
+
await installCursorMcpGatewayCommand({ ...baseArgs, project: String(cursorProject), serverName }, config);
|
|
1124
|
+
break;
|
|
1125
|
+
case 'claude-code':
|
|
1126
|
+
await installClaudeCodeMcpGatewayCommand({ ...baseArgs, scope: args.claudeCodeScope, serverName }, config);
|
|
1127
|
+
break;
|
|
1128
|
+
case 'claude-desktop':
|
|
1129
|
+
await installClaudeDesktopMcpGatewayCommand({ ...baseArgs, serverName }, config);
|
|
1130
|
+
break;
|
|
1131
|
+
case 'codex':
|
|
1132
|
+
await installCodexMcpGatewayCommand({ ...baseArgs, project: String(cursorProject), serverName }, config);
|
|
1133
|
+
break;
|
|
1134
|
+
case 'gemini-cli':
|
|
1135
|
+
await installGeminiMcpGatewayCommand({ ...baseArgs, project: String(cursorProject), serverName }, config);
|
|
1136
|
+
break;
|
|
1137
|
+
case 'windsurf':
|
|
1138
|
+
await installWindsurfMcpGatewayCommand({ ...baseArgs, serverName }, config);
|
|
1139
|
+
break;
|
|
1140
|
+
case 'vscode':
|
|
1141
|
+
await installVscodeMcpGatewayCommand({ ...baseArgs, project: String(cursorProject), serverName }, config);
|
|
1142
|
+
break;
|
|
1143
|
+
default:
|
|
1144
|
+
throw new Error(`Unsupported client: ${client}`);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface BotGuardConfig {
|
|
2
2
|
apiKey?: string;
|
|
3
|
+
organizationId?: string;
|
|
3
4
|
apiUrl?: string;
|
|
4
5
|
shieldId?: string;
|
|
5
6
|
shieldKey?: string;
|
|
@@ -15,6 +16,7 @@ export interface BotGuardConfig {
|
|
|
15
16
|
webhookFormat?: string;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
19
|
+
export declare function getHomeConfigPath(): string;
|
|
18
20
|
export declare function loadConfig(configPath?: string): BotGuardConfig;
|
|
19
21
|
export declare function getDefaultConfigPath(): string;
|
|
20
22
|
export declare function saveShieldConfig(input: {
|
|
@@ -22,4 +24,28 @@ export declare function saveShieldConfig(input: {
|
|
|
22
24
|
shieldKey?: string;
|
|
23
25
|
apiUrl?: string;
|
|
24
26
|
}): string;
|
|
27
|
+
export interface SetupConfigInput {
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
organizationId?: string;
|
|
30
|
+
apiUrl?: string;
|
|
31
|
+
shieldId?: string;
|
|
32
|
+
shieldKey?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ResolvedCliCredentials {
|
|
35
|
+
apiKey?: string;
|
|
36
|
+
organizationId?: string;
|
|
37
|
+
apiUrl: string;
|
|
38
|
+
shieldId?: string;
|
|
39
|
+
shieldKey?: string;
|
|
40
|
+
}
|
|
41
|
+
/** Merge saved ~/.fullcourtdefense.yml + env + optional CLI flag overrides. */
|
|
42
|
+
export declare function resolveCliCredentials(config: BotGuardConfig, overrides?: Partial<ResolvedCliCredentials>): ResolvedCliCredentials;
|
|
43
|
+
export declare function isCliSetupComplete(config: BotGuardConfig): boolean;
|
|
44
|
+
/** Fail fast with a clear message when setup was not run. */
|
|
45
|
+
export declare function requireCliSetup(config: BotGuardConfig, overrides?: Partial<ResolvedCliCredentials>, options?: {
|
|
46
|
+
requireApiKey?: boolean;
|
|
47
|
+
requireOrganizationId?: boolean;
|
|
48
|
+
requireShield?: boolean;
|
|
49
|
+
}): ResolvedCliCredentials;
|
|
50
|
+
export declare function saveSetupConfig(input: SetupConfigInput, targetPath?: string): string;
|
|
25
51
|
export declare function resolveSystemPrompt(value: string): string;
|