fullcourtdefense-cli 1.5.3 → 1.6.1
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/autoProtect.d.ts +10 -0
- package/dist/commands/autoProtect.js +150 -0
- package/dist/commands/installAll.d.ts +9 -5
- package/dist/commands/installAll.js +16 -7
- package/dist/commands/installCursorHook.js +2 -2
- package/dist/commands/mcpGateway.js +22 -7
- package/dist/index.js +29 -5
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BotGuardConfig } from '../config';
|
|
2
|
+
import { ProtectAllArgs } from './mcpGateway';
|
|
3
|
+
export interface AutoProtectArgs extends ProtectAllArgs {
|
|
4
|
+
install?: string;
|
|
5
|
+
uninstall?: string;
|
|
6
|
+
status?: string;
|
|
7
|
+
intervalMinutes?: string;
|
|
8
|
+
onLogon?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function autoProtectCommand(args: AutoProtectArgs, config: BotGuardConfig): Promise<void>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.autoProtectCommand = autoProtectCommand;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const mcpGateway_1 = require("./mcpGateway");
|
|
42
|
+
const COLOR = {
|
|
43
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
44
|
+
red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', cyan: '\x1b[36m', gray: '\x1b[90m',
|
|
45
|
+
};
|
|
46
|
+
const TASK_NAME = 'FullCourtDefense Auto-Protect';
|
|
47
|
+
const CRON_MARKER = '# FCD_AUTO_PROTECT';
|
|
48
|
+
function cliEntry() {
|
|
49
|
+
return path.resolve(process.argv[1] || path.join(__dirname, '..', 'index.js'));
|
|
50
|
+
}
|
|
51
|
+
function launcherDir() {
|
|
52
|
+
const base = process.platform === 'win32'
|
|
53
|
+
? (process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'))
|
|
54
|
+
: path.join(os.homedir(), '.fullcourtdefense');
|
|
55
|
+
return path.join(base, 'FullCourtDefense');
|
|
56
|
+
}
|
|
57
|
+
/** On Windows, write a hidden VBS launcher so the scheduled run doesn't flash a console window. */
|
|
58
|
+
function ensureWindowsLauncher() {
|
|
59
|
+
const dir = launcherDir();
|
|
60
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
61
|
+
const vbs = path.join(dir, 'auto-protect.vbs');
|
|
62
|
+
const node = process.execPath;
|
|
63
|
+
const cli = cliEntry();
|
|
64
|
+
// WScript.Shell.Run with window style 0 = hidden, wait = false.
|
|
65
|
+
const inner = `"${node}" "${cli}" protect-all`;
|
|
66
|
+
const content = `CreateObject("WScript.Shell").Run "cmd /c ${inner.replace(/"/g, '""')}", 0, False\n`;
|
|
67
|
+
fs.writeFileSync(vbs, content, 'utf8');
|
|
68
|
+
return vbs;
|
|
69
|
+
}
|
|
70
|
+
function installWindows(intervalMinutes, onLogon) {
|
|
71
|
+
const vbs = ensureWindowsLauncher();
|
|
72
|
+
const tr = `wscript.exe "${vbs}"`;
|
|
73
|
+
const baseArgs = ['/Create', '/TN', TASK_NAME, '/TR', tr, '/F', '/RL', 'LIMITED'];
|
|
74
|
+
const schedArgs = onLogon
|
|
75
|
+
? ['/SC', 'ONLOGON']
|
|
76
|
+
: ['/SC', 'MINUTE', '/MO', String(intervalMinutes)];
|
|
77
|
+
const result = (0, child_process_1.spawnSync)('schtasks', [...baseArgs, ...schedArgs], { stdio: 'inherit' });
|
|
78
|
+
return result.status === 0;
|
|
79
|
+
}
|
|
80
|
+
function uninstallWindows() {
|
|
81
|
+
const result = (0, child_process_1.spawnSync)('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'inherit' });
|
|
82
|
+
const vbs = path.join(launcherDir(), 'auto-protect.vbs');
|
|
83
|
+
try {
|
|
84
|
+
if (fs.existsSync(vbs))
|
|
85
|
+
fs.unlinkSync(vbs);
|
|
86
|
+
}
|
|
87
|
+
catch { /* ignore */ }
|
|
88
|
+
return result.status === 0;
|
|
89
|
+
}
|
|
90
|
+
function statusWindows() {
|
|
91
|
+
(0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', TASK_NAME, '/V', '/FO', 'LIST'], { stdio: 'inherit' });
|
|
92
|
+
}
|
|
93
|
+
function readCrontab() {
|
|
94
|
+
const result = (0, child_process_1.spawnSync)('crontab', ['-l'], { encoding: 'utf8' });
|
|
95
|
+
return result.status === 0 ? (result.stdout || '') : '';
|
|
96
|
+
}
|
|
97
|
+
function writeCrontab(content) {
|
|
98
|
+
const result = (0, child_process_1.spawnSync)('crontab', ['-'], { input: content.endsWith('\n') ? content : `${content}\n`, encoding: 'utf8' });
|
|
99
|
+
return result.status === 0;
|
|
100
|
+
}
|
|
101
|
+
function installUnix(intervalMinutes, onLogon) {
|
|
102
|
+
const node = process.execPath;
|
|
103
|
+
const cli = cliEntry();
|
|
104
|
+
const command = `"${node}" "${cli}" protect-all >/dev/null 2>&1`;
|
|
105
|
+
const schedule = onLogon ? '@reboot' : `*/${Math.max(1, intervalMinutes)} * * * *`;
|
|
106
|
+
const line = `${schedule} ${command} ${CRON_MARKER}`;
|
|
107
|
+
const existing = readCrontab().split('\n').filter(l => l && !l.includes(CRON_MARKER));
|
|
108
|
+
return writeCrontab([...existing, line].join('\n'));
|
|
109
|
+
}
|
|
110
|
+
function uninstallUnix() {
|
|
111
|
+
const existing = readCrontab().split('\n').filter(l => l && !l.includes(CRON_MARKER));
|
|
112
|
+
return writeCrontab(existing.join('\n'));
|
|
113
|
+
}
|
|
114
|
+
async function autoProtectCommand(args, config) {
|
|
115
|
+
const intervalMinutes = Number(args.intervalMinutes) > 0 ? Math.floor(Number(args.intervalMinutes)) : 60;
|
|
116
|
+
const onLogon = args.onLogon === 'true';
|
|
117
|
+
if (args.uninstall === 'true') {
|
|
118
|
+
const ok = process.platform === 'win32' ? uninstallWindows() : uninstallUnix();
|
|
119
|
+
console.log(ok
|
|
120
|
+
? `${COLOR.green}Removed the FullCourtDefense auto-protect schedule.${COLOR.reset}`
|
|
121
|
+
: `${COLOR.yellow}No auto-protect schedule found (or removal failed).${COLOR.reset}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (args.status === 'true') {
|
|
125
|
+
if (process.platform === 'win32')
|
|
126
|
+
statusWindows();
|
|
127
|
+
else
|
|
128
|
+
console.log(readCrontab().split('\n').filter(l => l.includes(CRON_MARKER)).join('\n') || 'No auto-protect cron entry found.');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Default action is install.
|
|
132
|
+
console.log(`\n${COLOR.bold}${COLOR.cyan}FullCourtDefense — schedule self-healing auto-protect${COLOR.reset}`);
|
|
133
|
+
console.log(`${COLOR.gray}Re-runs \`protect-all\` ${onLogon ? 'at every logon' : `every ${intervalMinutes} min`} so any server that loses the proxy is automatically re-wrapped.${COLOR.reset}\n`);
|
|
134
|
+
// Protect everything once right now, then register the recurring task.
|
|
135
|
+
await (0, mcpGateway_1.protectAllCommand)({ ...args, install: undefined, uninstall: undefined, status: undefined }, config);
|
|
136
|
+
const ok = process.platform === 'win32'
|
|
137
|
+
? installWindows(intervalMinutes, onLogon)
|
|
138
|
+
: installUnix(intervalMinutes, onLogon);
|
|
139
|
+
console.log('');
|
|
140
|
+
if (ok) {
|
|
141
|
+
console.log(`${COLOR.green}${COLOR.bold}Auto-protect scheduled.${COLOR.reset}`);
|
|
142
|
+
console.log(`${COLOR.gray}Trigger:${COLOR.reset} ${onLogon ? 'at logon' : `every ${intervalMinutes} minutes`}`);
|
|
143
|
+
console.log(`${COLOR.gray}Task:${COLOR.reset} ${process.platform === 'win32' ? `Scheduled Task "${TASK_NAME}"` : 'crontab entry'}`);
|
|
144
|
+
console.log(`${COLOR.gray}Status:${COLOR.reset} fullcourtdefense auto-protect --status true`);
|
|
145
|
+
console.log(`${COLOR.gray}Remove:${COLOR.reset} fullcourtdefense auto-protect --uninstall true`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log(`${COLOR.red}Could not register the schedule.${COLOR.reset} On Windows you may need to run the terminal as Administrator, or use --on-logon true.`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { BotGuardConfig } from '../config';
|
|
2
|
-
import {
|
|
3
|
-
export interface InstallAllArgs extends
|
|
2
|
+
import { ProtectAllArgs } from './mcpGateway';
|
|
3
|
+
export interface InstallAllArgs extends ProtectAllArgs {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
cursorProject?: string;
|
|
6
|
+
upload?: string;
|
|
4
7
|
hooks?: string;
|
|
5
8
|
discover?: string;
|
|
9
|
+
autoProtect?: string;
|
|
10
|
+
intervalMinutes?: string;
|
|
6
11
|
}
|
|
7
12
|
/**
|
|
8
|
-
* One-click install:
|
|
9
|
-
*
|
|
10
|
-
* should be the default enforcement path for MCP tools.
|
|
13
|
+
* One-click install: discover/wrap every existing MCP server, install Cursor
|
|
14
|
+
* hooks for built-in Cursor actions, optional self-heal and fleet upload.
|
|
11
15
|
*/
|
|
12
16
|
export declare function installAllCommand(args: InstallAllArgs, config: BotGuardConfig): Promise<void>;
|
|
@@ -5,10 +5,10 @@ const config_1 = require("../config");
|
|
|
5
5
|
const discover_1 = require("./discover");
|
|
6
6
|
const installCursorHook_1 = require("./installCursorHook");
|
|
7
7
|
const mcpGateway_1 = require("./mcpGateway");
|
|
8
|
+
const autoProtect_1 = require("./autoProtect");
|
|
8
9
|
/**
|
|
9
|
-
* One-click install:
|
|
10
|
-
*
|
|
11
|
-
* should be the default enforcement path for MCP tools.
|
|
10
|
+
* One-click install: discover/wrap every existing MCP server, install Cursor
|
|
11
|
+
* hooks for built-in Cursor actions, optional self-heal and fleet upload.
|
|
12
12
|
*/
|
|
13
13
|
async function installAllCommand(args, config) {
|
|
14
14
|
const creds = (0, config_1.requireCliSetup)(config, {
|
|
@@ -23,15 +23,17 @@ async function installAllCommand(args, config) {
|
|
|
23
23
|
clients: args.clients || 'all',
|
|
24
24
|
};
|
|
25
25
|
console.log('\n\x1b[1m\x1b[36mFullCourtDefense — install all AI clients\x1b[0m');
|
|
26
|
-
console.log('\x1b[
|
|
27
|
-
await (0, mcpGateway_1.
|
|
28
|
-
if (args.hooks
|
|
26
|
+
console.log('\x1b[2mAuto-protect existing MCP servers + Cursor action hooks. No --mcp-command or folder path needed.\x1b[0m\n');
|
|
27
|
+
await (0, mcpGateway_1.protectAllCommand)(gatewayArgs, config);
|
|
28
|
+
if (args.hooks !== 'false') {
|
|
29
29
|
console.log('\n\x1b[1mInstalling Cursor runtime hooks…\x1b[0m');
|
|
30
30
|
const hookArgs = {
|
|
31
31
|
shieldId: creds.shieldId,
|
|
32
32
|
shieldKey: creds.shieldKey,
|
|
33
33
|
apiUrl: creds.apiUrl,
|
|
34
34
|
project: args.cursorProject,
|
|
35
|
+
events: 'prompt,shell,mcp,file,read',
|
|
36
|
+
failClosed: 'true',
|
|
35
37
|
};
|
|
36
38
|
try {
|
|
37
39
|
await (0, installCursorHook_1.installCursorHookCommand)(hookArgs, config);
|
|
@@ -40,6 +42,13 @@ async function installAllCommand(args, config) {
|
|
|
40
42
|
console.log(`Cursor hooks skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
45
|
+
if (args.autoProtect === 'true') {
|
|
46
|
+
console.log('\n\x1b[1mScheduling self-healing MCP protection…\x1b[0m');
|
|
47
|
+
await (0, autoProtect_1.autoProtectCommand)({
|
|
48
|
+
...gatewayArgs,
|
|
49
|
+
intervalMinutes: args.intervalMinutes || '60',
|
|
50
|
+
}, config);
|
|
51
|
+
}
|
|
43
52
|
if (args.discover === 'true' || args.upload === 'true') {
|
|
44
53
|
console.log('\n\x1b[1mUploading desktop posture to AI Fleet…\x1b[0m');
|
|
45
54
|
const discoverArgs = {
|
|
@@ -52,5 +61,5 @@ async function installAllCommand(args, config) {
|
|
|
52
61
|
};
|
|
53
62
|
await (0, discover_1.discoverCommand)(discoverArgs, config);
|
|
54
63
|
}
|
|
55
|
-
console.log('\n\x1b[32mDone.\x1b[0m Restart each AI client you use, then run \x1b[1mfullcourtdefense
|
|
64
|
+
console.log('\n\x1b[32mDone.\x1b[0m Restart each AI client you use, then run \x1b[1mfullcourtdefense protect-all --dry-run true\x1b[0m to verify gateways.');
|
|
56
65
|
}
|
|
@@ -109,8 +109,8 @@ async function installCursorHookCommand(args, config) {
|
|
|
109
109
|
const projectScope = args.project === 'true';
|
|
110
110
|
const shadow = args.shadow === 'true';
|
|
111
111
|
const file = hooksJsonPath(projectScope);
|
|
112
|
-
const requested = (args.events || 'prompt,shell,mcp')
|
|
113
|
-
.split(
|
|
112
|
+
const requested = (args.events || 'prompt,shell,mcp,file,read')
|
|
113
|
+
.split(/[\s,]+/).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
114
114
|
const events = requested.filter((e) => EVENT_MAP[e]);
|
|
115
115
|
if (events.length === 0) {
|
|
116
116
|
console.error(`${COLOR.red}No valid events. Choose from: ${Object.keys(EVENT_MAP).join(', ')}${COLOR.reset}`);
|
|
@@ -1279,33 +1279,48 @@ async function protectAllCommand(args, config) {
|
|
|
1279
1279
|
: 'No MCP client config files found on this machine. Configure an MCP server in any client, then re-run.');
|
|
1280
1280
|
return;
|
|
1281
1281
|
}
|
|
1282
|
+
console.log(`\x1b[2mAuto-searching every known MCP client on this machine — ${files.length} config file(s) found.\x1b[0m\n`);
|
|
1282
1283
|
let totalWrapped = 0;
|
|
1283
1284
|
let totalManaged = 0;
|
|
1284
1285
|
let totalRemote = 0;
|
|
1286
|
+
let totalEmpty = 0;
|
|
1287
|
+
const presentClientKeys = new Set();
|
|
1285
1288
|
for (const file of files) {
|
|
1289
|
+
presentClientKeys.add(file.clientKey);
|
|
1286
1290
|
const agentClient = CLIENT_KEY_TO_AGENT[file.clientKey] || 'cursor';
|
|
1287
1291
|
const isToml = /\.toml$/i.test(file.path);
|
|
1288
1292
|
const stats = isToml
|
|
1289
1293
|
? transformCodexToml(file.path, 'wrap', gatewayConfig, dryRun)
|
|
1290
1294
|
: wrapJsonConfigFile(file.path, gatewayConfig, agentClient, dryRun);
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1295
|
+
console.log(`\x1b[1m${file.source}\x1b[0m \x1b[2m${file.path}\x1b[0m`);
|
|
1296
|
+
if (!stats) {
|
|
1297
|
+
console.log(' \x1b[33m• could not parse — left unchanged\x1b[0m\n');
|
|
1294
1298
|
continue;
|
|
1299
|
+
}
|
|
1300
|
+
const found = stats.wrapped.length + stats.skippedManaged.length + stats.skippedRemote.length;
|
|
1295
1301
|
totalWrapped += stats.wrapped.length;
|
|
1296
1302
|
totalManaged += stats.skippedManaged.length;
|
|
1297
1303
|
totalRemote += stats.skippedRemote.length;
|
|
1298
|
-
console.log(`\x1b[1m${file.source}\x1b[0m \x1b[2m${file.path}\x1b[0m`);
|
|
1299
1304
|
if (stats.wrapped.length)
|
|
1300
|
-
console.log(` \x1b[32m✓
|
|
1305
|
+
console.log(` \x1b[32m✓ ${dryRun ? 'would update' : 'updated'} → now protected:\x1b[0m ${stats.wrapped.join(', ')}`);
|
|
1301
1306
|
if (stats.skippedManaged.length)
|
|
1302
|
-
console.log(` \x1b[2m• already protected:\x1b[0m ${stats.skippedManaged.join(', ')}`);
|
|
1307
|
+
console.log(` \x1b[2m• already protected (unchanged):\x1b[0m ${stats.skippedManaged.join(', ')}`);
|
|
1303
1308
|
if (stats.skippedRemote.length)
|
|
1304
1309
|
console.log(` \x1b[33m• skipped (remote/no command):\x1b[0m ${stats.skippedRemote.join(', ')}`);
|
|
1310
|
+
if (found === 0) {
|
|
1311
|
+
totalEmpty++;
|
|
1312
|
+
console.log(' \x1b[2m• no MCP servers configured — nothing to protect\x1b[0m');
|
|
1313
|
+
}
|
|
1305
1314
|
console.log('');
|
|
1306
1315
|
}
|
|
1316
|
+
// Show leading tools that have NO config file at all, so it's clear they were searched.
|
|
1317
|
+
const consideredClients = exports.ALL_GATEWAY_CLIENTS.filter(c => !allowedClientKeys || (AGENT_TO_CLIENT_KEYS[c] || []).some(k => allowedClientKeys.has(k)));
|
|
1318
|
+
const notConfigured = consideredClients.filter(c => !(AGENT_TO_CLIENT_KEYS[c] || []).some(k => presentClientKeys.has(k)));
|
|
1319
|
+
if (notConfigured.length) {
|
|
1320
|
+
console.log(`\x1b[2mNot configured (no config file found): ${notConfigured.map(c => CLIENT_LABELS[c]).join(', ')}\x1b[0m\n`);
|
|
1321
|
+
}
|
|
1307
1322
|
console.log('\x1b[1mSummary\x1b[0m');
|
|
1308
|
-
console.log(` ${dryRun ? 'would
|
|
1323
|
+
console.log(` ${dryRun ? 'would update' : 'updated'}: ${totalWrapped} already protected: ${totalManaged} no servers: ${totalEmpty} skipped remote: ${totalRemote}`);
|
|
1309
1324
|
console.log(` Each protected server enforces your org Action Policies (allow / block / wait-for-human-approval) on its tool calls.`);
|
|
1310
1325
|
if (!dryRun && totalWrapped > 0)
|
|
1311
1326
|
console.log(' Backups saved next to each edited file (*.fcd-backup-*). Restart each client to load the protected servers.');
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ const hook_1 = require("./commands/hook");
|
|
|
45
45
|
const installCursorHook_1 = require("./commands/installCursorHook");
|
|
46
46
|
const mcpGateway_1 = require("./commands/mcpGateway");
|
|
47
47
|
const installAll_1 = require("./commands/installAll");
|
|
48
|
+
const autoProtect_1 = require("./commands/autoProtect");
|
|
48
49
|
const fs = __importStar(require("fs"));
|
|
49
50
|
const path = __importStar(require("path"));
|
|
50
51
|
function readCliVersion() {
|
|
@@ -112,7 +113,9 @@ function printHelp() {
|
|
|
112
113
|
configure Saves org API key, Organization ID, Shield ID, and Shield key to ~/.fullcourtdefense.yml.
|
|
113
114
|
After setup, other commands read credentials automatically — no --shield-id/--api-key needed.
|
|
114
115
|
setup Same as configure — validates credentials against the server before saving.
|
|
115
|
-
install-all One-click: MCP
|
|
116
|
+
install-all One-click: protect existing MCP servers in every AI client and
|
|
117
|
+
install Cursor hooks for built-in Cursor actions. No folder path
|
|
118
|
+
or --mcp-command needed. Use --hooks false to skip Cursor hooks.
|
|
116
119
|
install Alias for install-all.
|
|
117
120
|
scan Runs the scan. Use --local for inside-organization scans.
|
|
118
121
|
discover Finds MCP servers, local secrets, agent rules/skills, and machine
|
|
@@ -133,6 +136,10 @@ function printHelp() {
|
|
|
133
136
|
unprotect-all
|
|
134
137
|
Reverse protect-all: restore every server to its original command.
|
|
135
138
|
Also supports --clients to target one tool, and --dry-run.
|
|
139
|
+
auto-protect
|
|
140
|
+
Self-healing: schedule protect-all to re-run automatically so any
|
|
141
|
+
server that loses the proxy is re-wrapped. --interval-minutes 60
|
|
142
|
+
(default) or --on-logon true. --uninstall true / --status true.
|
|
136
143
|
install-mcp-gateway
|
|
137
144
|
Install MCP gateway into selected clients (--clients all for every tool).
|
|
138
145
|
install-cursor-mcp-gateway
|
|
@@ -169,8 +176,8 @@ function printHelp() {
|
|
|
169
176
|
fullcourtdefense setup
|
|
170
177
|
Saves to ~/.fullcourtdefense.yml — install, discover --upload, scan, gateway, and hooks use it automatically.
|
|
171
178
|
|
|
172
|
-
4. One-click protect every AI client on this machine with
|
|
173
|
-
fullcourtdefense install-all
|
|
179
|
+
4. One-click protect every AI client on this machine with Action Policies:
|
|
180
|
+
fullcourtdefense install-all
|
|
174
181
|
Verify installs:
|
|
175
182
|
fullcourtdefense discover --surface mcp
|
|
176
183
|
Or install one client: fullcourtdefense install-cursor-mcp-gateway ...
|
|
@@ -290,8 +297,8 @@ function printHelp() {
|
|
|
290
297
|
$ fullcourtdefense install-claude-desktop-mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
291
298
|
$ fullcourtdefense mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
292
299
|
$ fullcourtdefense setup
|
|
293
|
-
$ fullcourtdefense install-all
|
|
294
|
-
$ fullcourtdefense install-all --
|
|
300
|
+
$ fullcourtdefense install-all
|
|
301
|
+
$ fullcourtdefense install-all --auto-protect true --upload
|
|
295
302
|
$ fullcourtdefense install-mcp-gateway --clients cursor,codex --mcp-command npm --mcp-args "run mcp"
|
|
296
303
|
$ fullcourtdefense install-codex-mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
297
304
|
$ fullcourtdefense scan --system-prompt ./prompts/system.md --fail-threshold 80
|
|
@@ -500,6 +507,8 @@ async function main() {
|
|
|
500
507
|
...buildInstallArgs(),
|
|
501
508
|
hooks: flags.hooks,
|
|
502
509
|
discover: flags.discover,
|
|
510
|
+
autoProtect: flags['auto-protect'],
|
|
511
|
+
intervalMinutes: flags['interval-minutes'],
|
|
503
512
|
};
|
|
504
513
|
await (0, installAll_1.installAllCommand)(args, config);
|
|
505
514
|
break;
|
|
@@ -530,6 +539,21 @@ async function main() {
|
|
|
530
539
|
await (0, mcpGateway_1.unprotectAllCommand)(args, config);
|
|
531
540
|
break;
|
|
532
541
|
}
|
|
542
|
+
case 'auto-protect': {
|
|
543
|
+
const args = {
|
|
544
|
+
...buildGatewayArgs(),
|
|
545
|
+
clients: flags.clients,
|
|
546
|
+
config: flags.config,
|
|
547
|
+
dryRun: flags['dry-run'],
|
|
548
|
+
install: flags.install,
|
|
549
|
+
uninstall: flags.uninstall,
|
|
550
|
+
status: flags.status,
|
|
551
|
+
intervalMinutes: flags['interval-minutes'],
|
|
552
|
+
onLogon: flags['on-logon'],
|
|
553
|
+
};
|
|
554
|
+
await (0, autoProtect_1.autoProtectCommand)(args, config);
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
533
557
|
case 'install-cursor-mcp-gateway': {
|
|
534
558
|
const args = {
|
|
535
559
|
...buildGatewayArgs(),
|
package/dist/version.json
CHANGED