Drop Task Scheduler; autostart without administrator rights
Register-ScheduledTask failed with Access denied (0x80070005). Writing to the root task folder needs elevation, so the claim that this install needed no admin was simply wrong. Rather than demand UAC, use two mechanisms that need no privileges at all: - a Startup-folder entry (AutoFirmer.cmd) runs start-all.bat at logon - PM2's own --cron-restart with --no-autorestart drives the 5-minute update check, so PM2 owns the schedule it was already going to resurrect anyway Both still run in the logged-in interactive session, which is the requirement that ruled out a Windows service in the first place: the clicker sends real input and needs a desktop. pm2 save now runs after the updater is registered, so `pm2 resurrect` brings back all three processes rather than two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7e4714c33d
commit
724ba5581c
@@ -161,10 +161,10 @@ or after declining:
|
|||||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-autostart.ps1
|
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-autostart.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
That registers PM2 for both processes and two scheduled tasks — one to start
|
That puts the dashboard, the clicker and an update checker under PM2, adds a
|
||||||
everything at logon, one to check `master` for updates every 5 minutes. It needs
|
Startup-folder entry so PM2 comes back at logon, and lets PM2's cron restart run
|
||||||
no elevation and is safe to re-run; it replaces the tasks rather than stacking
|
the update check every 5 minutes. It needs no administrator rights, changes
|
||||||
them up.
|
nothing machine-wide, and is safe to re-run.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
pm2 list # what is running
|
pm2 list # what is running
|
||||||
@@ -172,11 +172,14 @@ pm2 logs autofirmer # dashboard output
|
|||||||
pm2 logs clicker # runner output
|
pm2 logs clicker # runner output
|
||||||
```
|
```
|
||||||
|
|
||||||
**Why scheduled tasks and not a Windows service.** The clicker sends real mouse
|
**Why not a Windows service.** The clicker sends real mouse and keyboard input
|
||||||
and keyboard input and has to own a desktop. A service runs in session 0, which
|
and has to own a desktop. A service runs in session 0, which has none, so the
|
||||||
has none, so the clicks would go nowhere. Both tasks therefore run as you with
|
clicks would go nowhere. Everything therefore runs in your logged-in session —
|
||||||
"run only when user is logged on" — which also means an unattended reboot leaves
|
which also means an unattended reboot leaves the instance down until somebody
|
||||||
the instance down until somebody logs in.
|
logs in.
|
||||||
|
|
||||||
|
To stop it starting at logon, delete the `AutoFirmer.cmd` shortcut from your
|
||||||
|
Startup folder (`shell:startup` in the Run dialog).
|
||||||
|
|
||||||
To start everything by hand without waiting for a logon:
|
To start everything by hand without waiting for a logon:
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,22 @@
|
|||||||
The only Windows-specific piece of the setup. Everything it schedules is
|
The only Windows-specific piece of the setup. Everything it schedules is
|
||||||
plain Node, so porting to macOS or Linux means replacing this file alone.
|
plain Node, so porting to macOS or Linux means replacing this file alone.
|
||||||
|
|
||||||
Two scheduled tasks are created, both running as the current user with
|
Deliberately needs no administrator rights, and changes nothing
|
||||||
LogonType Interactive. That is not incidental: the clicker drives real mouse
|
machine-wide:
|
||||||
and keyboard input and must own a desktop, which a Windows service (session
|
|
||||||
0) does not have.
|
|
||||||
|
|
||||||
Idempotent - re-running replaces the tasks rather than duplicating them.
|
* PM2 supervises the dashboard, the clicker and the update checker.
|
||||||
No elevation required.
|
* A Startup-folder entry runs scripts\start-all.bat at logon.
|
||||||
|
* PM2's own cron restart drives the update checks.
|
||||||
|
|
||||||
|
Task Scheduler is avoided on purpose - Register-ScheduledTask needs
|
||||||
|
elevation to write to the root task folder.
|
||||||
|
|
||||||
|
Everything runs in the logged-in user's interactive session. That is not
|
||||||
|
incidental: the clicker sends real mouse and keyboard input and must own a
|
||||||
|
desktop, which a Windows service (session 0) does not have. The trade-off
|
||||||
|
is that an unattended reboot leaves the instance down until someone logs in.
|
||||||
|
|
||||||
|
Idempotent - safe to re-run.
|
||||||
|
|
||||||
.PARAMETER IntervalMinutes
|
.PARAMETER IntervalMinutes
|
||||||
How often to check master for updates. Default 5.
|
How often to check master for updates. Default 5.
|
||||||
@@ -28,8 +37,6 @@ param(
|
|||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$Root = Split-Path -Parent $PSScriptRoot
|
$Root = Split-Path -Parent $PSScriptRoot
|
||||||
$StartTask = 'AutoFirmer Start'
|
|
||||||
$UpdateTask = 'AutoFirmer Update'
|
|
||||||
|
|
||||||
function Info($m) { Write-Host " $m" }
|
function Info($m) { Write-Host " $m" }
|
||||||
function Warn($m) { Write-Host " ! $m" -ForegroundColor Yellow }
|
function Warn($m) { Write-Host " ! $m" -ForegroundColor Yellow }
|
||||||
@@ -127,46 +134,50 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Invoke-Pm2 @('save')) -ne 0) { throw 'pm2 save failed' }
|
|
||||||
Info 'pm2: process list saved'
|
|
||||||
} finally {
|
} finally {
|
||||||
Pop-Location
|
Pop-Location
|
||||||
}
|
}
|
||||||
|
|
||||||
# -- Scheduled tasks ---------------------------------------------------------
|
# -- Start at logon (Startup folder) -----------------------------------------
|
||||||
# LogonType Interactive is what grants the desktop the clicker needs.
|
# Not Task Scheduler: Register-ScheduledTask needs elevation to write to the
|
||||||
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" `
|
# root task folder, and the whole point is an install that never asks for
|
||||||
-LogonType Interactive -RunLevel Limited
|
# admin. A Startup-folder entry needs no privileges and runs in the user's
|
||||||
|
# interactive session, which is exactly what the clicker requires - a service
|
||||||
|
# in session 0 has no desktop and its clicks would go nowhere.
|
||||||
|
$startupDir = [Environment]::GetFolderPath('Startup')
|
||||||
|
$startupCmd = Join-Path $startupDir 'AutoFirmer.cmd'
|
||||||
|
$startAll = Join-Path $Root 'scripts\start-all.bat'
|
||||||
|
|
||||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries `
|
@"
|
||||||
-DontStopIfGoingOnBatteries -StartWhenAvailable `
|
@echo off
|
||||||
-MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Hours 1)
|
REM Written by scripts\install-autostart.ps1 - delete this file to stop
|
||||||
|
REM AutoFirmer starting at logon.
|
||||||
|
call "$startAll"
|
||||||
|
"@ | Set-Content -Path $startupCmd -Encoding ASCII
|
||||||
|
|
||||||
function Register-Task($Name, $Action, $Triggers, $Description) {
|
Info "logon entry: $startupCmd"
|
||||||
Unregister-ScheduledTask -TaskName $Name -Confirm:$false -ErrorAction SilentlyContinue
|
|
||||||
Register-ScheduledTask -TaskName $Name -Action $Action -Trigger $Triggers `
|
# -- Update checks (PM2 cron) ------------------------------------------------
|
||||||
-Principal $principal -Settings $settings -Description $Description | Out-Null
|
# Also not Task Scheduler, same reason. PM2 is already running and already
|
||||||
Info "task registered: $Name"
|
# comes back at logon, so it can own the schedule too: --cron-restart fires the
|
||||||
|
# script on a schedule and --no-autorestart stops PM2 relaunching it the moment
|
||||||
|
# it exits.
|
||||||
|
$updateScript = Join-Path $Root 'scripts\update-check.mjs'
|
||||||
|
Invoke-Pm2 @('delete', 'autofirmer-update') -Quiet | Out-Null
|
||||||
|
$cron = "*/$IntervalMinutes * * * *"
|
||||||
|
if ((Invoke-Pm2 @('start', $updateScript, '--name', 'autofirmer-update',
|
||||||
|
'--no-autorestart', '--cron-restart', $cron)) -eq 0) {
|
||||||
|
Info "update checks: every $IntervalMinutes minutes ($cron)"
|
||||||
|
} else {
|
||||||
|
Warn 'could not register the update checker - run scripts\update-check.mjs by hand to update'
|
||||||
}
|
}
|
||||||
|
|
||||||
Register-Task $StartTask `
|
# Save again so `pm2 resurrect` at logon brings the updater back too.
|
||||||
(New-ScheduledTaskAction -Execute 'cmd.exe' `
|
if ((Invoke-Pm2 @('save')) -ne 0) { Warn 'pm2 save failed - processes may not return after a reboot' }
|
||||||
-Argument "/c `"$(Join-Path $Root 'scripts\start-all.bat')`"" -WorkingDirectory $Root) `
|
|
||||||
(New-ScheduledTaskTrigger -AtLogOn) `
|
|
||||||
'Start AutoFirmer and the clicker at logon.'
|
|
||||||
|
|
||||||
# AtLogOn covers a reboot; the repeating Once trigger covers the rest of the day.
|
|
||||||
$repeat = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) `
|
|
||||||
-RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes) `
|
|
||||||
-RepetitionDuration (New-TimeSpan -Days 3650)
|
|
||||||
|
|
||||||
Register-Task $UpdateTask `
|
|
||||||
(New-ScheduledTaskAction -Execute 'node.exe' `
|
|
||||||
-Argument 'scripts\update-check.mjs' -WorkingDirectory $Root) `
|
|
||||||
@((New-ScheduledTaskTrigger -AtLogOn), $repeat) `
|
|
||||||
"Check master for updates every $IntervalMinutes minutes; rebuild and restart when it moves."
|
|
||||||
|
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Info 'Done. Both processes are registered and will come back at logon.'
|
Info 'Done. No admin was needed and nothing machine-wide was changed.'
|
||||||
Info "Update checks run every $IntervalMinutes minutes; see scripts\update.log"
|
Info 'Running now, and again at every logon.'
|
||||||
|
Info "Updates checked every $IntervalMinutes minutes; see scripts\update.log"
|
||||||
Info 'Useful: pm2 list | pm2 logs autofirmer | pm2 logs clicker'
|
Info 'Useful: pm2 list | pm2 logs autofirmer | pm2 logs clicker'
|
||||||
|
Info "To disable autostart: delete $startupCmd"
|
||||||
|
|||||||
Reference in New Issue
Block a user