fullcourtdefense-cli 1.15.10 → 1.15.11
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.
- package/dist/commands/daemon.js +34 -3
- package/dist/version.json +1 -1
- package/package.json +1 -1
package/dist/commands/daemon.js
CHANGED
|
@@ -53,6 +53,7 @@ const COLOR = {
|
|
|
53
53
|
red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', cyan: '\x1b[36m', gray: '\x1b[90m',
|
|
54
54
|
};
|
|
55
55
|
const TASK_NAME = 'FullCourtDefense Daemon';
|
|
56
|
+
const WATCHDOG_TASK_NAME = 'FullCourtDefense Daemon Watchdog';
|
|
56
57
|
const CRON_MARKER = '# FCD_DAEMON';
|
|
57
58
|
const LAUNCHD_LABEL = 'ai.fullcourtdefense.daemon';
|
|
58
59
|
const SYSTEMD_UNIT = 'fullcourtdefense-daemon.service';
|
|
@@ -262,6 +263,16 @@ async function runDaemon(args, config) {
|
|
|
262
263
|
console.log(`${COLOR.yellow}Another FullCourtDefense daemon is already running (pid file: ${pidFile()}).${COLOR.reset}`);
|
|
263
264
|
return;
|
|
264
265
|
}
|
|
266
|
+
// The resident loop must never die silently from one bad tick (fs watcher
|
|
267
|
+
// callback, fetch, timer). Log and keep running — the loop is timer-driven,
|
|
268
|
+
// so surviving a failed tick is safe, and on Windows there is no supervisor
|
|
269
|
+
// that would restart a crashed process instantly.
|
|
270
|
+
process.on('uncaughtException', error => {
|
|
271
|
+
log(`Uncaught exception (daemon continues): ${error?.stack || String(error)}`);
|
|
272
|
+
});
|
|
273
|
+
process.on('unhandledRejection', reason => {
|
|
274
|
+
log(`Unhandled rejection (daemon continues): ${reason?.stack || String(reason)}`);
|
|
275
|
+
});
|
|
265
276
|
const creds = (0, config_1.resolveCliCredentials)(config, {
|
|
266
277
|
shieldId: args.shieldId,
|
|
267
278
|
shieldKey: args.shieldKey,
|
|
@@ -696,12 +707,23 @@ function installWindows() {
|
|
|
696
707
|
], { stdio: 'ignore', windowsHide: true });
|
|
697
708
|
ok = reg.status === 0;
|
|
698
709
|
}
|
|
710
|
+
// Watchdog: logon triggers only START the daemon — nothing restarts it if it
|
|
711
|
+
// is killed or crashes mid-session. A time-based per-user task (no elevation
|
|
712
|
+
// needed, unlike ONLOGON) re-launches the daemon every 5 minutes;
|
|
713
|
+
// acquirePidLock() makes each tick a cheap no-op while the daemon is alive,
|
|
714
|
+
// and revives it within one tick when it is not. Best-effort: a missing
|
|
715
|
+
// watchdog never fails the install.
|
|
716
|
+
(0, child_process_1.spawnSync)('schtasks', [
|
|
717
|
+
'/Create', '/TN', WATCHDOG_TASK_NAME, '/TR', `wscript.exe "${vbs}"`,
|
|
718
|
+
'/SC', 'MINUTE', '/MO', '5', '/F',
|
|
719
|
+
], { stdio: 'ignore', windowsHide: true });
|
|
699
720
|
if (ok)
|
|
700
721
|
startDaemonNowWindows(vbs, taskOk);
|
|
701
722
|
return ok;
|
|
702
723
|
}
|
|
703
724
|
function uninstallWindows() {
|
|
704
725
|
const task = (0, child_process_1.spawnSync)('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'ignore', windowsHide: true });
|
|
726
|
+
(0, child_process_1.spawnSync)('schtasks', ['/Delete', '/TN', WATCHDOG_TASK_NAME, '/F'], { stdio: 'ignore', windowsHide: true });
|
|
705
727
|
const reg = (0, child_process_1.spawnSync)('reg', ['delete', WINDOWS_RUN_KEY, '/v', WINDOWS_RUN_VALUE, '/f'], { stdio: 'ignore', windowsHide: true });
|
|
706
728
|
const vbs = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'FullCourtDefense', 'daemon.vbs');
|
|
707
729
|
try {
|
|
@@ -790,11 +812,16 @@ WantedBy=default.target
|
|
|
790
812
|
return true;
|
|
791
813
|
}
|
|
792
814
|
catch { /* fall back to cron */ }
|
|
793
|
-
// Fallback: cron
|
|
815
|
+
// Fallback: cron for boxes without a systemd user session. @reboot starts
|
|
816
|
+
// the daemon at boot; the 5-min line is the watchdog (pid lock makes each
|
|
817
|
+
// tick a no-op while the daemon is alive, and revives it after a kill/crash).
|
|
794
818
|
const command = `"${process.execPath}" "${cliEntry()}" daemon >/dev/null 2>&1`;
|
|
795
819
|
const list = (0, child_process_1.spawnSync)('crontab', ['-l'], { encoding: 'utf8' });
|
|
796
820
|
const existing = (list.status === 0 ? (list.stdout || '') : '').split('\n').filter(l => l && !l.includes(CRON_MARKER));
|
|
797
|
-
const write = (0, child_process_1.spawnSync)('crontab', ['-'], {
|
|
821
|
+
const write = (0, child_process_1.spawnSync)('crontab', ['-'], {
|
|
822
|
+
input: [...existing, `@reboot ${command} ${CRON_MARKER}`, `*/5 * * * * ${command} ${CRON_MARKER}`].join('\n') + '\n',
|
|
823
|
+
encoding: 'utf8',
|
|
824
|
+
});
|
|
798
825
|
return write.status === 0;
|
|
799
826
|
}
|
|
800
827
|
function uninstallLinux() {
|
|
@@ -854,6 +881,10 @@ function statusCommand() {
|
|
|
854
881
|
else {
|
|
855
882
|
console.log(' not installed — run: fullcourtdefense daemon --install true');
|
|
856
883
|
}
|
|
884
|
+
const watchdog = (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', WATCHDOG_TASK_NAME], { stdio: 'ignore', windowsHide: true, timeout: 5_000 });
|
|
885
|
+
console.log(watchdog.status === 0
|
|
886
|
+
? ` Watchdog task "${WATCHDOG_TASK_NAME}" installed — revives a killed daemon within 5 min`
|
|
887
|
+
: ` ${COLOR.yellow}watchdog not installed${COLOR.reset} — a killed daemon stays down until next logon; re-run: fullcourtdefense daemon --install true`);
|
|
857
888
|
}
|
|
858
889
|
else if (process.platform === 'darwin') {
|
|
859
890
|
console.log(fs.existsSync(launchdPlistPath()) ? ` launchd agent installed (${launchdPlistPath()})` : ' not installed');
|
|
@@ -897,7 +928,7 @@ async function daemonCommand(args, config) {
|
|
|
897
928
|
: installLinux();
|
|
898
929
|
if (ok) {
|
|
899
930
|
console.log(`${COLOR.green}${COLOR.bold}Daemon autostart installed.${COLOR.reset}`);
|
|
900
|
-
console.log(`${COLOR.gray}Trigger:${COLOR.reset} ${process.platform === 'win32' ? `at logon (Scheduled Task "${TASK_NAME}")` : process.platform === 'darwin' ? 'launchd (RunAtLoad + KeepAlive)' : 'systemd user unit (or @reboot cron)'}`);
|
|
931
|
+
console.log(`${COLOR.gray}Trigger:${COLOR.reset} ${process.platform === 'win32' ? `at logon (Scheduled Task "${TASK_NAME}") + 5-min watchdog ("${WATCHDOG_TASK_NAME}") that revives a killed daemon` : process.platform === 'darwin' ? 'launchd (RunAtLoad + KeepAlive)' : 'systemd user unit (or @reboot + 5-min watchdog cron)'}`);
|
|
901
932
|
console.log(`${COLOR.gray}Start now:${COLOR.reset} fullcourtdefense daemon`);
|
|
902
933
|
console.log(`${COLOR.gray}Status:${COLOR.reset} fullcourtdefense daemon --status true`);
|
|
903
934
|
console.log(`${COLOR.gray}Remove:${COLOR.reset} fullcourtdefense daemon --uninstall true`);
|
package/dist/version.json
CHANGED