fullcourtdefense-cli 1.25.1 → 1.25.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.
- package/dist/commands/daemon.d.ts +20 -2
- package/dist/commands/daemon.js +125 -36
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -38,14 +38,27 @@ export type TaskLogonType = 'S4U' | 'InteractiveToken';
|
|
|
38
38
|
export declare function taskUserId(): string;
|
|
39
39
|
/** Local wall-clock timestamp (no ms, no tz) as Task Scheduler expects. */
|
|
40
40
|
export declare function taskLocalTimestamp(date: Date): string;
|
|
41
|
+
/** Full path to conhost.exe — the signed Windows binary whose `--headless`
|
|
42
|
+
* flag hosts a console child on a hidden pseudoconsole (no window, ever). */
|
|
43
|
+
export declare function conhostPath(): string;
|
|
41
44
|
/**
|
|
42
45
|
* Task Scheduler XML that runs `node.exe <cliEntry> <command>` hidden.
|
|
43
46
|
*
|
|
44
47
|
* @param command CLI subcommand + args ('daemon' | 'watchdog' | 'discover …').
|
|
45
48
|
* @param triggers Inner <Triggers> XML — logon for the daemon, a repeating
|
|
46
49
|
* time trigger for the 5-minute watchdog.
|
|
47
|
-
* @param logonType S4U (windowless, needs elevation to register) or
|
|
48
|
-
* InteractiveToken (registers unelevated
|
|
50
|
+
* @param logonType S4U (windowless by session, needs elevation to register) or
|
|
51
|
+
* InteractiveToken (registers unelevated — the action is
|
|
52
|
+
* wrapped in `conhost --headless` so it is ALSO windowless).
|
|
53
|
+
*
|
|
54
|
+
* Why the conhost wrapper: an InteractiveToken task runs console apps on the
|
|
55
|
+
* interactive desktop — a visible window on EVERY trigger (the "console flash
|
|
56
|
+
* every 5 minutes" complaint). Current Windows builds deny S4U registration to
|
|
57
|
+
* everyone except the task's own user running elevated (even SYSTEM gets
|
|
58
|
+
* Access denied — verified empirically; the updater's SYSTEM repair pass can
|
|
59
|
+
* no longer fix it). `conhost.exe --headless` sidesteps the whole fight: the
|
|
60
|
+
* child gets a hidden pseudoconsole, no window ever exists, registration
|
|
61
|
+
* stays unelevated, and no script host is involved (Defender-safe).
|
|
49
62
|
*/
|
|
50
63
|
export declare function buildTaskXml(command: string, triggers: string, logonType?: TaskLogonType): string;
|
|
51
64
|
/**
|
|
@@ -74,6 +87,11 @@ export declare function windowsTaskReferencesScriptHost(taskName: string): boole
|
|
|
74
87
|
* the 1.22.1 window-flash bug (console window on every trigger). Migration
|
|
75
88
|
* trigger for the windowless S4U principal. */
|
|
76
89
|
export declare function windowsTaskRunsInteractive(taskName: string): boolean;
|
|
90
|
+
/** True when triggering the task would open a VISIBLE console window:
|
|
91
|
+
* InteractiveToken principal AND a bare console action (not wrapped in
|
|
92
|
+
* `conhost --headless`). This — not the principal alone — is the flash
|
|
93
|
+
* condition; a conhost-wrapped InteractiveToken task is fully windowless. */
|
|
94
|
+
export declare function windowsTaskRunsVisibly(taskName: string): boolean;
|
|
77
95
|
/** Snapshot of the resident daemon for out-of-process callers (watchdog/status). */
|
|
78
96
|
export interface DaemonRuntimeState {
|
|
79
97
|
alive: boolean;
|
package/dist/commands/daemon.js
CHANGED
|
@@ -39,11 +39,13 @@ exports.discoverSweepCredentialEnv = discoverSweepCredentialEnv;
|
|
|
39
39
|
exports.cliVersion = cliVersion;
|
|
40
40
|
exports.taskUserId = taskUserId;
|
|
41
41
|
exports.taskLocalTimestamp = taskLocalTimestamp;
|
|
42
|
+
exports.conhostPath = conhostPath;
|
|
42
43
|
exports.buildTaskXml = buildTaskXml;
|
|
43
44
|
exports.buildTaskXmlVariants = buildTaskXmlVariants;
|
|
44
45
|
exports.registerHiddenTask = registerHiddenTask;
|
|
45
46
|
exports.windowsTaskReferencesScriptHost = windowsTaskReferencesScriptHost;
|
|
46
47
|
exports.windowsTaskRunsInteractive = windowsTaskRunsInteractive;
|
|
48
|
+
exports.windowsTaskRunsVisibly = windowsTaskRunsVisibly;
|
|
47
49
|
exports.daemonRuntimeState = daemonRuntimeState;
|
|
48
50
|
exports.spawnDetachedDaemon = spawnDetachedDaemon;
|
|
49
51
|
exports.pidIsAlive = pidIsAlive;
|
|
@@ -1261,23 +1263,41 @@ function taskLocalTimestamp(date) {
|
|
|
1261
1263
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
|
|
1262
1264
|
`T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
1263
1265
|
}
|
|
1266
|
+
/** Full path to conhost.exe — the signed Windows binary whose `--headless`
|
|
1267
|
+
* flag hosts a console child on a hidden pseudoconsole (no window, ever). */
|
|
1268
|
+
function conhostPath() {
|
|
1269
|
+
return path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'conhost.exe');
|
|
1270
|
+
}
|
|
1264
1271
|
/**
|
|
1265
1272
|
* Task Scheduler XML that runs `node.exe <cliEntry> <command>` hidden.
|
|
1266
1273
|
*
|
|
1267
1274
|
* @param command CLI subcommand + args ('daemon' | 'watchdog' | 'discover …').
|
|
1268
1275
|
* @param triggers Inner <Triggers> XML — logon for the daemon, a repeating
|
|
1269
1276
|
* time trigger for the 5-minute watchdog.
|
|
1270
|
-
* @param logonType S4U (windowless, needs elevation to register) or
|
|
1271
|
-
* InteractiveToken (registers unelevated
|
|
1277
|
+
* @param logonType S4U (windowless by session, needs elevation to register) or
|
|
1278
|
+
* InteractiveToken (registers unelevated — the action is
|
|
1279
|
+
* wrapped in `conhost --headless` so it is ALSO windowless).
|
|
1280
|
+
*
|
|
1281
|
+
* Why the conhost wrapper: an InteractiveToken task runs console apps on the
|
|
1282
|
+
* interactive desktop — a visible window on EVERY trigger (the "console flash
|
|
1283
|
+
* every 5 minutes" complaint). Current Windows builds deny S4U registration to
|
|
1284
|
+
* everyone except the task's own user running elevated (even SYSTEM gets
|
|
1285
|
+
* Access denied — verified empirically; the updater's SYSTEM repair pass can
|
|
1286
|
+
* no longer fix it). `conhost.exe --headless` sidesteps the whole fight: the
|
|
1287
|
+
* child gets a hidden pseudoconsole, no window ever exists, registration
|
|
1288
|
+
* stays unelevated, and no script host is involved (Defender-safe).
|
|
1272
1289
|
*/
|
|
1273
1290
|
function buildTaskXml(command, triggers, logonType = 'S4U') {
|
|
1274
1291
|
const userId = xmlEscape(taskUserId());
|
|
1275
|
-
const
|
|
1276
|
-
const
|
|
1292
|
+
const headless = logonType === 'InteractiveToken';
|
|
1293
|
+
const execCommand = headless ? xmlEscape(conhostPath()) : xmlEscape(process.execPath);
|
|
1294
|
+
const args = headless
|
|
1295
|
+
? xmlEscape(`--headless "${process.execPath}" "${cliEntry()}" ${command}`)
|
|
1296
|
+
: xmlEscape(`"${cliEntry()}" ${command}`);
|
|
1277
1297
|
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
1278
1298
|
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
1279
1299
|
<RegistrationInfo>
|
|
1280
|
-
<Description>FullCourtDefense ${xmlEscape(command)} — runs node directly, windowless, no script host.</Description>
|
|
1300
|
+
<Description>FullCourtDefense ${xmlEscape(command)} — runs node ${headless ? 'under conhost --headless' : 'directly'}, windowless, no script host.</Description>
|
|
1281
1301
|
</RegistrationInfo>
|
|
1282
1302
|
<Triggers>
|
|
1283
1303
|
${triggers}
|
|
@@ -1307,7 +1327,7 @@ ${triggers}
|
|
|
1307
1327
|
</Settings>
|
|
1308
1328
|
<Actions Context="Author">
|
|
1309
1329
|
<Exec>
|
|
1310
|
-
<Command>${
|
|
1330
|
+
<Command>${execCommand}</Command>
|
|
1311
1331
|
<Arguments>${args}</Arguments>
|
|
1312
1332
|
</Exec>
|
|
1313
1333
|
</Actions>
|
|
@@ -1383,10 +1403,10 @@ function registerHiddenTask(taskName, xml, command, scheduleArgs) {
|
|
|
1383
1403
|
}
|
|
1384
1404
|
catch { /* try next variant, then /TR */ }
|
|
1385
1405
|
}
|
|
1386
|
-
// Fallback: node
|
|
1387
|
-
// under a path with spaces (Program Files), so
|
|
1388
|
-
// pass the whole /TR string through.
|
|
1389
|
-
const tr =
|
|
1406
|
+
// Fallback: node via /TR under conhost --headless (no wscript, no VBS, no
|
|
1407
|
+
// window). node/CLI may live under a path with spaces (Program Files), so
|
|
1408
|
+
// quote each and let schtasks pass the whole /TR string through.
|
|
1409
|
+
const tr = `${conhostPath()} --headless "${process.execPath}" "${cliEntry()}" ${command}`;
|
|
1390
1410
|
const fallback = (0, child_process_1.spawnSync)('schtasks', [
|
|
1391
1411
|
'/Create', '/TN', taskName, '/TR', tr, ...scheduleArgs, '/F',
|
|
1392
1412
|
], { stdio: 'ignore', windowsHide: true, timeout: 20_000 });
|
|
@@ -1421,16 +1441,30 @@ function windowsTaskReferencesScriptHost(taskName) {
|
|
|
1421
1441
|
* the 1.22.1 window-flash bug (console window on every trigger). Migration
|
|
1422
1442
|
* trigger for the windowless S4U principal. */
|
|
1423
1443
|
function windowsTaskRunsInteractive(taskName) {
|
|
1444
|
+
return windowsTaskXmlState(taskName).interactive;
|
|
1445
|
+
}
|
|
1446
|
+
/** True when triggering the task would open a VISIBLE console window:
|
|
1447
|
+
* InteractiveToken principal AND a bare console action (not wrapped in
|
|
1448
|
+
* `conhost --headless`). This — not the principal alone — is the flash
|
|
1449
|
+
* condition; a conhost-wrapped InteractiveToken task is fully windowless. */
|
|
1450
|
+
function windowsTaskRunsVisibly(taskName) {
|
|
1451
|
+
const state = windowsTaskXmlState(taskName);
|
|
1452
|
+
return state.interactive && !state.headless;
|
|
1453
|
+
}
|
|
1454
|
+
function windowsTaskXmlState(taskName) {
|
|
1424
1455
|
try {
|
|
1425
1456
|
const query = (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', taskName, '/XML'], {
|
|
1426
1457
|
encoding: 'utf8', windowsHide: true, timeout: 10_000,
|
|
1427
1458
|
});
|
|
1428
1459
|
if (query.status !== 0 || typeof query.stdout !== 'string')
|
|
1429
|
-
return false;
|
|
1430
|
-
return
|
|
1460
|
+
return { interactive: false, headless: false };
|
|
1461
|
+
return {
|
|
1462
|
+
interactive: /<LogonType>\s*InteractiveToken\s*<\/LogonType>/i.test(query.stdout),
|
|
1463
|
+
headless: /conhost(\.exe)?<\/Command>[\s\S]*?--headless/i.test(query.stdout),
|
|
1464
|
+
};
|
|
1431
1465
|
}
|
|
1432
1466
|
catch {
|
|
1433
|
-
return false;
|
|
1467
|
+
return { interactive: false, headless: false };
|
|
1434
1468
|
}
|
|
1435
1469
|
}
|
|
1436
1470
|
/**
|
|
@@ -1456,7 +1490,38 @@ function upgradeTaskWindowless(taskName, s4uXml, logFn) {
|
|
|
1456
1490
|
logFn(`Self-heal: "${taskName}" migrated to the windowless S4U principal (no more console-window flash).`);
|
|
1457
1491
|
return true;
|
|
1458
1492
|
}
|
|
1459
|
-
logFn(`Self-heal: could not migrate "${taskName}" to S4U (needs elevation) —
|
|
1493
|
+
logFn(`Self-heal: could not migrate "${taskName}" to S4U (needs same-user elevation) — re-registering it headless instead.`);
|
|
1494
|
+
return false;
|
|
1495
|
+
}
|
|
1496
|
+
catch {
|
|
1497
|
+
return false; /* keep the existing working task */
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Re-register an InteractiveToken task with its action wrapped in
|
|
1502
|
+
* `conhost --headless` — windowless without touching the principal, so the
|
|
1503
|
+
* SAME user can do it unelevated (unlike S4U, which current Windows only
|
|
1504
|
+
* grants to the task's own user running elevated — even SYSTEM is denied).
|
|
1505
|
+
* Returns true when the task no longer opens a window on trigger.
|
|
1506
|
+
*/
|
|
1507
|
+
function rewrapTaskHeadless(taskName, interactiveHeadlessXml, logFn) {
|
|
1508
|
+
const base = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
1509
|
+
const dir = path.join(base, 'FullCourtDefense');
|
|
1510
|
+
try {
|
|
1511
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1512
|
+
}
|
|
1513
|
+
catch { /* ignore */ }
|
|
1514
|
+
const xmlPath = path.join(dir, `${taskName.replace(/[^A-Za-z0-9]+/g, '_')}.task.xml`);
|
|
1515
|
+
try {
|
|
1516
|
+
fs.writeFileSync(xmlPath, `\ufeff${interactiveHeadlessXml}`, 'utf16le');
|
|
1517
|
+
const created = (0, child_process_1.spawnSync)('schtasks', ['/Create', '/TN', taskName, '/XML', xmlPath, '/F'], {
|
|
1518
|
+
stdio: 'ignore', windowsHide: true, timeout: 20_000,
|
|
1519
|
+
});
|
|
1520
|
+
if (created.status === 0) {
|
|
1521
|
+
logFn(`Self-heal: "${taskName}" re-registered with a conhost --headless action (windowless from its next trigger).`);
|
|
1522
|
+
return true;
|
|
1523
|
+
}
|
|
1524
|
+
logFn(`Self-heal: could not re-register "${taskName}" headless — asking the elevated updater task to migrate it.`);
|
|
1460
1525
|
return false;
|
|
1461
1526
|
}
|
|
1462
1527
|
catch {
|
|
@@ -1478,14 +1543,20 @@ let updaterMigrationKicked = false;
|
|
|
1478
1543
|
function kickUpdaterTaskForMigration(logFn) {
|
|
1479
1544
|
if (updaterMigrationKicked)
|
|
1480
1545
|
return;
|
|
1481
|
-
updaterMigrationKicked = true;
|
|
1482
1546
|
try {
|
|
1483
1547
|
const run = (0, child_process_1.spawnSync)('schtasks', ['/Run', '/TN', selfUpdate_1.MSI_UPDATER_TASK_NAME], {
|
|
1484
1548
|
stdio: 'ignore', windowsHide: true, timeout: 20_000,
|
|
1485
1549
|
});
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1550
|
+
// Latch ONLY on a successful kick. On a fresh MSI install the daemon can
|
|
1551
|
+
// boot before the SYSTEM updater task exists — burning the one-shot latch
|
|
1552
|
+
// on that failure left the machine flashing until the 03:07 daily run.
|
|
1553
|
+
if (run.status === 0) {
|
|
1554
|
+
updaterMigrationKicked = true;
|
|
1555
|
+
logFn(`Self-heal: elevated updater task triggered — its repair pass migrates the tasks now.`);
|
|
1556
|
+
}
|
|
1557
|
+
else {
|
|
1558
|
+
logFn(`Self-heal: updater task unavailable for the task migration — will retry on a later self-heal; the daily run (03:07) is the backstop.`);
|
|
1559
|
+
}
|
|
1489
1560
|
}
|
|
1490
1561
|
catch { /* best-effort — the daily schedule remains the backstop */ }
|
|
1491
1562
|
}
|
|
@@ -1528,22 +1599,33 @@ function startDaemonNowWindows(viaTask = false) {
|
|
|
1528
1599
|
}
|
|
1529
1600
|
}
|
|
1530
1601
|
catch { /* not running */ }
|
|
1531
|
-
if (viaTask) {
|
|
1602
|
+
if (viaTask && !windowsTaskRunsVisibly(TASK_NAME)) {
|
|
1532
1603
|
// Start through the scheduled task so the daemon runs with the task's
|
|
1533
1604
|
// LIMITED (non-elevated) token. Launching directly from an elevated MSI
|
|
1534
1605
|
// custom action would leave an elevated daemon that a normal-user CLI can
|
|
1535
1606
|
// never stop or supersede.
|
|
1607
|
+
//
|
|
1608
|
+
// ONLY when the task cannot open a window (S4U principal, or an
|
|
1609
|
+
// InteractiveToken action wrapped in conhost --headless): starting a
|
|
1610
|
+
// bare-console InteractiveToken task runs node.exe on the interactive
|
|
1611
|
+
// desktop, and since the daemon never exits, that console window stays
|
|
1612
|
+
// OPEN on the user's screen — the "open console at the end of
|
|
1613
|
+
// installation" customer complaint. A visibly-registered task takes the
|
|
1614
|
+
// hidden WMI path below instead; self-heal re-registers it headless on
|
|
1615
|
+
// the next daemon boot.
|
|
1536
1616
|
const run = (0, child_process_1.spawnSync)('schtasks', ['/Run', '/TN', TASK_NAME], { stdio: 'ignore', windowsHide: true, timeout: 20_000 });
|
|
1537
1617
|
if (run.status === 0)
|
|
1538
1618
|
return;
|
|
1539
1619
|
}
|
|
1540
1620
|
// WMI process creation escapes the MSI job object. Runs node.exe DIRECTLY —
|
|
1541
|
-
// no wscript/VBS (Defender's Commando.A!ml heuristic)
|
|
1621
|
+
// no wscript/VBS (Defender's Commando.A!ml heuristic). Win32_ProcessStartup
|
|
1622
|
+
// ShowWindow=0 (SW_HIDE) is REQUIRED: without it Win32_Process.Create gives
|
|
1623
|
+
// the console app a visible window on the interactive desktop.
|
|
1542
1624
|
const nodeEsc = process.execPath.replace(/'/g, "''");
|
|
1543
1625
|
const entryEsc = cliEntry().replace(/'/g, "''");
|
|
1544
1626
|
(0, child_process_1.spawnSync)('powershell', [
|
|
1545
1627
|
'-NoProfile', '-NonInteractive', '-Command',
|
|
1546
|
-
|
|
1628
|
+
`$si = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly -Property @{ ShowWindow = [uint16]0 }; Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = ('\"' + '${nodeEsc}' + '\" \"' + '${entryEsc}' + '\" daemon'); ProcessStartupInformation = $si } | Out-Null`,
|
|
1547
1629
|
], { stdio: 'ignore', windowsHide: true, timeout: 20_000 });
|
|
1548
1630
|
}
|
|
1549
1631
|
function installWindows() {
|
|
@@ -1555,11 +1637,11 @@ function installWindows() {
|
|
|
1555
1637
|
let ok = taskOk;
|
|
1556
1638
|
if (!ok) {
|
|
1557
1639
|
// Fallback: per-user Run key — no elevation needed, runs at every logon.
|
|
1558
|
-
//
|
|
1559
|
-
//
|
|
1640
|
+
// conhost --headless keeps the logon launch windowless (no wscript, no
|
|
1641
|
+
// script-host persistence fingerprint).
|
|
1560
1642
|
const reg = (0, child_process_1.spawnSync)('reg', [
|
|
1561
1643
|
'add', WINDOWS_RUN_KEY, '/v', WINDOWS_RUN_VALUE, '/t', 'REG_SZ',
|
|
1562
|
-
'/d', `"${process.execPath}" "${cliEntry()}" daemon`, '/f',
|
|
1644
|
+
'/d', `"${conhostPath()}" --headless "${process.execPath}" "${cliEntry()}" daemon`, '/f',
|
|
1563
1645
|
], { stdio: 'ignore', windowsHide: true });
|
|
1564
1646
|
ok = reg.status === 0;
|
|
1565
1647
|
}
|
|
@@ -1604,7 +1686,7 @@ function ensureWindowsAutostartHealthy(logFn) {
|
|
|
1604
1686
|
if (!created) {
|
|
1605
1687
|
(0, child_process_1.spawnSync)('reg', [
|
|
1606
1688
|
'add', WINDOWS_RUN_KEY, '/v', WINDOWS_RUN_VALUE, '/t', 'REG_SZ',
|
|
1607
|
-
'/d', `"${process.execPath}" "${cliEntry()}" daemon`, '/f',
|
|
1689
|
+
'/d', `"${conhostPath()}" --headless "${process.execPath}" "${cliEntry()}" daemon`, '/f',
|
|
1608
1690
|
], { stdio: 'ignore', windowsHide: true, timeout: 10_000 });
|
|
1609
1691
|
}
|
|
1610
1692
|
if (daemonTask.status !== 0)
|
|
@@ -1618,17 +1700,24 @@ function ensureWindowsAutostartHealthy(logFn) {
|
|
|
1618
1700
|
logFn('Self-heal: could not (re)create the watchdog task — daemon revival relies on logon autostart only.');
|
|
1619
1701
|
}
|
|
1620
1702
|
}
|
|
1621
|
-
// Migration
|
|
1622
|
-
// window on every trigger.
|
|
1623
|
-
//
|
|
1624
|
-
//
|
|
1625
|
-
//
|
|
1703
|
+
// Migration: an InteractiveToken task with a bare console action flashes a
|
|
1704
|
+
// window on every trigger. Try the S4U principal first (works when this
|
|
1705
|
+
// daemon happens to be elevated — current Windows denies S4U registration
|
|
1706
|
+
// to everyone else, INCLUDING SYSTEM, so the updater usually cannot help).
|
|
1707
|
+
// When S4U is denied, re-register the task as InteractiveToken with the
|
|
1708
|
+
// action wrapped in `conhost --headless` — same-user unelevated
|
|
1709
|
+
// registration always works and is just as windowless. The updater kick
|
|
1710
|
+
// stays as the final backstop for machines where even that failed.
|
|
1626
1711
|
let migrationDenied = false;
|
|
1627
|
-
if (daemonTask.status === 0 && !legacyDaemon &&
|
|
1628
|
-
|
|
1712
|
+
if (daemonTask.status === 0 && !legacyDaemon && windowsTaskRunsVisibly(TASK_NAME)) {
|
|
1713
|
+
const daemonXml = buildDaemonTaskXml();
|
|
1714
|
+
migrationDenied = !(upgradeTaskWindowless(TASK_NAME, daemonXml[0], logFn)
|
|
1715
|
+
|| rewrapTaskHeadless(TASK_NAME, daemonXml[1], logFn)) || migrationDenied;
|
|
1629
1716
|
}
|
|
1630
|
-
if (watchdogQuery.status === 0 && !legacyWatchdog &&
|
|
1631
|
-
|
|
1717
|
+
if (watchdogQuery.status === 0 && !legacyWatchdog && windowsTaskRunsVisibly(WATCHDOG_TASK_NAME)) {
|
|
1718
|
+
const watchdogXml = buildWatchdogTaskXml();
|
|
1719
|
+
migrationDenied = !(upgradeTaskWindowless(WATCHDOG_TASK_NAME, watchdogXml[0], logFn)
|
|
1720
|
+
|| rewrapTaskHeadless(WATCHDOG_TASK_NAME, watchdogXml[1], logFn)) || migrationDenied;
|
|
1632
1721
|
}
|
|
1633
1722
|
if (migrationDenied)
|
|
1634
1723
|
kickUpdaterTaskForMigration(logFn);
|
|
@@ -1636,9 +1725,9 @@ function ensureWindowsAutostartHealthy(logFn) {
|
|
|
1636
1725
|
if (windowsRunKeyReferencesScriptHost()) {
|
|
1637
1726
|
(0, child_process_1.spawnSync)('reg', [
|
|
1638
1727
|
'add', WINDOWS_RUN_KEY, '/v', WINDOWS_RUN_VALUE, '/t', 'REG_SZ',
|
|
1639
|
-
'/d', `"${process.execPath}" "${cliEntry()}" daemon`, '/f',
|
|
1728
|
+
'/d', `"${conhostPath()}" --headless "${process.execPath}" "${cliEntry()}" daemon`, '/f',
|
|
1640
1729
|
], { stdio: 'ignore', windowsHide: true, timeout: 10_000 });
|
|
1641
|
-
logFn('Self-heal: rewrote legacy wscript Run-key entry to run node
|
|
1730
|
+
logFn('Self-heal: rewrote legacy wscript Run-key entry to run node headless.');
|
|
1642
1731
|
}
|
|
1643
1732
|
removeLegacyWindowsLaunchers();
|
|
1644
1733
|
}
|
package/dist/version.json
CHANGED