fullcourtdefense-cli 1.6.10 → 1.6.12
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.
|
@@ -6,6 +6,7 @@ const discover_1 = require("./discover");
|
|
|
6
6
|
const installCursorHook_1 = require("./installCursorHook");
|
|
7
7
|
const mcpGateway_1 = require("./mcpGateway");
|
|
8
8
|
const autoProtect_1 = require("./autoProtect");
|
|
9
|
+
const restartNotice_1 = require("./restartNotice");
|
|
9
10
|
/**
|
|
10
11
|
* One-click install: discover/wrap every existing MCP server, install Cursor
|
|
11
12
|
* hooks for built-in Cursor actions, optional self-heal and fleet upload.
|
|
@@ -32,7 +33,7 @@ async function installAllCommand(args, config) {
|
|
|
32
33
|
shieldKey: creds.shieldKey,
|
|
33
34
|
apiUrl: creds.apiUrl,
|
|
34
35
|
project: args.cursorProject,
|
|
35
|
-
events: 'shell,mcp',
|
|
36
|
+
events: 'prompt,shell,mcp',
|
|
36
37
|
failClosed: 'true',
|
|
37
38
|
};
|
|
38
39
|
try {
|
|
@@ -61,5 +62,6 @@ async function installAllCommand(args, config) {
|
|
|
61
62
|
};
|
|
62
63
|
await (0, discover_1.discoverCommand)(discoverArgs, config);
|
|
63
64
|
}
|
|
64
|
-
console.log('\n\x1b[32mDone.\x1b[0m
|
|
65
|
+
console.log('\n\x1b[32mDone.\x1b[0m Run \x1b[1mfullcourtdefense protect-all --dry-run true\x1b[0m to verify gateways.');
|
|
66
|
+
await (0, restartNotice_1.promptRestartNotice)();
|
|
65
67
|
}
|
|
@@ -39,6 +39,7 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const os = __importStar(require("os"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const config_1 = require("../config");
|
|
42
|
+
const restartNotice_1 = require("./restartNotice");
|
|
42
43
|
const COLOR = {
|
|
43
44
|
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
44
45
|
red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', cyan: '\x1b[36m', gray: '\x1b[90m',
|
|
@@ -168,7 +169,8 @@ async function installCursorHookCommand(args, config) {
|
|
|
168
169
|
console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense setup${COLOR.reset} first, then re-run install.`);
|
|
169
170
|
}
|
|
170
171
|
console.log('');
|
|
171
|
-
|
|
172
|
+
(0, restartNotice_1.printRestartNotice)('Cursor');
|
|
173
|
+
console.log(`${COLOR.gray}Verify in Cursor → Settings → Hooks after restart.${COLOR.reset}`);
|
|
172
174
|
console.log(`${COLOR.gray}Remove with:${COLOR.reset} fullcourtdefense uninstall-cursor-hook${projectScope ? ' --project true' : ''}`);
|
|
173
175
|
}
|
|
174
176
|
async function uninstallCursorHookCommand(args) {
|
|
@@ -53,6 +53,7 @@ const config_1 = require("../config");
|
|
|
53
53
|
const discoverPaths_1 = require("./discoverPaths");
|
|
54
54
|
const discover_1 = require("./discover");
|
|
55
55
|
const deterministicGuard_1 = require("./deterministicGuard");
|
|
56
|
+
const restartNotice_1 = require("./restartNotice");
|
|
56
57
|
const DEFAULT_API_URL = 'https://api.fullcourtdefense.ai';
|
|
57
58
|
const MANAGED_SERVER_NAME = 'agentguard-gateway';
|
|
58
59
|
const INSTALL_GATEWAY_ARGS = { includeShieldKey: false };
|
|
@@ -936,7 +937,7 @@ async function installMcpGatewayCommand(args, config) {
|
|
|
936
937
|
console.log(` - ${failure}`);
|
|
937
938
|
}
|
|
938
939
|
console.log(` Runtime identity: ${detectedDeveloperName(args)}`);
|
|
939
|
-
|
|
940
|
+
(0, restartNotice_1.printRestartNotice)(clients.map(c => CLIENT_LABELS[c]).join(', '));
|
|
940
941
|
if (args.upload === 'true') {
|
|
941
942
|
console.log('\nUploading desktop inventory to AI Fleet…');
|
|
942
943
|
const creds = (0, config_1.resolveCliCredentials)(config, { apiKey: args.apiKey, apiUrl: args.apiUrl });
|
|
@@ -1334,8 +1335,10 @@ async function protectAllCommand(args, config) {
|
|
|
1334
1335
|
console.log('\x1b[1mSummary\x1b[0m');
|
|
1335
1336
|
console.log(` ${dryRun ? 'would update' : 'updated'}: ${totalWrapped} already protected: ${totalManaged} no servers: ${totalEmpty} skipped remote: ${totalRemote}`);
|
|
1336
1337
|
console.log(` Each protected server enforces your org Action Policies (allow / block / wait-for-human-approval) on its tool calls.`);
|
|
1337
|
-
if (!dryRun && totalWrapped > 0)
|
|
1338
|
-
console.log(' Backups saved next to each edited file (*.fcd-backup-*).
|
|
1338
|
+
if (!dryRun && totalWrapped > 0) {
|
|
1339
|
+
console.log(' Backups saved next to each edited file (*.fcd-backup-*).');
|
|
1340
|
+
(0, restartNotice_1.printRestartNotice)(allowedClientKeys ? [...allowedClientKeys].join(', ') : undefined);
|
|
1341
|
+
}
|
|
1339
1342
|
console.log(' Reverse anytime with: \x1b[1mfullcourtdefense unprotect-all\x1b[0m');
|
|
1340
1343
|
}
|
|
1341
1344
|
async function unprotectAllCommand(args, config) {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printRestartNotice = printRestartNotice;
|
|
4
|
+
exports.promptRestartNotice = promptRestartNotice;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
function printRestartNotice(clients = 'Cursor, Claude, Codex, VS Code, Windsurf, Gemini') {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('\x1b[33m\x1b[1mACTION REQUIRED: restart your AI clients\x1b[0m');
|
|
9
|
+
console.log(`\x1b[33mRestart/reload ${clients} so they load the protected MCP gateway and hooks.\x1b[0m`);
|
|
10
|
+
console.log('\x1b[2mAlready-running MCP servers may keep using old unprotected processes until the client restarts.\x1b[0m');
|
|
11
|
+
}
|
|
12
|
+
function shouldPrompt() {
|
|
13
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
14
|
+
return false;
|
|
15
|
+
if (process.env.CI === 'true' || process.env.FCD_NO_RESTART_PROMPT === 'true')
|
|
16
|
+
return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function parseJsonArray(raw) {
|
|
20
|
+
const trimmed = raw.trim();
|
|
21
|
+
if (!trimmed)
|
|
22
|
+
return [];
|
|
23
|
+
const parsed = JSON.parse(trimmed);
|
|
24
|
+
const rows = Array.isArray(parsed) ? parsed : [parsed];
|
|
25
|
+
return rows
|
|
26
|
+
.map((row) => row)
|
|
27
|
+
.map((row) => ({
|
|
28
|
+
pid: Number(row.ProcessId || row.pid),
|
|
29
|
+
name: String(row.Name || row.name || ''),
|
|
30
|
+
executablePath: typeof row.ExecutablePath === 'string' ? row.ExecutablePath : undefined,
|
|
31
|
+
}))
|
|
32
|
+
.filter((row) => Number.isFinite(row.pid) && row.pid > 0 && row.name);
|
|
33
|
+
}
|
|
34
|
+
function detectWindowsClients() {
|
|
35
|
+
const script = [
|
|
36
|
+
"$names=@('Cursor.exe','Claude.exe','Code.exe','Windsurf.exe');",
|
|
37
|
+
'Get-CimInstance Win32_Process |',
|
|
38
|
+
'Where-Object { $names -contains $_.Name } |',
|
|
39
|
+
'Select-Object ProcessId,Name,ExecutablePath |',
|
|
40
|
+
'ConvertTo-Json -Compress',
|
|
41
|
+
].join(' ');
|
|
42
|
+
try {
|
|
43
|
+
return parseJsonArray((0, child_process_1.execFileSync)('powershell.exe', ['-NoProfile', '-Command', script], { encoding: 'utf8' }));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function detectRestartableClients() {
|
|
50
|
+
if (process.platform === 'win32')
|
|
51
|
+
return detectWindowsClients();
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
async function askRestart(clients) {
|
|
55
|
+
const names = [...new Set(clients.map(client => client.name.replace(/\.exe$/i, '')))].join(', ');
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
process.stdout.write(`\n\x1b[1mRestart detected AI clients now (${names})? Press r then Enter to restart, or Enter to skip.\x1b[0m `);
|
|
58
|
+
process.stdin.resume();
|
|
59
|
+
process.stdin.once('data', (chunk) => {
|
|
60
|
+
process.stdin.pause();
|
|
61
|
+
resolve(String(chunk).trim().toLowerCase() === 'r');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async function restartWindowsClients(clients) {
|
|
66
|
+
const launchPaths = [...new Set(clients.map(client => client.executablePath).filter((value) => !!value))];
|
|
67
|
+
for (const client of clients) {
|
|
68
|
+
try {
|
|
69
|
+
(0, child_process_1.execFileSync)('taskkill.exe', ['/PID', String(client.pid), '/T', '/F'], { stdio: 'ignore' });
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// The process may already be gone.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
await new Promise(resolve => setTimeout(resolve, 1200));
|
|
76
|
+
for (const exePath of launchPaths) {
|
|
77
|
+
try {
|
|
78
|
+
const child = (0, child_process_1.spawn)(exePath, [], { detached: true, stdio: 'ignore' });
|
|
79
|
+
child.unref();
|
|
80
|
+
console.log(`Restarted ${exePath}`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.log(`Could not restart ${exePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function restartClients(clients) {
|
|
88
|
+
if (process.platform === 'win32') {
|
|
89
|
+
await restartWindowsClients(clients);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log('Automatic restart is not supported on this OS yet. Please restart your AI clients manually.');
|
|
93
|
+
}
|
|
94
|
+
async function promptRestartNotice(clients = 'Cursor, Claude, Codex, VS Code, Windsurf, Gemini') {
|
|
95
|
+
printRestartNotice(clients);
|
|
96
|
+
if (!shouldPrompt())
|
|
97
|
+
return;
|
|
98
|
+
const runningClients = detectRestartableClients();
|
|
99
|
+
if (runningClients.length === 0) {
|
|
100
|
+
console.log('\n\x1b[2mNo running GUI AI clients detected. Start them again to load protection.\x1b[0m');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (await askRestart(runningClients)) {
|
|
104
|
+
await restartClients(runningClients);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log('Skipped automatic restart. Protection applies after you restart/reload the clients.');
|
|
108
|
+
}
|
|
109
|
+
}
|
package/dist/version.json
CHANGED