fullcourtdefense-cli 1.5.0 → 1.5.2
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/commands/mcpGateway.d.ts +1 -0
- package/dist/commands/mcpGateway.js +74 -30
- package/dist/index.js +5 -0
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -61,6 +61,7 @@ export declare function installMcpGatewayCommand(args: InstallMcpGatewayArgs, co
|
|
|
61
61
|
export interface ProtectAllArgs extends McpGatewayArgs {
|
|
62
62
|
dryRun?: string;
|
|
63
63
|
config?: string;
|
|
64
|
+
clients?: string;
|
|
64
65
|
}
|
|
65
66
|
export declare function protectAllCommand(args: ProtectAllArgs, config: BotGuardConfig): Promise<void>;
|
|
66
67
|
export declare function unprotectAllCommand(args: ProtectAllArgs, config: BotGuardConfig): Promise<void>;
|
|
@@ -942,6 +942,24 @@ async function installMcpGatewayCommand(args, config) {
|
|
|
942
942
|
console.log('Add --upload to refresh AI Fleet after install.');
|
|
943
943
|
}
|
|
944
944
|
}
|
|
945
|
+
/** Map a high-level client to the discovery clientKey(s) its config files use. */
|
|
946
|
+
const AGENT_TO_CLIENT_KEYS = {
|
|
947
|
+
cursor: ['cursor'],
|
|
948
|
+
'claude-code': ['claude_code'],
|
|
949
|
+
'claude-desktop': ['claude_desktop'],
|
|
950
|
+
codex: ['codex'],
|
|
951
|
+
'gemini-cli': ['gemini_cli'],
|
|
952
|
+
windsurf: ['windsurf'],
|
|
953
|
+
vscode: ['vscode'],
|
|
954
|
+
};
|
|
955
|
+
/** Resolve the optional --clients filter into a set of clientKeys, or undefined for "all". */
|
|
956
|
+
function resolveProtectClientKeys(clients) {
|
|
957
|
+
const raw = (clients || '').trim().toLowerCase();
|
|
958
|
+
if (!raw || raw === 'auto' || raw === 'all')
|
|
959
|
+
return undefined; // no filter — process every client
|
|
960
|
+
const selection = parseClientList(clients);
|
|
961
|
+
return new Set(selection.flatMap(client => AGENT_TO_CLIENT_KEYS[client] || []));
|
|
962
|
+
}
|
|
945
963
|
const CLIENT_KEY_TO_AGENT = {
|
|
946
964
|
cursor: 'cursor',
|
|
947
965
|
claude_code: 'claude-code',
|
|
@@ -1035,7 +1053,7 @@ function wrapJsonConfigFile(file, gatewayConfig, agentClient, dryRun) {
|
|
|
1035
1053
|
}
|
|
1036
1054
|
const downstreamCommand = entry.command;
|
|
1037
1055
|
const downstreamArgs = Array.isArray(entry.args) ? entry.args.map(String) : [];
|
|
1038
|
-
const perServer = { ...gatewayConfig, agentName: perServerAgentName(gatewayConfig.developerName, agentClient, name) };
|
|
1056
|
+
const perServer = { ...gatewayConfig, agentClient, agentName: perServerAgentName(gatewayConfig.developerName, agentClient, name) };
|
|
1039
1057
|
const wrappedArgs = buildGatewayCommandArgs(perServer, downstreamCommand, downstreamArgs, INSTALL_GATEWAY_ARGS);
|
|
1040
1058
|
entry.command = nodeExe;
|
|
1041
1059
|
entry.args = wrappedArgs;
|
|
@@ -1087,30 +1105,47 @@ function unwrapJsonConfigFile(file, dryRun) {
|
|
|
1087
1105
|
}
|
|
1088
1106
|
return { restored };
|
|
1089
1107
|
}
|
|
1090
|
-
/** Parse a single-line TOML scalar string value: command = "x". */
|
|
1108
|
+
/** Parse a single-line TOML scalar string value: command = "x" or command = 'x'. */
|
|
1091
1109
|
function parseTomlInlineString(line) {
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1110
|
+
const rhs = line.slice(line.indexOf('=') + 1).trim();
|
|
1111
|
+
const basic = rhs.match(/^"((?:[^"\\]|\\.)*)"/);
|
|
1112
|
+
if (basic) {
|
|
1113
|
+
try {
|
|
1114
|
+
return JSON.parse(`"${basic[1]}"`);
|
|
1115
|
+
}
|
|
1116
|
+
catch {
|
|
1117
|
+
return basic[1];
|
|
1118
|
+
}
|
|
1100
1119
|
}
|
|
1120
|
+
const literal = rhs.match(/^'([^']*)'/); // TOML literal string — no escapes
|
|
1121
|
+
if (literal)
|
|
1122
|
+
return literal[1];
|
|
1123
|
+
return undefined;
|
|
1101
1124
|
}
|
|
1102
|
-
/** Parse a single-line TOML string array: args = ["a",
|
|
1125
|
+
/** Parse a single-line TOML string array: args = ["a", 'b'] (mixed quotes ok). */
|
|
1103
1126
|
function parseTomlInlineStringArray(line) {
|
|
1104
|
-
const
|
|
1105
|
-
if (!
|
|
1106
|
-
return undefined;
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1127
|
+
const rhs = line.slice(line.indexOf('=') + 1).trim();
|
|
1128
|
+
if (!rhs.startsWith('[') || !rhs.endsWith(']'))
|
|
1129
|
+
return undefined; // multi-line / not an inline array
|
|
1130
|
+
const inner = rhs.slice(1, -1).trim();
|
|
1131
|
+
if (inner === '')
|
|
1132
|
+
return [];
|
|
1133
|
+
const out = [];
|
|
1134
|
+
const re = /"((?:[^"\\]|\\.)*)"|'([^']*)'/g;
|
|
1135
|
+
let match;
|
|
1136
|
+
while ((match = re.exec(inner)) !== null) {
|
|
1137
|
+
if (match[1] !== undefined) {
|
|
1138
|
+
try {
|
|
1139
|
+
out.push(JSON.parse(`"${match[1]}"`));
|
|
1140
|
+
}
|
|
1141
|
+
catch {
|
|
1142
|
+
out.push(match[1]);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
else
|
|
1146
|
+
out.push(match[2] ?? '');
|
|
1111
1147
|
}
|
|
1112
|
-
|
|
1113
|
-
return undefined;
|
|
1148
|
+
return out;
|
|
1114
1149
|
}
|
|
1115
1150
|
function tomlStringArrayLiteral(values) {
|
|
1116
1151
|
return `[${values.map(v => JSON.stringify(v)).join(', ')}]`;
|
|
@@ -1169,7 +1204,7 @@ function transformCodexToml(file, mode, gatewayConfig, dryRun) {
|
|
|
1169
1204
|
stats.skippedManaged.push(section.name);
|
|
1170
1205
|
continue;
|
|
1171
1206
|
}
|
|
1172
|
-
const perServer = { ...gatewayConfig, agentName: perServerAgentName(gatewayConfig.developerName, 'codex', section.name) };
|
|
1207
|
+
const perServer = { ...gatewayConfig, agentClient: 'codex', agentName: perServerAgentName(gatewayConfig.developerName, 'codex', section.name) };
|
|
1173
1208
|
const wrappedArgs = buildGatewayCommandArgs(perServer, section.command, section.args || [], INSTALL_GATEWAY_ARGS);
|
|
1174
1209
|
const indent = lines[section.cmdLine].match(/^\s*/)?.[0] || '';
|
|
1175
1210
|
lines[section.cmdLine] = `${indent}command = ${JSON.stringify(nodeExe)}`;
|
|
@@ -1201,24 +1236,29 @@ function transformCodexToml(file, mode, gatewayConfig, dryRun) {
|
|
|
1201
1236
|
}
|
|
1202
1237
|
return stats;
|
|
1203
1238
|
}
|
|
1204
|
-
function protectAllTargetFiles(extra) {
|
|
1239
|
+
function protectAllTargetFiles(extra, allowedClientKeys) {
|
|
1205
1240
|
const cwd = process.cwd();
|
|
1206
1241
|
const seen = new Set();
|
|
1242
|
+
const allow = (clientKey) => !allowedClientKeys || allowedClientKeys.has(clientKey);
|
|
1207
1243
|
const files = [];
|
|
1208
1244
|
for (const candidate of (0, discoverPaths_1.candidateConfigPaths)(cwd, extra)) {
|
|
1209
1245
|
const key = path.resolve(candidate.path).toLowerCase();
|
|
1210
1246
|
if (seen.has(key))
|
|
1211
1247
|
continue;
|
|
1248
|
+
if (!allow(candidate.clientKey))
|
|
1249
|
+
continue;
|
|
1212
1250
|
if (!fs.existsSync(candidate.path))
|
|
1213
1251
|
continue;
|
|
1214
1252
|
seen.add(key);
|
|
1215
1253
|
files.push(candidate);
|
|
1216
1254
|
}
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
seen.
|
|
1221
|
-
|
|
1255
|
+
if (allow('claude_desktop')) {
|
|
1256
|
+
for (const target of (0, discoverPaths_1.claudeDesktopInstallTargets)()) {
|
|
1257
|
+
const key = path.resolve(target.file).toLowerCase();
|
|
1258
|
+
if (target.exists && !seen.has(key)) {
|
|
1259
|
+
seen.add(key);
|
|
1260
|
+
files.push({ path: target.file, source: target.source, clientKey: 'claude_desktop' });
|
|
1261
|
+
}
|
|
1222
1262
|
}
|
|
1223
1263
|
}
|
|
1224
1264
|
return files;
|
|
@@ -1227,11 +1267,14 @@ async function protectAllCommand(args, config) {
|
|
|
1227
1267
|
(0, config_1.requireCliSetup)(config, { shieldId: args.shieldId, shieldKey: args.shieldKey, apiUrl: args.apiUrl }, { requireOrganizationId: false });
|
|
1228
1268
|
const gatewayConfig = resolveGatewayConfig(args, config);
|
|
1229
1269
|
const dryRun = args.dryRun === 'true';
|
|
1230
|
-
const
|
|
1270
|
+
const allowedClientKeys = resolveProtectClientKeys(args.clients);
|
|
1271
|
+
const files = protectAllTargetFiles(args.config, allowedClientKeys);
|
|
1231
1272
|
console.log('\n\x1b[1m\x1b[36mFullCourtDefense — protect every MCP server in every client\x1b[0m');
|
|
1232
1273
|
console.log(`\x1b[2mRuntime identity: ${gatewayConfig.developerName} | Shield: ${gatewayConfig.shieldId}${dryRun ? ' | DRY RUN' : ''}\x1b[0m\n`);
|
|
1233
1274
|
if (files.length === 0) {
|
|
1234
|
-
console.log(
|
|
1275
|
+
console.log(allowedClientKeys
|
|
1276
|
+
? 'No matching client config files found for the requested --clients. Try without --clients, or pick a client that is installed.'
|
|
1277
|
+
: 'No MCP client config files found on this machine. Configure an MCP server in any client, then re-run.');
|
|
1235
1278
|
return;
|
|
1236
1279
|
}
|
|
1237
1280
|
let totalWrapped = 0;
|
|
@@ -1268,7 +1311,8 @@ async function protectAllCommand(args, config) {
|
|
|
1268
1311
|
}
|
|
1269
1312
|
async function unprotectAllCommand(args, config) {
|
|
1270
1313
|
const dryRun = args.dryRun === 'true';
|
|
1271
|
-
const
|
|
1314
|
+
const allowedClientKeys = resolveProtectClientKeys(args.clients);
|
|
1315
|
+
const files = protectAllTargetFiles(args.config, allowedClientKeys);
|
|
1272
1316
|
let gatewayConfig;
|
|
1273
1317
|
try {
|
|
1274
1318
|
gatewayConfig = resolveGatewayConfig(args, config);
|
package/dist/index.js
CHANGED
|
@@ -128,8 +128,11 @@ function printHelp() {
|
|
|
128
128
|
(Cursor, Claude Code/Desktop, Codex, Gemini, Windsurf, VS Code)
|
|
129
129
|
and wrap each through the Shield proxy — no --mcp-command needed.
|
|
130
130
|
Enforces your org Action Policies on every tool. --dry-run to preview.
|
|
131
|
+
Per-tool: --clients cursor (or claude-code, claude-desktop, codex,
|
|
132
|
+
gemini-cli, windsurf, vscode; comma-separated). Default: all tools.
|
|
131
133
|
unprotect-all
|
|
132
134
|
Reverse protect-all: restore every server to its original command.
|
|
135
|
+
Also supports --clients to target one tool, and --dry-run.
|
|
133
136
|
install-mcp-gateway
|
|
134
137
|
Install MCP gateway into selected clients (--clients all for every tool).
|
|
135
138
|
install-cursor-mcp-gateway
|
|
@@ -511,6 +514,7 @@ async function main() {
|
|
|
511
514
|
...buildGatewayArgs(),
|
|
512
515
|
dryRun: flags['dry-run'],
|
|
513
516
|
config: flags.config,
|
|
517
|
+
clients: flags.clients,
|
|
514
518
|
};
|
|
515
519
|
await (0, mcpGateway_1.protectAllCommand)(args, config);
|
|
516
520
|
break;
|
|
@@ -521,6 +525,7 @@ async function main() {
|
|
|
521
525
|
...buildGatewayArgs(),
|
|
522
526
|
dryRun: flags['dry-run'],
|
|
523
527
|
config: flags.config,
|
|
528
|
+
clients: flags.clients,
|
|
524
529
|
};
|
|
525
530
|
await (0, mcpGateway_1.unprotectAllCommand)(args, config);
|
|
526
531
|
break;
|
package/dist/version.json
CHANGED