fullcourtdefense-cli 1.7.3 → 1.7.5

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.
@@ -511,6 +511,7 @@ async function upload(servers, host, clientCoverage, apiUrl, auth, connectorName
511
511
  } : undefined,
512
512
  secrets: extras?.secrets?.findings.slice(0, 100).map(sanitizeSecretForUpload),
513
513
  agentFiles: extras?.agentFiles?.slice(0, 100).map(sanitizeAgentFileForUpload),
514
+ scanScope: extras?.scanScope,
514
515
  servers: servers.map(s => ({
515
516
  serverName: s.serverName,
516
517
  command: s.command,
@@ -626,12 +627,19 @@ async function discoverCommand(args, config) {
626
627
  console.log('Removed daily desktop discovery schedule.');
627
628
  return;
628
629
  }
629
- if (args.schedule === 'daily') {
630
+ if (args.schedule === 'daily' || args.schedule === 'logon') {
630
631
  const hour = args.scheduleHour ? Number.parseInt(args.scheduleHour, 10) : 9;
631
- (0, discoverSchedule_1.installDailyDiscoverSchedule)({ userEmail: args.userEmail, hour: Number.isFinite(hour) ? hour : 9 });
632
+ const surface = surfaces.size === 4 ? 'all' : Array.from(surfaces).join(',');
633
+ (0, discoverSchedule_1.installDailyDiscoverSchedule)({
634
+ userEmail: args.userEmail,
635
+ hour: Number.isFinite(hour) ? hour : 9,
636
+ surface,
637
+ trigger: args.schedule,
638
+ });
632
639
  if (!silent) {
633
- console.log(`Daily desktop discovery scheduled (runs discover --upload --silent${args.userEmail ? ` --user-email ${args.userEmail}` : ''}).`);
634
- console.log('Requires FULLCOURTDEFENSE_API_KEY or apiKey in ~/.fullcourtdefense.yml');
640
+ const when = args.schedule === 'logon' ? 'at logon' : `daily at ${String(Number.isFinite(hour) ? hour : 9).padStart(2, '0')}:00`;
641
+ console.log(`Desktop discovery scheduled ${when} (runs discover --surface ${surface} --upload --silent${args.userEmail ? ` --user-email ${args.userEmail}` : ''}).`);
642
+ console.log('Uses login Shield credentials or an explicit org API key.');
635
643
  }
636
644
  return;
637
645
  }
@@ -684,7 +692,30 @@ async function discoverCommand(args, config) {
684
692
  }, null, 2));
685
693
  return;
686
694
  }
687
- const uploadExtras = { secrets, agentFiles, posture };
695
+ const home = os.homedir();
696
+ const uploadExtras = {
697
+ secrets,
698
+ agentFiles,
699
+ posture,
700
+ scanScope: {
701
+ surfaces: [...surfaces],
702
+ workingDirectory: cwd,
703
+ mcpConfigPaths: scanned.map(s => ({ ...s, exists: fs.existsSync(s.path) })),
704
+ included: [
705
+ `Current command folder: ${cwd}`,
706
+ 'Known MCP/AI client config files for Cursor, Claude, Codex, Gemini, Windsurf, and VS Code',
707
+ `User-level AI rules, skills, hooks, and instruction files under ${home}`,
708
+ `Credential stores and shell history under ${home}`,
709
+ `Environment files under ${cwd}, ${path.join(home, 'dev')}, ${path.join(home, 'repos')}, ${path.join(home, 'projects')}, ${path.join(home, 'Documents')}, and ${path.join(home, 'code')}`,
710
+ ],
711
+ excluded: [
712
+ 'Unrelated project folders outside the listed roots',
713
+ 'Large/generated dependency and build folders such as node_modules, .git, dist, build, vendor, caches, and temp folders',
714
+ 'Full disk contents, browser profiles, email/chat archives, binary files, and operating-system folders',
715
+ ],
716
+ note: 'Desktop posture is a targeted AI-development exposure scan, not a full-device forensic scan. Run the command from the project folder you want represented.',
717
+ },
718
+ };
688
719
  async function maybeUpload() {
689
720
  if (args.upload !== 'true')
690
721
  return;
@@ -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
- function discoverCommandLine(userEmail) {
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
- (0, child_process_1.execSync)(`schtasks /Create /F /TN "${TASK_NAME}" /SC DAILY /ST ${st} /TR "${tr.replace(/"/g, '\\"')}"`, { stdio: 'inherit' });
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, '&amp;').replace(/</g, '&lt;')}</string>`).join('')}</array>
77
- <key>StartCalendarInterval</key>
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 line = `0 ${hour} * * * ${discoverCommandLine(userEmail)} >> ${path.join(os.homedir(), '.fullcourtdefense-discover.log')} 2>&1`;
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
- (0, child_process_1.execSync)(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'inherit' });
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/index.js CHANGED
@@ -124,6 +124,7 @@ function printHelp() {
124
124
  scan Runs the scan. Use --local for inside-organization scans.
125
125
  discover Finds MCP servers, local secrets, agent rules/skills, and machine
126
126
  blast radius on this laptop. Use --surface all --upload for AI Fleet.
127
+ Use --schedule logon to upload inventory/posture at machine login.
127
128
  install-cursor-hook
128
129
  Installs a Cursor hook so EVERY agent action on this machine is
129
130
  checked against your org's Action Policies — in any repo/folder.
@@ -305,6 +306,7 @@ function printHelp() {
305
306
  $ fullcourtdefense login --token <fleet-enrollment-token>
306
307
  $ fullcourtdefense install-all
307
308
  $ fullcourtdefense install-all --auto-protect true --upload
309
+ $ fullcourtdefense discover --surface all --upload --schedule logon
308
310
  $ fullcourtdefense install-mcp-gateway --clients cursor,codex --mcp-command npm --mcp-args "run mcp"
309
311
  $ fullcourtdefense install-codex-mcp-gateway --mcp-command npm --mcp-args "run mcp"
310
312
  $ fullcourtdefense scan --system-prompt ./prompts/system.md --fail-threshold 80
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.7.3"
2
+ "version": "1.7.5"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {