fullcourtdefense-cli 1.22.1 → 1.22.2
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/autoProtect.js +1 -1
- package/dist/commands/daemon.d.ts +24 -10
- package/dist/commands/daemon.js +95 -43
- package/dist/commands/discoverSchedule.js +34 -20
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -76,7 +76,7 @@ function installWindows(intervalMinutes, onLogon) {
|
|
|
76
76
|
const schedArgs = onLogon
|
|
77
77
|
? ['/SC', 'ONLOGON', '/RL', 'LIMITED']
|
|
78
78
|
: ['/SC', 'MINUTE', '/MO', String(Math.max(1, intervalMinutes)), '/RL', 'LIMITED'];
|
|
79
|
-
return (0, daemon_1.registerHiddenTask)(TASK_NAME, (0, daemon_1.
|
|
79
|
+
return (0, daemon_1.registerHiddenTask)(TASK_NAME, (0, daemon_1.buildTaskXmlVariants)('protect-all', trigger), 'protect-all', schedArgs);
|
|
80
80
|
}
|
|
81
81
|
function uninstallWindows() {
|
|
82
82
|
const result = (0, child_process_1.spawnSync)('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'inherit' });
|
|
@@ -32,6 +32,7 @@ export declare function discoverSweepCredentialEnv(credentials?: {
|
|
|
32
32
|
shieldKey?: string;
|
|
33
33
|
apiUrl?: string;
|
|
34
34
|
}): Record<string, string>;
|
|
35
|
+
export type TaskLogonType = 'S4U' | 'InteractiveToken';
|
|
35
36
|
/** Fully-qualified current user (DOMAIN\\user) for the task principal. */
|
|
36
37
|
export declare function taskUserId(): string;
|
|
37
38
|
/** Local wall-clock timestamp (no ms, no tz) as Task Scheduler expects. */
|
|
@@ -39,26 +40,39 @@ export declare function taskLocalTimestamp(date: Date): string;
|
|
|
39
40
|
/**
|
|
40
41
|
* Task Scheduler XML that runs `node.exe <cliEntry> <command>` hidden.
|
|
41
42
|
*
|
|
42
|
-
* @param command
|
|
43
|
-
* @param triggers
|
|
44
|
-
*
|
|
43
|
+
* @param command CLI subcommand + args ('daemon' | 'watchdog' | 'discover …').
|
|
44
|
+
* @param triggers Inner <Triggers> XML — logon for the daemon, a repeating
|
|
45
|
+
* time trigger for the 5-minute watchdog.
|
|
46
|
+
* @param logonType S4U (windowless, needs elevation to register) or
|
|
47
|
+
* InteractiveToken (registers unelevated, may flash).
|
|
45
48
|
*/
|
|
46
|
-
export declare function buildTaskXml(command: string, triggers: string): string;
|
|
49
|
+
export declare function buildTaskXml(command: string, triggers: string, logonType?: TaskLogonType): string;
|
|
47
50
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
51
|
+
* Windowless-first XML variants for one task: S4U (no window, needs elevation
|
|
52
|
+
* to register) first, InteractiveToken (unelevated fallback) second.
|
|
53
|
+
* registerHiddenTask() tries them in order.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildTaskXmlVariants(command: string, triggers: string): string[];
|
|
56
|
+
/**
|
|
57
|
+
* Register a scheduled task from XML (node run directly). Tries each XML
|
|
58
|
+
* variant in order — S4U (windowless) first, then InteractiveToken — and
|
|
59
|
+
* finally falls back to a plain `/TR node …` task. The fallbacks may briefly
|
|
60
|
+
* flash a console window but still contain NO script host, so the malware
|
|
61
|
+
* fingerprint never returns. Returns true if any path succeeds.
|
|
52
62
|
*
|
|
53
63
|
* @param taskName Scheduled-task name.
|
|
54
|
-
* @param xml
|
|
64
|
+
* @param xml Task Scheduler XML variant(s), tried in order.
|
|
55
65
|
* @param command CLI subcommand for the /TR fallback ('daemon'|'watchdog').
|
|
56
66
|
* @param scheduleArgs schtasks schedule flags for the fallback (e.g. ['/SC','MINUTE','/MO','5']).
|
|
57
67
|
*/
|
|
58
|
-
export declare function registerHiddenTask(taskName: string, xml: string, command: string, scheduleArgs: string[]): boolean;
|
|
68
|
+
export declare function registerHiddenTask(taskName: string, xml: string | string[], command: string, scheduleArgs: string[]): boolean;
|
|
59
69
|
/** True when an existing scheduled task's action still runs wscript/cscript
|
|
60
70
|
* (the pre-1.22.1 VBS launcher) — the migration trigger. */
|
|
61
71
|
export declare function windowsTaskReferencesScriptHost(taskName: string): boolean;
|
|
72
|
+
/** True when an existing task still runs with an InteractiveToken principal —
|
|
73
|
+
* the 1.22.1 window-flash bug (console window on every trigger). Migration
|
|
74
|
+
* trigger for the windowless S4U principal. */
|
|
75
|
+
export declare function windowsTaskRunsInteractive(taskName: string): boolean;
|
|
62
76
|
/** Snapshot of the resident daemon for out-of-process callers (watchdog/status). */
|
|
63
77
|
export interface DaemonRuntimeState {
|
|
64
78
|
alive: boolean;
|
package/dist/commands/daemon.js
CHANGED
|
@@ -39,8 +39,10 @@ exports.discoverSweepCredentialEnv = discoverSweepCredentialEnv;
|
|
|
39
39
|
exports.taskUserId = taskUserId;
|
|
40
40
|
exports.taskLocalTimestamp = taskLocalTimestamp;
|
|
41
41
|
exports.buildTaskXml = buildTaskXml;
|
|
42
|
+
exports.buildTaskXmlVariants = buildTaskXmlVariants;
|
|
42
43
|
exports.registerHiddenTask = registerHiddenTask;
|
|
43
44
|
exports.windowsTaskReferencesScriptHost = windowsTaskReferencesScriptHost;
|
|
45
|
+
exports.windowsTaskRunsInteractive = windowsTaskRunsInteractive;
|
|
44
46
|
exports.daemonRuntimeState = daemonRuntimeState;
|
|
45
47
|
exports.spawnDetachedDaemon = spawnDetachedDaemon;
|
|
46
48
|
exports.pidIsAlive = pidIsAlive;
|
|
@@ -1187,23 +1189,6 @@ async function runDaemon(args, config) {
|
|
|
1187
1189
|
// Keep the process alive forever (timers alone would do it, but be explicit).
|
|
1188
1190
|
await new Promise(() => { });
|
|
1189
1191
|
}
|
|
1190
|
-
// ---------------------------------------------------------------------------
|
|
1191
|
-
// Autostart install / uninstall / status
|
|
1192
|
-
// ---------------------------------------------------------------------------
|
|
1193
|
-
// ---------------------------------------------------------------------------
|
|
1194
|
-
// Windows autostart launchers
|
|
1195
|
-
//
|
|
1196
|
-
// HISTORY: we used to run `wscript.exe <script>.vbs` from the scheduled tasks
|
|
1197
|
-
// to launch node with no console-window flash. Windows Defender's ML heuristic
|
|
1198
|
-
// (`Trojan:Win32/Commando.A!ml`) flags "schtasks task -> wscript runs a script
|
|
1199
|
-
// from a user-writable dir on a short recurring interval" as script-based
|
|
1200
|
-
// persistence — a textbook malware pattern — and quarantined our own watchdog.
|
|
1201
|
-
//
|
|
1202
|
-
// FIX: no VBS, no wscript, no PowerShell anywhere in the autostart chain. The
|
|
1203
|
-
// scheduled tasks run the bundled `node.exe` DIRECTLY, and the console window
|
|
1204
|
-
// is suppressed the supported way — Task Scheduler XML `<Hidden>true</Hidden>`
|
|
1205
|
-
// with an InteractiveToken principal (needs no password, no elevation).
|
|
1206
|
-
// ---------------------------------------------------------------------------
|
|
1207
1192
|
/** Fully-qualified current user (DOMAIN\\user) for the task principal. */
|
|
1208
1193
|
function taskUserId() {
|
|
1209
1194
|
const domain = process.env.USERDOMAIN;
|
|
@@ -1231,18 +1216,20 @@ function taskLocalTimestamp(date) {
|
|
|
1231
1216
|
/**
|
|
1232
1217
|
* Task Scheduler XML that runs `node.exe <cliEntry> <command>` hidden.
|
|
1233
1218
|
*
|
|
1234
|
-
* @param command
|
|
1235
|
-
* @param triggers
|
|
1236
|
-
*
|
|
1219
|
+
* @param command CLI subcommand + args ('daemon' | 'watchdog' | 'discover …').
|
|
1220
|
+
* @param triggers Inner <Triggers> XML — logon for the daemon, a repeating
|
|
1221
|
+
* time trigger for the 5-minute watchdog.
|
|
1222
|
+
* @param logonType S4U (windowless, needs elevation to register) or
|
|
1223
|
+
* InteractiveToken (registers unelevated, may flash).
|
|
1237
1224
|
*/
|
|
1238
|
-
function buildTaskXml(command, triggers) {
|
|
1225
|
+
function buildTaskXml(command, triggers, logonType = 'S4U') {
|
|
1239
1226
|
const userId = xmlEscape(taskUserId());
|
|
1240
1227
|
const nodeExe = xmlEscape(process.execPath);
|
|
1241
1228
|
const args = xmlEscape(`"${cliEntry()}" ${command}`);
|
|
1242
1229
|
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
1243
1230
|
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
1244
1231
|
<RegistrationInfo>
|
|
1245
|
-
<Description>FullCourtDefense ${command} — runs node directly,
|
|
1232
|
+
<Description>FullCourtDefense ${xmlEscape(command)} — runs node directly, windowless, no script host.</Description>
|
|
1246
1233
|
</RegistrationInfo>
|
|
1247
1234
|
<Triggers>
|
|
1248
1235
|
${triggers}
|
|
@@ -1250,7 +1237,7 @@ ${triggers}
|
|
|
1250
1237
|
<Principals>
|
|
1251
1238
|
<Principal id="Author">
|
|
1252
1239
|
<UserId>${userId}</UserId>
|
|
1253
|
-
<LogonType
|
|
1240
|
+
<LogonType>${logonType}</LogonType>
|
|
1254
1241
|
<RunLevel>LeastPrivilege</RunLevel>
|
|
1255
1242
|
</Principal>
|
|
1256
1243
|
</Principals>
|
|
@@ -1279,13 +1266,23 @@ ${triggers}
|
|
|
1279
1266
|
</Task>
|
|
1280
1267
|
`;
|
|
1281
1268
|
}
|
|
1282
|
-
/**
|
|
1283
|
-
|
|
1284
|
-
|
|
1269
|
+
/**
|
|
1270
|
+
* Windowless-first XML variants for one task: S4U (no window, needs elevation
|
|
1271
|
+
* to register) first, InteractiveToken (unelevated fallback) second.
|
|
1272
|
+
* registerHiddenTask() tries them in order.
|
|
1273
|
+
*/
|
|
1274
|
+
function buildTaskXmlVariants(command, triggers) {
|
|
1275
|
+
return [buildTaskXml(command, triggers, 'S4U'), buildTaskXml(command, triggers, 'InteractiveToken')];
|
|
1276
|
+
}
|
|
1277
|
+
function daemonTaskTrigger() {
|
|
1278
|
+
return ` <LogonTrigger>
|
|
1285
1279
|
<Enabled>true</Enabled>
|
|
1286
1280
|
<UserId>${xmlEscape(taskUserId())}</UserId>
|
|
1287
1281
|
</LogonTrigger>`;
|
|
1288
|
-
|
|
1282
|
+
}
|
|
1283
|
+
/** Daemon task variants: start at logon (node run windowless, no repetition). */
|
|
1284
|
+
function buildDaemonTaskXml() {
|
|
1285
|
+
return buildTaskXmlVariants('daemon', daemonTaskTrigger());
|
|
1289
1286
|
}
|
|
1290
1287
|
/**
|
|
1291
1288
|
* Watchdog task: run the `watchdog` command every 5 minutes (liveness beacon +
|
|
@@ -1303,16 +1300,17 @@ function buildWatchdogTaskXml() {
|
|
|
1303
1300
|
<StopAtDurationEnd>false</StopAtDurationEnd>
|
|
1304
1301
|
</Repetition>
|
|
1305
1302
|
</TimeTrigger>`;
|
|
1306
|
-
return
|
|
1303
|
+
return buildTaskXmlVariants('watchdog', trigger);
|
|
1307
1304
|
}
|
|
1308
1305
|
/**
|
|
1309
|
-
* Register a scheduled task from XML (node run directly
|
|
1310
|
-
*
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1306
|
+
* Register a scheduled task from XML (node run directly). Tries each XML
|
|
1307
|
+
* variant in order — S4U (windowless) first, then InteractiveToken — and
|
|
1308
|
+
* finally falls back to a plain `/TR node …` task. The fallbacks may briefly
|
|
1309
|
+
* flash a console window but still contain NO script host, so the malware
|
|
1310
|
+
* fingerprint never returns. Returns true if any path succeeds.
|
|
1313
1311
|
*
|
|
1314
1312
|
* @param taskName Scheduled-task name.
|
|
1315
|
-
* @param xml
|
|
1313
|
+
* @param xml Task Scheduler XML variant(s), tried in order.
|
|
1316
1314
|
* @param command CLI subcommand for the /TR fallback ('daemon'|'watchdog').
|
|
1317
1315
|
* @param scheduleArgs schtasks schedule flags for the fallback (e.g. ['/SC','MINUTE','/MO','5']).
|
|
1318
1316
|
*/
|
|
@@ -1324,17 +1322,19 @@ function registerHiddenTask(taskName, xml, command, scheduleArgs) {
|
|
|
1324
1322
|
}
|
|
1325
1323
|
catch { /* ignore */ }
|
|
1326
1324
|
const xmlPath = path.join(dir, `${taskName.replace(/[^A-Za-z0-9]+/g, '_')}.task.xml`);
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1325
|
+
for (const variant of Array.isArray(xml) ? xml : [xml]) {
|
|
1326
|
+
try {
|
|
1327
|
+
// schtasks /XML insists the file encoding matches the XML declaration and
|
|
1328
|
+
// rejects UTF-8 ("unable to switch the encoding") — write UTF-16LE + BOM.
|
|
1329
|
+
fs.writeFileSync(xmlPath, `\ufeff${variant}`, 'utf16le');
|
|
1330
|
+
const created = (0, child_process_1.spawnSync)('schtasks', ['/Create', '/TN', taskName, '/XML', xmlPath, '/F'], {
|
|
1331
|
+
stdio: 'ignore', windowsHide: true, timeout: 20_000,
|
|
1332
|
+
});
|
|
1333
|
+
if (created.status === 0)
|
|
1334
|
+
return true;
|
|
1335
|
+
}
|
|
1336
|
+
catch { /* try next variant, then /TR */ }
|
|
1336
1337
|
}
|
|
1337
|
-
catch { /* fall through to /TR */ }
|
|
1338
1338
|
// Fallback: node directly via /TR (no wscript, no VBS). node/CLI may live
|
|
1339
1339
|
// under a path with spaces (Program Files), so quote each and let schtasks
|
|
1340
1340
|
// pass the whole /TR string through.
|
|
@@ -1369,6 +1369,49 @@ function windowsTaskReferencesScriptHost(taskName) {
|
|
|
1369
1369
|
return false;
|
|
1370
1370
|
}
|
|
1371
1371
|
}
|
|
1372
|
+
/** True when an existing task still runs with an InteractiveToken principal —
|
|
1373
|
+
* the 1.22.1 window-flash bug (console window on every trigger). Migration
|
|
1374
|
+
* trigger for the windowless S4U principal. */
|
|
1375
|
+
function windowsTaskRunsInteractive(taskName) {
|
|
1376
|
+
try {
|
|
1377
|
+
const query = (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', taskName, '/XML'], {
|
|
1378
|
+
encoding: 'utf8', windowsHide: true, timeout: 10_000,
|
|
1379
|
+
});
|
|
1380
|
+
if (query.status !== 0 || typeof query.stdout !== 'string')
|
|
1381
|
+
return false;
|
|
1382
|
+
return /<LogonType>\s*InteractiveToken\s*<\/LogonType>/i.test(query.stdout);
|
|
1383
|
+
}
|
|
1384
|
+
catch {
|
|
1385
|
+
return false;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Upgrade an EXISTING (working) task to the windowless S4U principal. Tries
|
|
1390
|
+
* ONLY the S4U XML — on failure (unelevated daemon) the current task is left
|
|
1391
|
+
* untouched so autostart never regresses from "flashes" to "broken".
|
|
1392
|
+
*/
|
|
1393
|
+
function upgradeTaskWindowless(taskName, s4uXml, logFn) {
|
|
1394
|
+
const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
1395
|
+
const dir = path.join(base, 'FullCourtDefense');
|
|
1396
|
+
try {
|
|
1397
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1398
|
+
}
|
|
1399
|
+
catch { /* ignore */ }
|
|
1400
|
+
const xmlPath = path.join(dir, `${taskName.replace(/[^A-Za-z0-9]+/g, '_')}.task.xml`);
|
|
1401
|
+
try {
|
|
1402
|
+
fs.writeFileSync(xmlPath, `\ufeff${s4uXml}`, 'utf16le');
|
|
1403
|
+
const created = (0, child_process_1.spawnSync)('schtasks', ['/Create', '/TN', taskName, '/XML', xmlPath, '/F'], {
|
|
1404
|
+
stdio: 'ignore', windowsHide: true, timeout: 20_000,
|
|
1405
|
+
});
|
|
1406
|
+
if (created.status === 0) {
|
|
1407
|
+
logFn(`Self-heal: "${taskName}" migrated to the windowless S4U principal (no more console-window flash).`);
|
|
1408
|
+
}
|
|
1409
|
+
else {
|
|
1410
|
+
logFn(`Self-heal: could not migrate "${taskName}" to S4U (needs elevation) — existing task kept as-is.`);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
catch { /* keep the existing working task */ }
|
|
1414
|
+
}
|
|
1372
1415
|
/** True when the per-user Run-key fallback still names wscript/a .vbs. */
|
|
1373
1416
|
function windowsRunKeyReferencesScriptHost() {
|
|
1374
1417
|
try {
|
|
@@ -1498,6 +1541,15 @@ function ensureWindowsAutostartHealthy(logFn) {
|
|
|
1498
1541
|
logFn('Self-heal: could not (re)create the watchdog task — daemon revival relies on logon autostart only.');
|
|
1499
1542
|
}
|
|
1500
1543
|
}
|
|
1544
|
+
// Migration (1.22.1 -> 1.22.2): InteractiveToken tasks flash a console
|
|
1545
|
+
// window on every trigger. Upgrade healthy existing tasks to S4U; S4U-only
|
|
1546
|
+
// attempt so a denied re-registration never breaks a working task.
|
|
1547
|
+
if (daemonTask.status === 0 && !legacyDaemon && windowsTaskRunsInteractive(TASK_NAME)) {
|
|
1548
|
+
upgradeTaskWindowless(TASK_NAME, buildDaemonTaskXml()[0], logFn);
|
|
1549
|
+
}
|
|
1550
|
+
if (watchdogQuery.status === 0 && !legacyWatchdog && windowsTaskRunsInteractive(WATCHDOG_TASK_NAME)) {
|
|
1551
|
+
upgradeTaskWindowless(WATCHDOG_TASK_NAME, buildWatchdogTaskXml()[0], logFn);
|
|
1552
|
+
}
|
|
1501
1553
|
// A stale Run-key fallback from an older install may still name wscript.
|
|
1502
1554
|
if (windowsRunKeyReferencesScriptHost()) {
|
|
1503
1555
|
(0, child_process_1.spawnSync)('reg', [
|
|
@@ -40,17 +40,17 @@ const child_process_1 = require("child_process");
|
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const os = __importStar(require("os"));
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
|
+
const daemon_1 = require("./daemon");
|
|
43
44
|
const TASK_NAME = 'FullCourtDefenseDesktopDiscover';
|
|
44
45
|
const STARTUP_LAUNCHER = 'FullCourtDefenseDesktopDiscover.cmd';
|
|
45
46
|
function windowsStartupLauncherPath() {
|
|
46
47
|
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
47
48
|
return path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', STARTUP_LAUNCHER);
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const script = path.resolve(process.argv[1] || path.join(__dirname, '..', 'index.js'));
|
|
50
|
+
/** The `discover …` argument tail (no node/script prefix) for task actions. */
|
|
51
|
+
function discoverCommandTail(opts = {}) {
|
|
52
52
|
const q = (value) => (/\s/.test(value) ? `"${value}"` : value);
|
|
53
|
-
const parts = [
|
|
53
|
+
const parts = ['discover', '--upload', '--deep', '--silent'];
|
|
54
54
|
if (opts.surface)
|
|
55
55
|
parts.push('--surface', q(opts.surface));
|
|
56
56
|
if (opts.scanRoot)
|
|
@@ -61,26 +61,40 @@ function discoverCommandLine(opts = {}) {
|
|
|
61
61
|
parts.push('--user-email', q(opts.userEmail));
|
|
62
62
|
return parts.join(' ');
|
|
63
63
|
}
|
|
64
|
+
function discoverCommandLine(opts = {}) {
|
|
65
|
+
const node = process.execPath;
|
|
66
|
+
const script = path.resolve(process.argv[1] || path.join(__dirname, '..', 'index.js'));
|
|
67
|
+
const q = (value) => (/\s/.test(value) ? `"${value}"` : value);
|
|
68
|
+
return [q(node), q(script), discoverCommandTail(opts)].join(' ');
|
|
69
|
+
}
|
|
70
|
+
// Registered through the shared windowless task helpers (daemon.ts): S4U
|
|
71
|
+
// principal first (no console-window flash on the daily run), InteractiveToken
|
|
72
|
+
// then plain /TR as fallbacks — never a script host.
|
|
64
73
|
function installWindowsSchedule(hour, opts, trigger = 'daily') {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
const tail = discoverCommandTail(opts);
|
|
75
|
+
const startBoundary = `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}-${String(new Date().getDate()).padStart(2, '0')}T${String(hour).padStart(2, '0')}:00:00`;
|
|
76
|
+
const triggerXml = trigger === 'logon'
|
|
77
|
+
? ` <LogonTrigger>
|
|
78
|
+
<Enabled>true</Enabled>
|
|
79
|
+
<UserId>${(0, daemon_1.xmlEscape)((0, daemon_1.taskUserId)())}</UserId>
|
|
80
|
+
</LogonTrigger>`
|
|
81
|
+
: ` <CalendarTrigger>
|
|
82
|
+
<Enabled>true</Enabled>
|
|
83
|
+
<StartBoundary>${startBoundary}</StartBoundary>
|
|
84
|
+
<ScheduleByDay>
|
|
85
|
+
<DaysInterval>1</DaysInterval>
|
|
86
|
+
</ScheduleByDay>
|
|
87
|
+
</CalendarTrigger>`;
|
|
88
|
+
const schedArgs = trigger === 'logon'
|
|
89
|
+
? ['/SC', 'ONLOGON', '/RL', 'LIMITED']
|
|
90
|
+
: ['/SC', 'DAILY', '/ST', `${String(hour).padStart(2, '0')}:00`, '/RL', 'LIMITED'];
|
|
91
|
+
const ok = (0, daemon_1.registerHiddenTask)(TASK_NAME, (0, daemon_1.buildTaskXmlVariants)(tail, triggerXml), tail, schedArgs);
|
|
92
|
+
if (!ok) {
|
|
79
93
|
if (trigger !== 'logon')
|
|
80
|
-
throw
|
|
94
|
+
throw new Error(`Could not register scheduled task "${TASK_NAME}" (Task Scheduler denied all variants).`);
|
|
81
95
|
const launcher = windowsStartupLauncherPath();
|
|
82
96
|
fs.mkdirSync(path.dirname(launcher), { recursive: true });
|
|
83
|
-
fs.writeFileSync(launcher, `@echo off\r\n${
|
|
97
|
+
fs.writeFileSync(launcher, `@echo off\r\n${discoverCommandLine(opts)} > "%USERPROFILE%\\.fullcourtdefense-discover.log" 2>&1\r\n`, 'utf8');
|
|
84
98
|
console.log(`Task Scheduler denied access; installed user logon launcher instead: ${launcher}`);
|
|
85
99
|
}
|
|
86
100
|
}
|
package/dist/version.json
CHANGED