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.
@@ -37,7 +37,7 @@ exports.hookCommand = hookCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
- const DEFAULT_API_URL = 'https://api.fullcourtdefense.ai';
40
+ const config_1 = require("../config");
41
41
  const DEBUG_LOG = path.join(os.homedir(), '.fullcourtdefense-hook.log');
42
42
  /** Append a diagnostic line so we can see exactly what Cursor invoked + the verdict. */
43
43
  function dbg(obj) {
@@ -78,27 +78,6 @@ function readStdin() {
78
78
  setTimeout(finish, 2000).unref?.();
79
79
  });
80
80
  }
81
- /** Load ~/.fullcourtdefense.yml (machine-wide creds) as a fallback to project config. */
82
- function loadHomeShield() {
83
- try {
84
- const candidates = ['.fullcourtdefense.yml', '.fullcourtdefense.yaml'];
85
- for (const name of candidates) {
86
- const p = path.join(os.homedir(), name);
87
- if (!fs.existsSync(p))
88
- continue;
89
- const text = fs.readFileSync(p, 'utf8');
90
- const pick = (key) => {
91
- const m = text.match(new RegExp(`^\\s*${key}\\s*:\\s*(.+)\\s*$`, 'm'));
92
- if (!m)
93
- return undefined;
94
- return m[1].replace(/^["']|["']$/g, '').trim() || undefined;
95
- };
96
- return { shieldId: pick('shieldId'), shieldKey: pick('shieldKey'), apiUrl: pick('apiUrl') };
97
- }
98
- }
99
- catch { /* ignore */ }
100
- return {};
101
- }
102
81
  function inferEvent(explicit, payload) {
103
82
  const e = (explicit || '').toLowerCase();
104
83
  if (e) {
@@ -234,11 +213,14 @@ async function hookCommand(args, config) {
234
213
  const shadow = args.shadow === 'true';
235
214
  const failClosed = args.failClosed === 'true';
236
215
  const timeoutMs = Number(args.timeout) > 0 ? Number(args.timeout) : 8000;
237
- const home = loadHomeShield();
238
- const shieldId = args.shieldId || process.env.FULLCOURTDEFENSE_SHIELD_ID || config.shieldId || home.shieldId;
239
- const shieldKey = args.shieldKey || process.env.FULLCOURTDEFENSE_SHIELD_KEY || config.shieldKey || home.shieldKey;
240
- const apiUrl = (args.apiUrl || process.env.FULLCOURTDEFENSE_API_URL || config.apiUrl || home.apiUrl || DEFAULT_API_URL)
241
- .replace(/\/$/, '');
216
+ const creds = (0, config_1.resolveCliCredentials)(config, {
217
+ shieldId: args.shieldId,
218
+ shieldKey: args.shieldKey,
219
+ apiUrl: args.apiUrl,
220
+ });
221
+ const shieldId = creds.shieldId;
222
+ const shieldKey = creds.shieldKey;
223
+ const apiUrl = creds.apiUrl;
242
224
  let raw = '';
243
225
  let stdinErr = '';
244
226
  const isTTY = !!process.stdin.isTTY;
@@ -0,0 +1,11 @@
1
+ import { BotGuardConfig } from '../config';
2
+ import { InstallMcpGatewayArgs } from './mcpGateway';
3
+ export interface InstallAllArgs extends InstallMcpGatewayArgs {
4
+ hooks?: string;
5
+ discover?: string;
6
+ }
7
+ /**
8
+ * One-click install: MCP gateway into every supported AI client, optional Cursor hooks,
9
+ * optional fleet upload. Use per-client commands when you only need one tool.
10
+ */
11
+ export declare function installAllCommand(args: InstallAllArgs, config: BotGuardConfig): Promise<void>;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installAllCommand = installAllCommand;
4
+ const config_1 = require("../config");
5
+ const discover_1 = require("./discover");
6
+ const installCursorHook_1 = require("./installCursorHook");
7
+ const mcpGateway_1 = require("./mcpGateway");
8
+ /**
9
+ * One-click install: MCP gateway into every supported AI client, optional Cursor hooks,
10
+ * optional fleet upload. Use per-client commands when you only need one tool.
11
+ */
12
+ async function installAllCommand(args, config) {
13
+ const creds = (0, config_1.requireCliSetup)(config, {
14
+ apiKey: args.apiKey,
15
+ shieldId: args.shieldId,
16
+ shieldKey: args.shieldKey,
17
+ apiUrl: args.apiUrl,
18
+ }, { requireOrganizationId: false });
19
+ const gatewayArgs = {
20
+ ...args,
21
+ ...creds,
22
+ clients: args.clients || 'all',
23
+ };
24
+ console.log('\n\x1b[1m\x1b[36mFullCourtDefense — install all AI clients\x1b[0m');
25
+ console.log('\x1b[2mGateway + optional Cursor hooks + optional fleet upload\x1b[0m\n');
26
+ await (0, mcpGateway_1.installMcpGatewayCommand)(gatewayArgs, config);
27
+ if (args.hooks !== 'false') {
28
+ console.log('\n\x1b[1mInstalling Cursor runtime hooks…\x1b[0m');
29
+ const hookArgs = {
30
+ shieldId: creds.shieldId,
31
+ shieldKey: creds.shieldKey,
32
+ apiUrl: creds.apiUrl,
33
+ project: args.cursorProject,
34
+ };
35
+ try {
36
+ await (0, installCursorHook_1.installCursorHookCommand)(hookArgs, config);
37
+ }
38
+ catch (error) {
39
+ console.log(`Cursor hooks skipped: ${error instanceof Error ? error.message : String(error)}`);
40
+ }
41
+ }
42
+ if (args.discover === 'true' || args.upload === 'true') {
43
+ console.log('\n\x1b[1mUploading desktop posture to AI Fleet…\x1b[0m');
44
+ const discoverArgs = {
45
+ surface: 'all',
46
+ upload: 'true',
47
+ apiKey: creds.apiKey,
48
+ apiUrl: creds.apiUrl,
49
+ userEmail: args.developerName,
50
+ silent: args.upload === 'true' && args.discover !== 'true' ? 'true' : undefined,
51
+ };
52
+ await (0, discover_1.discoverCommand)(discoverArgs, config);
53
+ }
54
+ console.log('\n\x1b[32mDone.\x1b[0m Restart each AI client you use, then run \x1b[1mfullcourtdefense discover --surface all\x1b[0m to verify.');
55
+ }
@@ -38,6 +38,7 @@ exports.uninstallCursorHookCommand = uninstallCursorHookCommand;
38
38
  const fs = __importStar(require("fs"));
39
39
  const os = __importStar(require("os"));
40
40
  const path = __importStar(require("path"));
41
+ const config_1 = require("../config");
41
42
  const COLOR = {
42
43
  reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
43
44
  red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', cyan: '\x1b[36m', gray: '\x1b[90m',
@@ -90,28 +91,15 @@ function isManaged(entry) {
90
91
  return typeof entry?.command === 'string'
91
92
  && (entry.command.includes(MANAGED_MARKER) || entry.command.includes(MANAGED_TAG));
92
93
  }
93
- /** Save the home-level Shield credentials so the hook can find them machine-wide. */
94
- function writeHomeShield(input) {
94
+ /** Ensure Shield creds are in ~/.fullcourtdefense.yml for runtime hooks (no-op if already saved via setup). */
95
+ function ensureHomeShield(input) {
95
96
  if (!input.shieldId && !input.shieldKey && !input.apiUrl)
96
97
  return undefined;
97
- const target = path.join(os.homedir(), '.fullcourtdefense.yml');
98
- const existing = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : '# FullCourtDefense CLI Configuration\n';
99
- const lines = existing.split(/\r?\n/);
100
- const setKey = (key, value) => {
101
- if (!value)
102
- return;
103
- const idx = lines.findIndex((l) => new RegExp(`^\\s*${key}\\s*:`).test(l));
104
- const next = `${key}: ${value}`;
105
- if (idx >= 0)
106
- lines[idx] = next;
107
- else
108
- lines.push(next);
109
- };
110
- setKey('shieldId', input.shieldId);
111
- setKey('shieldKey', input.shieldKey);
112
- setKey('apiUrl', input.apiUrl);
113
- fs.writeFileSync(target, lines.filter((l, i, a) => !(l === '' && a[i - 1] === '')).join('\n').trim() + '\n', 'utf8');
114
- return target;
98
+ return (0, config_1.saveSetupConfig)({
99
+ shieldId: input.shieldId,
100
+ shieldKey: input.shieldKey,
101
+ apiUrl: input.apiUrl,
102
+ });
115
103
  }
116
104
  async function installCursorHookCommand(args, config) {
117
105
  const projectScope = args.project === 'true';
@@ -124,11 +112,12 @@ async function installCursorHookCommand(args, config) {
124
112
  console.error(`${COLOR.red}No valid events. Choose from: ${Object.keys(EVENT_MAP).join(', ')}${COLOR.reset}`);
125
113
  process.exit(1);
126
114
  }
127
- // Persist creds machine-wide (home) so the runtime hook resolves them.
128
- const shieldId = args.shieldId || config.shieldId;
129
- const shieldKey = args.shieldKey || config.shieldKey;
130
- const apiUrl = args.apiUrl || config.apiUrl;
131
- const credFile = writeHomeShield({ shieldId, shieldKey, apiUrl });
115
+ const creds = (0, config_1.resolveCliCredentials)(config, {
116
+ shieldId: args.shieldId,
117
+ shieldKey: args.shieldKey,
118
+ apiUrl: args.apiUrl,
119
+ });
120
+ const credFile = ensureHomeShield(creds);
132
121
  const json = readHooksJson(file);
133
122
  for (const e of events) {
134
123
  const { hookKey, flag, failClosedDefault } = EVENT_MAP[e];
@@ -155,9 +144,9 @@ async function installCursorHookCommand(args, config) {
155
144
  console.log(`${COLOR.yellow}Mode: SHADOW (monitor only — nothing is blocked).${COLOR.reset}`);
156
145
  if (credFile)
157
146
  console.log(`${COLOR.gray}Creds:${COLOR.reset} ${credFile}`);
158
- if (!shieldId) {
147
+ if (!creds.shieldId) {
159
148
  console.log('');
160
- console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense configure${COLOR.reset} or pass ${COLOR.bold}--shield-id${COLOR.reset}, then re-run install.`);
149
+ console.log(`${COLOR.yellow}No Shield ID set.${COLOR.reset} Run ${COLOR.bold}fullcourtdefense setup${COLOR.reset} first, then re-run install.`);
161
150
  }
162
151
  console.log('');
163
152
  console.log(`${COLOR.gray}Restart Cursor (or it will hot-reload hooks.json). Verify in Cursor → Settings → Hooks.${COLOR.reset}`);
@@ -1,4 +1,7 @@
1
1
  import { BotGuardConfig } from '../config';
2
+ type AgentClient = 'cursor' | 'claude-code' | 'claude-desktop' | 'codex' | 'gemini-cli' | 'windsurf' | 'vscode';
3
+ export type GatewayClient = AgentClient;
4
+ export declare const ALL_GATEWAY_CLIENTS: GatewayClient[];
2
5
  export interface McpGatewayArgs {
3
6
  mcpCommand?: string;
4
7
  mcpArgs?: string;
@@ -29,6 +32,21 @@ export interface InstallClaudeDesktopMcpGatewayArgs extends McpGatewayArgs {
29
32
  configPath?: string;
30
33
  serverName?: string;
31
34
  }
35
+ export interface InstallVscodeMcpGatewayArgs extends McpGatewayArgs {
36
+ project?: string;
37
+ serverName?: string;
38
+ }
39
+ export interface InstallCodexMcpGatewayArgs extends McpGatewayArgs {
40
+ project?: string;
41
+ serverName?: string;
42
+ }
43
+ export interface InstallGeminiMcpGatewayArgs extends McpGatewayArgs {
44
+ project?: string;
45
+ serverName?: string;
46
+ }
47
+ export interface InstallWindsurfMcpGatewayArgs extends McpGatewayArgs {
48
+ serverName?: string;
49
+ }
32
50
  export interface InstallMcpGatewayArgs extends McpGatewayArgs {
33
51
  clients?: string;
34
52
  cursorProject?: string;
@@ -42,3 +60,8 @@ export declare function installCursorMcpGatewayCommand(args: InstallCursorMcpGat
42
60
  export declare function installMcpGatewayCommand(args: InstallMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
43
61
  export declare function installClaudeCodeMcpGatewayCommand(args: InstallClaudeCodeMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
44
62
  export declare function installClaudeDesktopMcpGatewayCommand(args: InstallClaudeDesktopMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
63
+ export declare function installCodexMcpGatewayCommand(args: InstallCodexMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
64
+ export declare function installGeminiMcpGatewayCommand(args: InstallGeminiMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
65
+ export declare function installWindsurfMcpGatewayCommand(args: InstallWindsurfMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
66
+ export declare function installVscodeMcpGatewayCommand(args: InstallVscodeMcpGatewayArgs, config: BotGuardConfig): Promise<void>;
67
+ export {};