fullcourtdefense-cli 1.7.2 → 1.7.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.
@@ -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 org API key path; otherwise fall back to the Shield-key endpoint so
509
- // `discover --upload` works straight from `login` credentials with no org key.
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.apiKey && !!(auth.shieldId && auth.shieldKey);
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) {
@@ -668,11 +693,11 @@ async function discoverCommand(args, config) {
668
693
  const hasShieldCreds = !!(creds.shieldId && creds.shieldKey);
669
694
  if (!creds.apiKey && !hasShieldCreds) {
670
695
  if (!silent) {
671
- console.error(`\n${COLOR.red}--upload needs credentials.${COLOR.reset} Run \`fullcourtdefense login --token <fleet-token>\` (or \`fullcourtdefense setup\`) first.`);
696
+ console.error(`\n${COLOR.red}--upload needs credentials.${COLOR.reset} Run \`fullcourtdefense login --token <fleet-enrollment-token>\` first.`);
672
697
  }
673
698
  process.exit(1);
674
699
  }
675
- await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey }, args.connectorName, uploadExtras);
700
+ await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey, preferApiKey: !!args.apiKey }, args.connectorName, uploadExtras);
676
701
  }
677
702
  if (silent) {
678
703
  if (args.upload === 'true') {
@@ -714,7 +739,7 @@ async function discoverCommand(args, config) {
714
739
  found.sort((a, b) => order[a.riskLevel] - order[b.riskLevel]);
715
740
  for (const s of found) {
716
741
  const col = riskColor(s.riskLevel);
717
- const target = s.command ? `${s.command} ${(s.args || []).join(' ')}`.trim() : (s.url || 'unknown');
742
+ const target = s.command ? `${s.command} ${redactCommandArgs(s.args).join(' ')}`.trim() : (s.url || 'unknown');
718
743
  console.log(` ${col}[${s.riskLevel.toUpperCase()}]${COLOR.reset} ${s.serverName} — ${target} (${proxyStatusLabel(s.proxyStatus)})`);
719
744
  }
720
745
  console.log('');
@@ -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 setup`).');
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 setup${COLOR.reset} first, then re-run install.`);
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 setup` once (saves org, API key, Shield ID, and Shield key to ~/.fullcourtdefense.yml).');
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 setup` writes
181
- // ~/.fullcourtdefense.yml, then `install-all` should use that same verified
182
- // Shield from any repo. Project config is still supported for scan defaults
183
- // and explicit `--config` workflows, but it must not silently override the
184
- // user's one-time setup credentials.
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 setup\` once — it saves org, API key, Shield ID, and Shield key to ~/.fullcourtdefense.yml so you never pass them again.`);
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
- configure Saves org API key, Organization ID, Shield ID, and Shield key to ~/.fullcourtdefense.yml.
116
- After setup, other commands read credentials automatically — no --shield-id/--api-key needed.
117
- setup Same as configure validates credentials against the server before saving.
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.
@@ -167,21 +169,23 @@ function printHelp() {
167
169
  init Creates a starter .fullcourtdefense.yml config file.
168
170
 
169
171
  \x1b[1mNew User Process:\x1b[0m
170
- 1. In the web app, create a Shield.
171
- Copy the Shield ID and Shield key from the Shield Integrate tab.
172
+ 1. In the web app, an org admin creates a fleet enrollment token:
173
+ AI Fleet Settings Fleet enrollment token.
172
174
 
173
175
  2. Check outbound connectivity from the customer machine:
174
176
  fullcourtdefense doctor
175
177
  This confirms the machine can reach https://api.fullcourtdefense.ai over HTTPS.
176
178
 
177
- 3. Save org + Shield credentials locally (one time):
178
- fullcourtdefense setup
179
- Saves to ~/.fullcourtdefense.yml — install, discover --upload, scan, gateway, and hooks use it automatically.
179
+ 3. Enroll this developer machine (one time):
180
+ fullcourtdefense login --token <fleet-enrollment-token>
181
+ Or set FCD_ENROLL_TOKEN and run: fullcourtdefense login
182
+ Saves per-machine Shield credentials to ~/.fullcourtdefense.yml —
183
+ install, discover --upload, scan, gateway, and hooks use it automatically.
180
184
 
181
185
  4. One-click protect every AI client on this machine with Action Policies:
182
186
  fullcourtdefense install-all
183
187
  Verify installs:
184
- fullcourtdefense discover --surface mcp
188
+ fullcourtdefense protect-all --dry-run true
185
189
  Or install one client: fullcourtdefense install-cursor-mcp-gateway ...
186
190
 
187
191
  5. Run a guided local scan:
@@ -298,7 +302,7 @@ function printHelp() {
298
302
  $ fullcourtdefense install-claude-code-mcp-gateway --scope project --mcp-command npm --mcp-args "run mcp"
299
303
  $ fullcourtdefense install-claude-desktop-mcp-gateway --mcp-command npm --mcp-args "run mcp"
300
304
  $ fullcourtdefense mcp-gateway --mcp-command npm --mcp-args "run mcp"
301
- $ fullcourtdefense setup
305
+ $ fullcourtdefense login --token <fleet-enrollment-token>
302
306
  $ fullcourtdefense install-all
303
307
  $ fullcourtdefense install-all --auto-protect true --upload
304
308
  $ fullcourtdefense install-mcp-gateway --clients cursor,codex --mcp-command npm --mcp-args "run mcp"
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.7.2"
2
+ "version": "1.7.3"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.7.2",
3
+ "version": "1.7.3",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {