fullcourtdefense-cli 1.3.3 → 1.3.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.
@@ -6,5 +6,7 @@ export interface ConfigureArgs {
6
6
  shieldKey?: string;
7
7
  apiUrl?: string;
8
8
  skipValidate?: boolean;
9
+ /** When true, use only flags/env/config — no prompts (for scripts). */
10
+ nonInteractive?: boolean;
9
11
  }
10
12
  export declare function configureCommand(args: ConfigureArgs, config: BotGuardConfig): Promise<void>;
@@ -43,63 +43,107 @@ async function ask(rl, question, fallback) {
43
43
  const answer = (await rl.question(`${question}${suffix}: `)).trim();
44
44
  return answer || fallback || '';
45
45
  }
46
+ async function confirm(rl, question, defaultYes = true) {
47
+ const hint = defaultYes ? 'Y/n' : 'y/N';
48
+ const answer = (await rl.question(`${question} (${hint}): `)).trim().toLowerCase();
49
+ if (!answer)
50
+ return defaultYes;
51
+ return answer === 'y' || answer === 'yes';
52
+ }
46
53
  function resolveApiKey(args, config) {
47
54
  return args.apiKey || config.apiKey || process.env.FULLCOURTDEFENSE_API_KEY || '';
48
55
  }
56
+ function defaultsFrom(args, config) {
57
+ return {
58
+ apiUrl: args.apiUrl || config.apiUrl || 'https://api.fullcourtdefense.ai',
59
+ apiKey: resolveApiKey(args, config),
60
+ organizationId: args.organizationId || config.organizationId || '',
61
+ shieldId: args.shieldId || config.shieldId || '',
62
+ shieldKey: args.shieldKey ?? config.shieldKey ?? '',
63
+ };
64
+ }
65
+ function allFlagsProvided(args) {
66
+ return Boolean(args.apiKey && args.organizationId && args.shieldId && args.apiUrl);
67
+ }
68
+ function isValidationUnavailableError(error) {
69
+ const message = error instanceof Error ? error.message : String(error);
70
+ return message.includes('HTML instead of JSON') || message.includes('404');
71
+ }
49
72
  async function configureCommand(args, config) {
50
- const apiUrl = args.apiUrl || config.apiUrl || 'https://api.fullcourtdefense.ai';
51
- const apiKey = resolveApiKey(args, config);
52
- const organizationId = args.organizationId || config.organizationId;
53
- const shieldId = args.shieldId || config.shieldId;
54
- const shieldKey = args.shieldKey ?? config.shieldKey;
55
- const hasAllRequired = Boolean(apiKey && organizationId && shieldId);
56
- let finalApiUrl = apiUrl;
57
- let finalApiKey = apiKey;
58
- let finalOrgId = organizationId;
59
- let finalShieldId = shieldId;
60
- let finalShieldKey = shieldKey;
61
- if (!hasAllRequired) {
62
- const rl = readline.createInterface({ input: process_1.stdin, output: process_1.stdout });
63
- try {
64
- finalApiUrl = args.apiUrl || await ask(rl, 'FullCourtDefense API URL', config.apiUrl || 'https://api.fullcourtdefense.ai');
65
- finalApiKey = resolveApiKey(args, config) || await ask(rl, 'Organization API key (from Settings → API Keys)', '');
66
- finalOrgId = args.organizationId || await ask(rl, 'Organization ID (from Settings → Organization)', config.organizationId || '');
67
- finalShieldId = args.shieldId || await ask(rl, 'Shield ID', config.shieldId || 'sh_...');
68
- finalShieldKey = args.shieldKey ?? await ask(rl, 'Shield key if locked, blank if not required', config.shieldKey || '');
73
+ const saved = defaultsFrom(args, config);
74
+ const useWizard = !args.nonInteractive && !allFlagsProvided(args);
75
+ let finalApiUrl = saved.apiUrl;
76
+ let finalApiKey = saved.apiKey;
77
+ let finalOrgId = saved.organizationId;
78
+ let finalShieldId = saved.shieldId;
79
+ let finalShieldKey = saved.shieldKey;
80
+ const rl = useWizard ? readline.createInterface({ input: process_1.stdin, output: process_1.stdout }) : null;
81
+ try {
82
+ if (useWizard && rl) {
83
+ console.log('');
84
+ console.log('FullCourtDefense setup — enter each value one at a time.');
85
+ console.log('Press Enter to keep the default shown in parentheses.');
86
+ console.log('Find these in the app: Settings → API Keys / Organization, Shield → Integrate.\n');
87
+ finalApiUrl = await ask(rl, '1/5 API URL', saved.apiUrl);
88
+ finalApiKey = await ask(rl, '2/5 Organization API key', saved.apiKey || undefined);
89
+ finalOrgId = await ask(rl, '3/5 Organization ID', saved.organizationId || undefined);
90
+ finalShieldId = await ask(rl, '4/5 Shield ID (sh_...)', saved.shieldId || undefined);
91
+ finalShieldKey = await ask(rl, '5/5 Shield key (blank if not required)', saved.shieldKey || undefined);
92
+ console.log('');
69
93
  }
70
- finally {
71
- rl.close();
94
+ if (!finalApiKey) {
95
+ throw new Error('Organization API key is required.');
72
96
  }
73
- }
74
- if (!finalApiKey) {
75
- throw new Error('Organization API key is required to validate your setup.');
76
- }
77
- if (!finalOrgId) {
78
- throw new Error('Organization ID is required.');
79
- }
80
- if (!finalShieldId || finalShieldId === 'sh_...') {
81
- throw new Error('Shield ID is required.');
82
- }
83
- if (args.skipValidate !== true) {
84
- console.log('Validating organization, Shield ID, and Shield key against FullCourtDefense...');
85
- const api = new api_1.BotGuardApi(finalApiKey, finalApiUrl);
86
- const validated = await api.validateSetup({
97
+ if (!finalOrgId) {
98
+ throw new Error('Organization ID is required.');
99
+ }
100
+ if (!finalShieldId || finalShieldId === 'sh_...') {
101
+ throw new Error('Shield ID is required.');
102
+ }
103
+ let skipValidate = args.skipValidate === true;
104
+ if (!skipValidate) {
105
+ console.log('Validating organization, Shield ID, and Shield key against FullCourtDefense...');
106
+ const api = new api_1.BotGuardApi(finalApiKey, finalApiUrl);
107
+ try {
108
+ const validated = await api.validateSetup({
109
+ organizationId: finalOrgId,
110
+ shieldId: finalShieldId,
111
+ shieldKey: finalShieldKey || undefined,
112
+ });
113
+ if (validated.organizationId !== finalOrgId) {
114
+ throw new Error(`Organization mismatch: API key belongs to "${validated.organizationName}" (${validated.organizationId}), not "${finalOrgId}".`);
115
+ }
116
+ console.log(`Verified: ${validated.organizationName} → Shield "${validated.shieldName}" (${validated.shieldId})`);
117
+ }
118
+ catch (error) {
119
+ if (isValidationUnavailableError(error)) {
120
+ console.log('Server validation is not available yet (API endpoint not deployed).');
121
+ if (useWizard && rl) {
122
+ skipValidate = await confirm(rl, 'Save credentials locally anyway?', true);
123
+ if (!skipValidate)
124
+ throw error;
125
+ }
126
+ else {
127
+ throw new Error(`${error instanceof Error ? error.message : String(error)} `
128
+ + 'Use `fullcourtdefense setup --skip-validate true` or run interactive `fullcourtdefense setup` to save without validation.');
129
+ }
130
+ }
131
+ else {
132
+ throw error;
133
+ }
134
+ }
135
+ }
136
+ const savedPath = (0, config_1.saveSetupConfig)({
137
+ apiKey: finalApiKey,
87
138
  organizationId: finalOrgId,
88
139
  shieldId: finalShieldId,
89
140
  shieldKey: finalShieldKey || undefined,
141
+ apiUrl: finalApiUrl,
90
142
  });
91
- if (validated.organizationId !== finalOrgId) {
92
- throw new Error(`Organization mismatch: API key belongs to "${validated.organizationName}" (${validated.organizationId}), not "${finalOrgId}".`);
93
- }
94
- console.log(`Verified: ${validated.organizationName} → Shield "${validated.shieldName}" (${validated.shieldId})`);
143
+ console.log(`Saved setup to ${savedPath}`);
144
+ console.log('All other CLI commands (install, discover --upload, scan, hooks) will use these credentials automatically.');
145
+ }
146
+ finally {
147
+ rl?.close();
95
148
  }
96
- const savedPath = (0, config_1.saveSetupConfig)({
97
- apiKey: finalApiKey,
98
- organizationId: finalOrgId,
99
- shieldId: finalShieldId,
100
- shieldKey: finalShieldKey || undefined,
101
- apiUrl: finalApiUrl,
102
- });
103
- console.log(`Saved setup to ${savedPath}`);
104
- console.log('All other CLI commands (install, discover --upload, scan, hooks) will use these credentials automatically.');
105
149
  }
package/dist/index.js CHANGED
@@ -111,7 +111,7 @@ function printHelp() {
111
111
  doctor First step. Checks outbound HTTPS access to FullCourtDefense.
112
112
  configure Saves org API key, Organization ID, Shield ID, and Shield key to ~/.fullcourtdefense.yml.
113
113
  After setup, other commands read credentials automatically — no --shield-id/--api-key needed.
114
- setup Same as configure — validates credentials against the server before saving.
114
+ setup Same as configure — step-by-step wizard; validates before saving.
115
115
  install-all One-click: MCP gateway in every AI client + Cursor hooks + optional upload.
116
116
  install Alias for install-all.
117
117
  scan Runs the scan. Use --local for inside-organization scans.
@@ -154,9 +154,8 @@ function printHelp() {
154
154
  fullcourtdefense doctor
155
155
  This confirms the machine can reach https://api.fullcourtdefense.ai over HTTPS.
156
156
 
157
- 3. Save org + Shield credentials locally (one time):
157
+ 3. Save org + Shield credentials locally (one time, prompts one field at a time):
158
158
  fullcourtdefense setup
159
- Saves to ~/.fullcourtdefense.yml — install, discover --upload, scan, and hooks use it automatically.
160
159
 
161
160
  4. One-click protect every AI client on this machine:
162
161
  fullcourtdefense install-all --mcp-command npm --mcp-args "run mcp" --upload
@@ -409,6 +408,7 @@ async function main() {
409
408
  shieldKey: flags['shield-key'],
410
409
  apiUrl: flags['api-url'],
411
410
  skipValidate: flags['skip-validate'] === 'true',
411
+ nonInteractive: flags['non-interactive'] === 'true',
412
412
  };
413
413
  await (0, configure_1.configureCommand)(args, config);
414
414
  break;
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.3.3"
2
+ "version": "1.3.4"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {