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.
@@ -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
- if (value === 'cursor' || value === 'claude-code' || value === 'claude-desktop')
91
- return value;
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 home = loadHomeShield();
144
- const shieldId = args.shieldId || process.env.FCD_SHIELD_ID || process.env.FULLCOURTDEFENSE_SHIELD_ID || process.env.AGENTGUARD_SHIELD_ID || config.shieldId || home.shieldId;
145
- if (!shieldId)
146
- throw new Error('Missing Shield ID. Pass --shield-id or run fullcourtdefense configure.');
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: args.shieldKey || process.env.FCD_SHIELD_KEY || process.env.FULLCOURTDEFENSE_SHIELD_KEY || process.env.AGENTGUARD_SHIELD_KEY || config.shieldKey || home.shieldKey,
152
- apiUrl: (args.apiUrl || process.env.FCD_API_URL || process.env.FULLCOURTDEFENSE_API_URL || config.apiUrl || home.apiUrl || DEFAULT_API_URL).replace(/\/$/, ''),
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 (gatewayConfig.shieldKey && options.includeShieldKey !== false)
661
- commandArgs.push('--shield-key', gatewayConfig.shieldKey);
662
- if (gatewayConfig.apiUrl)
663
- commandArgs.push('--api-url', gatewayConfig.apiUrl);
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
- ? ['cursor', 'claude-code', 'claude-desktop']
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((_, index) => !clients[index]);
700
- if (unknown.length > 0)
701
- throw new Error(`Unknown client(s): ${unknown.join(', ')}. Use auto, all, cursor, claude-code, claude-desktop.`);
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(os.homedir(), '.cursor');
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. Re-run with --clients all or a comma list such as cursor,claude-desktop.');
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
- if (client === 'cursor') {
780
- await installCursorMcpGatewayCommand({ ...baseArgs, project: String(cursorProject), serverName: args.serverName }, config);
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('AgentGuard one-install summary:');
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(`- ${failure}`);
919
+ console.log(` - ${failure}`);
801
920
  }
802
- console.log(`Runtime identity: ${detectedDeveloperName(args)}`);
803
- console.log('Restart/reload each client to use the protected tools.');
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
- console.log('Uploading desktop inventory to AI Inventory…');
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: args.apiKey || config.apiKey,
810
- apiUrl: args.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('Run with --upload to refresh AI Inventory / Desktop MCP coverage in the app.');
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, { includeShieldKey });
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;