fullcourtdefense-cli 1.7.3 → 1.7.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.
@@ -626,12 +626,19 @@ async function discoverCommand(args, config) {
626
626
  console.log('Removed daily desktop discovery schedule.');
627
627
  return;
628
628
  }
629
- if (args.schedule === 'daily') {
629
+ if (args.schedule === 'daily' || args.schedule === 'logon') {
630
630
  const hour = args.scheduleHour ? Number.parseInt(args.scheduleHour, 10) : 9;
631
- (0, discoverSchedule_1.installDailyDiscoverSchedule)({ userEmail: args.userEmail, hour: Number.isFinite(hour) ? hour : 9 });
631
+ const surface = surfaces.size === 4 ? 'all' : Array.from(surfaces).join(',');
632
+ (0, discoverSchedule_1.installDailyDiscoverSchedule)({
633
+ userEmail: args.userEmail,
634
+ hour: Number.isFinite(hour) ? hour : 9,
635
+ surface,
636
+ trigger: args.schedule,
637
+ });
632
638
  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');
639
+ const when = args.schedule === 'logon' ? 'at logon' : `daily at ${String(Number.isFinite(hour) ? hour : 9).padStart(2, '0')}:00`;
640
+ console.log(`Desktop discovery scheduled ${when} (runs discover --surface ${surface} --upload --silent${args.userEmail ? ` --user-email ${args.userEmail}` : ''}).`);
641
+ console.log('Uses login Shield credentials or an explicit org API key.');
635
642
  }
636
643
  return;
637
644
  }
@@ -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.4"
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.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": {