fullcourtdefense-cli 1.7.2 → 1.7.4
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/discover.js +42 -10
- package/dist/commands/discoverSchedule.d.ts +2 -0
- package/dist/commands/discoverSchedule.js +52 -15
- package/dist/commands/hook.js +1 -1
- package/dist/commands/installCursorHook.js +1 -1
- package/dist/commands/mcpGateway.js +1 -1
- package/dist/config.js +6 -6
- package/dist/index.js +16 -10
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -64,6 +64,30 @@ function parseSurfaces(args) {
|
|
|
64
64
|
}
|
|
65
65
|
const SECRET_VALUE = /sk-[A-Za-z0-9_-]{12,}|ghp_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{20,}/;
|
|
66
66
|
const SECRET_KEY = /key|token|secret|password|credential|api[_-]?key/i;
|
|
67
|
+
const SECRET_ARG_NAME = /^(?:--?(?:shield-key|api-key|token|mcp-token|mcp-api-key|key|secret|password|credential)|(?:.*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL).*))$/i;
|
|
68
|
+
function redactCommandArgs(args) {
|
|
69
|
+
const out = [];
|
|
70
|
+
let redactNext = false;
|
|
71
|
+
for (const arg of args || []) {
|
|
72
|
+
if (redactNext) {
|
|
73
|
+
out.push('[redacted]');
|
|
74
|
+
redactNext = false;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const eq = arg.indexOf('=');
|
|
78
|
+
if (eq > 0 && SECRET_ARG_NAME.test(arg.slice(0, eq))) {
|
|
79
|
+
out.push(`${arg.slice(0, eq + 1)}[redacted]`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (SECRET_ARG_NAME.test(arg)) {
|
|
83
|
+
out.push(arg);
|
|
84
|
+
redactNext = true;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
out.push(arg.replace(SECRET_VALUE, '[redacted]'));
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
67
91
|
function machineFingerprint() {
|
|
68
92
|
const seed = [os.hostname(), os.userInfo().username, os.platform(), os.arch()].join('|');
|
|
69
93
|
return crypto.createHash('sha256').update(seed).digest('hex').slice(0, 16);
|
|
@@ -505,10 +529,11 @@ async function upload(servers, host, clientCoverage, apiUrl, auth, connectorName
|
|
|
505
529
|
tools: s.tools,
|
|
506
530
|
})),
|
|
507
531
|
};
|
|
508
|
-
// Prefer the
|
|
509
|
-
// `
|
|
532
|
+
// Prefer the enrolled Shield-key endpoint when available so the zero-paste
|
|
533
|
+
// `login --token` flow works even if an older org API key remains in config.
|
|
534
|
+
// Passing --api-key is the explicit opt-in to the org API key path.
|
|
510
535
|
const base = apiUrl.replace(/\/$/, '');
|
|
511
|
-
const useShieldKey = !auth.
|
|
536
|
+
const useShieldKey = !auth.preferApiKey && !!(auth.shieldId && auth.shieldKey);
|
|
512
537
|
const url = useShieldKey ? `${base}/api/cli/discovery` : `${base}/api/cicd/discovery/mcp`;
|
|
513
538
|
const headers = { 'Content-Type': 'application/json' };
|
|
514
539
|
if (useShieldKey) {
|
|
@@ -601,12 +626,19 @@ async function discoverCommand(args, config) {
|
|
|
601
626
|
console.log('Removed daily desktop discovery schedule.');
|
|
602
627
|
return;
|
|
603
628
|
}
|
|
604
|
-
if (args.schedule === 'daily') {
|
|
629
|
+
if (args.schedule === 'daily' || args.schedule === 'logon') {
|
|
605
630
|
const hour = args.scheduleHour ? Number.parseInt(args.scheduleHour, 10) : 9;
|
|
606
|
-
|
|
631
|
+
const surface = surfaces.size === 4 ? 'all' : Array.from(surfaces).join(',');
|
|
632
|
+
(0, discoverSchedule_1.installDailyDiscoverSchedule)({
|
|
633
|
+
userEmail: args.userEmail,
|
|
634
|
+
hour: Number.isFinite(hour) ? hour : 9,
|
|
635
|
+
surface,
|
|
636
|
+
trigger: args.schedule,
|
|
637
|
+
});
|
|
607
638
|
if (!silent) {
|
|
608
|
-
|
|
609
|
-
console.log(
|
|
639
|
+
const when = args.schedule === 'logon' ? 'at logon' : `daily at ${String(Number.isFinite(hour) ? hour : 9).padStart(2, '0')}:00`;
|
|
640
|
+
console.log(`Desktop discovery scheduled ${when} (runs discover --surface ${surface} --upload --silent${args.userEmail ? ` --user-email ${args.userEmail}` : ''}).`);
|
|
641
|
+
console.log('Uses login Shield credentials or an explicit org API key.');
|
|
610
642
|
}
|
|
611
643
|
return;
|
|
612
644
|
}
|
|
@@ -668,11 +700,11 @@ async function discoverCommand(args, config) {
|
|
|
668
700
|
const hasShieldCreds = !!(creds.shieldId && creds.shieldKey);
|
|
669
701
|
if (!creds.apiKey && !hasShieldCreds) {
|
|
670
702
|
if (!silent) {
|
|
671
|
-
console.error(`\n${COLOR.red}--upload needs credentials.${COLOR.reset} Run \`fullcourtdefense login --token <fleet-token>\`
|
|
703
|
+
console.error(`\n${COLOR.red}--upload needs credentials.${COLOR.reset} Run \`fullcourtdefense login --token <fleet-enrollment-token>\` first.`);
|
|
672
704
|
}
|
|
673
705
|
process.exit(1);
|
|
674
706
|
}
|
|
675
|
-
await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey }, args.connectorName, uploadExtras);
|
|
707
|
+
await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey, preferApiKey: !!args.apiKey }, args.connectorName, uploadExtras);
|
|
676
708
|
}
|
|
677
709
|
if (silent) {
|
|
678
710
|
if (args.upload === 'true') {
|
|
@@ -714,7 +746,7 @@ async function discoverCommand(args, config) {
|
|
|
714
746
|
found.sort((a, b) => order[a.riskLevel] - order[b.riskLevel]);
|
|
715
747
|
for (const s of found) {
|
|
716
748
|
const col = riskColor(s.riskLevel);
|
|
717
|
-
const target = s.command ? `${s.command} ${(s.args
|
|
749
|
+
const target = s.command ? `${s.command} ${redactCommandArgs(s.args).join(' ')}`.trim() : (s.url || 'unknown');
|
|
718
750
|
console.log(` ${col}[${s.riskLevel.toUpperCase()}]${COLOR.reset} ${s.serverName} — ${target} (${proxyStatusLabel(s.proxyStatus)})`);
|
|
719
751
|
}
|
|
720
752
|
console.log('');
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export interface ScheduleArgs {
|
|
2
2
|
userEmail?: string;
|
|
3
3
|
hour?: number;
|
|
4
|
+
surface?: string;
|
|
5
|
+
trigger?: 'daily' | 'logon';
|
|
4
6
|
}
|
|
5
7
|
export declare function installDailyDiscoverSchedule(args?: ScheduleArgs): void;
|
|
6
8
|
export declare function uninstallDailyDiscoverSchedule(): void;
|
|
@@ -40,33 +40,56 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const os = __importStar(require("os"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const TASK_NAME = 'FullCourtDefenseDesktopDiscover';
|
|
43
|
-
|
|
43
|
+
const STARTUP_LAUNCHER = 'FullCourtDefenseDesktopDiscover.cmd';
|
|
44
|
+
function windowsStartupLauncherPath() {
|
|
45
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
46
|
+
return path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', STARTUP_LAUNCHER);
|
|
47
|
+
}
|
|
48
|
+
function discoverCommandLine(userEmail, surface) {
|
|
44
49
|
const node = process.execPath;
|
|
45
50
|
const script = path.resolve(process.argv[1] || path.join(__dirname, '..', 'index.js'));
|
|
46
51
|
const q = (value) => (/\s/.test(value) ? `"${value}"` : value);
|
|
47
52
|
const parts = [q(node), q(script), 'discover', '--upload', '--silent'];
|
|
53
|
+
if (surface)
|
|
54
|
+
parts.push('--surface', q(surface));
|
|
48
55
|
if (userEmail)
|
|
49
56
|
parts.push('--user-email', q(userEmail));
|
|
50
57
|
return parts.join(' ');
|
|
51
58
|
}
|
|
52
|
-
function installWindowsSchedule(hour, userEmail) {
|
|
53
|
-
const tr = discoverCommandLine(userEmail);
|
|
54
|
-
const st = `${String(hour).padStart(2, '0')}:00`;
|
|
59
|
+
function installWindowsSchedule(hour, userEmail, surface, trigger = 'daily') {
|
|
60
|
+
const tr = discoverCommandLine(userEmail, surface);
|
|
55
61
|
try {
|
|
56
62
|
(0, child_process_1.execSync)(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'ignore' });
|
|
57
63
|
}
|
|
58
64
|
catch {
|
|
59
65
|
// task may not exist
|
|
60
66
|
}
|
|
61
|
-
|
|
67
|
+
const schedule = trigger === 'logon'
|
|
68
|
+
? '/SC ONLOGON'
|
|
69
|
+
: `/SC DAILY /ST ${String(hour).padStart(2, '0')}:00`;
|
|
70
|
+
try {
|
|
71
|
+
(0, child_process_1.execSync)(`schtasks /Create /F /TN "${TASK_NAME}" ${schedule} /RL LIMITED /TR "${tr.replace(/"/g, '\\"')}"`, { stdio: 'inherit' });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (trigger !== 'logon')
|
|
75
|
+
throw error;
|
|
76
|
+
const launcher = windowsStartupLauncherPath();
|
|
77
|
+
fs.mkdirSync(path.dirname(launcher), { recursive: true });
|
|
78
|
+
fs.writeFileSync(launcher, `@echo off\r\n${tr} > "%USERPROFILE%\\.fullcourtdefense-discover.log" 2>&1\r\n`, 'utf8');
|
|
79
|
+
console.log(`Task Scheduler denied access; installed user logon launcher instead: ${launcher}`);
|
|
80
|
+
}
|
|
62
81
|
}
|
|
63
|
-
function installMacSchedule(hour, userEmail) {
|
|
82
|
+
function installMacSchedule(hour, userEmail, surface, trigger = 'daily') {
|
|
64
83
|
const label = 'ai.fullcourtdefense.discover';
|
|
65
84
|
const plistDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
66
85
|
const plistPath = path.join(plistDir, `${label}.plist`);
|
|
67
86
|
fs.mkdirSync(plistDir, { recursive: true });
|
|
68
|
-
const cmd = discoverCommandLine(userEmail).split(/\s+/);
|
|
87
|
+
const cmd = discoverCommandLine(userEmail, surface).split(/\s+/);
|
|
69
88
|
const program = cmd.shift() || process.execPath;
|
|
89
|
+
const triggerXml = trigger === 'logon'
|
|
90
|
+
? '<key>RunAtLoad</key><true/>'
|
|
91
|
+
: `<key>StartCalendarInterval</key>
|
|
92
|
+
<dict><key>Hour</key><integer>${hour}</integer><key>Minute</key><integer>0</integer></dict>`;
|
|
70
93
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
71
94
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
72
95
|
<plist version="1.0">
|
|
@@ -74,8 +97,7 @@ function installMacSchedule(hour, userEmail) {
|
|
|
74
97
|
<key>Label</key><string>${label}</string>
|
|
75
98
|
<key>ProgramArguments</key>
|
|
76
99
|
<array>${[program, ...cmd].map(part => `<string>${part.replace(/&/g, '&').replace(/</g, '<')}</string>`).join('')}</array>
|
|
77
|
-
|
|
78
|
-
<dict><key>Hour</key><integer>${hour}</integer><key>Minute</key><integer>0</integer></dict>
|
|
100
|
+
${triggerXml}
|
|
79
101
|
<key>StandardOutPath</key><string>${path.join(os.homedir(), '.fullcourtdefense-discover.log')}</string>
|
|
80
102
|
<key>StandardErrorPath</key><string>${path.join(os.homedir(), '.fullcourtdefense-discover.err.log')}</string>
|
|
81
103
|
</dict>
|
|
@@ -84,8 +106,9 @@ function installMacSchedule(hour, userEmail) {
|
|
|
84
106
|
(0, child_process_1.spawnSync)('launchctl', ['unload', plistPath], { stdio: 'ignore' });
|
|
85
107
|
(0, child_process_1.spawnSync)('launchctl', ['load', plistPath], { stdio: 'inherit' });
|
|
86
108
|
}
|
|
87
|
-
function installLinuxCron(hour, userEmail) {
|
|
88
|
-
const
|
|
109
|
+
function installLinuxCron(hour, userEmail, surface, trigger = 'daily') {
|
|
110
|
+
const schedule = trigger === 'logon' ? '@reboot' : `0 ${hour} * * *`;
|
|
111
|
+
const line = `${schedule} ${discoverCommandLine(userEmail, surface)} >> ${path.join(os.homedir(), '.fullcourtdefense-discover.log')} 2>&1`;
|
|
89
112
|
const marker = '# fullcourtdefense-discover';
|
|
90
113
|
let crontab = '';
|
|
91
114
|
try {
|
|
@@ -101,16 +124,30 @@ function installLinuxCron(hour, userEmail) {
|
|
|
101
124
|
}
|
|
102
125
|
function installDailyDiscoverSchedule(args = {}) {
|
|
103
126
|
const hour = Number.isFinite(args.hour) ? Math.min(23, Math.max(0, args.hour)) : 9;
|
|
127
|
+
const trigger = args.trigger || 'daily';
|
|
104
128
|
if (process.platform === 'win32')
|
|
105
|
-
installWindowsSchedule(hour, args.userEmail);
|
|
129
|
+
installWindowsSchedule(hour, args.userEmail, args.surface, trigger);
|
|
106
130
|
else if (process.platform === 'darwin')
|
|
107
|
-
installMacSchedule(hour, args.userEmail);
|
|
131
|
+
installMacSchedule(hour, args.userEmail, args.surface, trigger);
|
|
108
132
|
else
|
|
109
|
-
installLinuxCron(hour, args.userEmail);
|
|
133
|
+
installLinuxCron(hour, args.userEmail, args.surface, trigger);
|
|
110
134
|
}
|
|
111
135
|
function uninstallDailyDiscoverSchedule() {
|
|
112
136
|
if (process.platform === 'win32') {
|
|
113
|
-
|
|
137
|
+
try {
|
|
138
|
+
(0, child_process_1.execSync)(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'inherit' });
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Scheduled Task may not exist or may be inaccessible.
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const launcher = windowsStartupLauncherPath();
|
|
145
|
+
if (fs.existsSync(launcher))
|
|
146
|
+
fs.unlinkSync(launcher);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// ignore
|
|
150
|
+
}
|
|
114
151
|
return;
|
|
115
152
|
}
|
|
116
153
|
if (process.platform === 'darwin') {
|
package/dist/commands/hook.js
CHANGED
|
@@ -409,7 +409,7 @@ async function hookCommand(args, config) {
|
|
|
409
409
|
// No Shield configured → allow (fail-open by design: a misconfigured machine
|
|
410
410
|
// must not brick the developer's IDE).
|
|
411
411
|
if (!shieldId) {
|
|
412
|
-
respond(false, undefined, 'FullCourtDefense hook installed but no Shield ID configured (run `fullcourtdefense
|
|
412
|
+
respond(false, undefined, 'FullCourtDefense hook installed but no Shield ID configured (run `fullcourtdefense login --token <fleet-enrollment-token>`).');
|
|
413
413
|
}
|
|
414
414
|
// --- Action Policy enforcement for actions (shell / mcp / file / read) ---
|
|
415
415
|
if (event === 'shell' || event === 'mcp' || event === 'file' || event === 'read') {
|
|
@@ -168,7 +168,7 @@ async function installCursorHookCommand(args, config) {
|
|
|
168
168
|
console.log(`${COLOR.gray}Creds:${COLOR.reset} ${credFile}`);
|
|
169
169
|
if (!creds.shieldId) {
|
|
170
170
|
console.log('');
|
|
171
|
-
console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense
|
|
171
|
+
console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense login --token <fleet-enrollment-token>${COLOR.reset} first, then re-run install.`);
|
|
172
172
|
}
|
|
173
173
|
console.log('');
|
|
174
174
|
(0, restartNotice_1.printRestartNotice)('Cursor');
|
|
@@ -172,7 +172,7 @@ function resolveGatewayConfig(args, config, defaults = {}) {
|
|
|
172
172
|
apiUrl: args.apiUrl,
|
|
173
173
|
});
|
|
174
174
|
if (!creds.shieldId) {
|
|
175
|
-
throw new Error('Missing Shield ID. Run `fullcourtdefense
|
|
175
|
+
throw new Error('Missing Shield ID. Run `fullcourtdefense login --token <fleet-enrollment-token>` once (saves per-machine Shield credentials to ~/.fullcourtdefense.yml).');
|
|
176
176
|
}
|
|
177
177
|
const agentClient = normalizeAgentClient(args.agentClient || process.env.FCD_AGENT_CLIENT || detectRuntimeAgentClient(), defaults.agentClient || 'cursor');
|
|
178
178
|
const developerName = detectedDeveloperName(args);
|
package/dist/config.js
CHANGED
|
@@ -177,11 +177,11 @@ function loadConfig(configPath) {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
// Plain CLI usage is intentionally global: `fullcourtdefense
|
|
181
|
-
// ~/.fullcourtdefense.yml, then
|
|
182
|
-
// Shield from any repo. Project
|
|
183
|
-
//
|
|
184
|
-
// user's
|
|
180
|
+
// Plain CLI usage is intentionally global: `fullcourtdefense login --token ...`
|
|
181
|
+
// writes per-machine Shield credentials to ~/.fullcourtdefense.yml, then
|
|
182
|
+
// `install-all` should use that same enrolled Shield from any repo. Project
|
|
183
|
+
// config is still supported for scan defaults and explicit `--config`
|
|
184
|
+
// workflows, but it must not silently override the user's machine enrollment.
|
|
185
185
|
return configPath
|
|
186
186
|
? mergeConfig(homeConfig, projectConfig)
|
|
187
187
|
: mergeConfig(projectConfig, homeConfig);
|
|
@@ -237,7 +237,7 @@ function requireCliSetup(config, overrides = {}, options = {}) {
|
|
|
237
237
|
if (requireOrganizationId && !creds.organizationId)
|
|
238
238
|
missing.push('organizationId');
|
|
239
239
|
if (missing.length > 0) {
|
|
240
|
-
throw new Error(`Missing ${missing.join(', ')}. Run \`fullcourtdefense
|
|
240
|
+
throw new Error(`Missing ${missing.join(', ')}. Run \`fullcourtdefense login --token <fleet-enrollment-token>\` once — it saves per-machine Shield credentials to ~/.fullcourtdefense.yml so you never pass them again.`);
|
|
241
241
|
}
|
|
242
242
|
return creds;
|
|
243
243
|
}
|
package/dist/index.js
CHANGED
|
@@ -112,9 +112,11 @@ function printHelp() {
|
|
|
112
112
|
\x1b[1mCommands:\x1b[0m
|
|
113
113
|
help Show this onboarding guide and command reference.
|
|
114
114
|
doctor First step. Checks outbound HTTPS access to FullCourtDefense.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
login Enrolls this machine with a fleet enrollment token and saves
|
|
116
|
+
per-machine Shield credentials to ~/.fullcourtdefense.yml.
|
|
117
|
+
configure Legacy/manual setup: saves org API key, Organization ID,
|
|
118
|
+
Shield ID, and Shield key to ~/.fullcourtdefense.yml.
|
|
119
|
+
setup Same as configure — manual fallback when not using fleet login.
|
|
118
120
|
install-all One-click: protect existing MCP servers in every AI client and
|
|
119
121
|
install Cursor hooks for built-in Cursor actions. No folder path
|
|
120
122
|
or --mcp-command needed. Use --hooks false to skip Cursor hooks.
|
|
@@ -122,6 +124,7 @@ function printHelp() {
|
|
|
122
124
|
scan Runs the scan. Use --local for inside-organization scans.
|
|
123
125
|
discover Finds MCP servers, local secrets, agent rules/skills, and machine
|
|
124
126
|
blast radius on this laptop. Use --surface all --upload for AI Fleet.
|
|
127
|
+
Use --schedule logon to upload inventory/posture at machine login.
|
|
125
128
|
install-cursor-hook
|
|
126
129
|
Installs a Cursor hook so EVERY agent action on this machine is
|
|
127
130
|
checked against your org's Action Policies — in any repo/folder.
|
|
@@ -167,21 +170,23 @@ function printHelp() {
|
|
|
167
170
|
init Creates a starter .fullcourtdefense.yml config file.
|
|
168
171
|
|
|
169
172
|
\x1b[1mNew User Process:\x1b[0m
|
|
170
|
-
1. In the web app,
|
|
171
|
-
|
|
173
|
+
1. In the web app, an org admin creates a fleet enrollment token:
|
|
174
|
+
AI Fleet → Settings → Fleet enrollment token.
|
|
172
175
|
|
|
173
176
|
2. Check outbound connectivity from the customer machine:
|
|
174
177
|
fullcourtdefense doctor
|
|
175
178
|
This confirms the machine can reach https://api.fullcourtdefense.ai over HTTPS.
|
|
176
179
|
|
|
177
|
-
3.
|
|
178
|
-
fullcourtdefense
|
|
179
|
-
|
|
180
|
+
3. Enroll this developer machine (one time):
|
|
181
|
+
fullcourtdefense login --token <fleet-enrollment-token>
|
|
182
|
+
Or set FCD_ENROLL_TOKEN and run: fullcourtdefense login
|
|
183
|
+
Saves per-machine Shield credentials to ~/.fullcourtdefense.yml —
|
|
184
|
+
install, discover --upload, scan, gateway, and hooks use it automatically.
|
|
180
185
|
|
|
181
186
|
4. One-click protect every AI client on this machine with Action Policies:
|
|
182
187
|
fullcourtdefense install-all
|
|
183
188
|
Verify installs:
|
|
184
|
-
fullcourtdefense
|
|
189
|
+
fullcourtdefense protect-all --dry-run true
|
|
185
190
|
Or install one client: fullcourtdefense install-cursor-mcp-gateway ...
|
|
186
191
|
|
|
187
192
|
5. Run a guided local scan:
|
|
@@ -298,9 +303,10 @@ function printHelp() {
|
|
|
298
303
|
$ fullcourtdefense install-claude-code-mcp-gateway --scope project --mcp-command npm --mcp-args "run mcp"
|
|
299
304
|
$ fullcourtdefense install-claude-desktop-mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
300
305
|
$ fullcourtdefense mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
301
|
-
$ fullcourtdefense
|
|
306
|
+
$ fullcourtdefense login --token <fleet-enrollment-token>
|
|
302
307
|
$ fullcourtdefense install-all
|
|
303
308
|
$ fullcourtdefense install-all --auto-protect true --upload
|
|
309
|
+
$ fullcourtdefense discover --surface all --upload --schedule logon
|
|
304
310
|
$ fullcourtdefense install-mcp-gateway --clients cursor,codex --mcp-command npm --mcp-args "run mcp"
|
|
305
311
|
$ fullcourtdefense install-codex-mcp-gateway --mcp-command npm --mcp-args "run mcp"
|
|
306
312
|
$ fullcourtdefense scan --system-prompt ./prompts/system.md --fail-threshold 80
|
package/dist/version.json
CHANGED