feat(worker): run worker as per-user logon task instead of Windows service

A LocalSystem Windows service can't see the logged-in user's Claude CLI
authentication, so the worker now runs as the current user via a hidden
per-user logon Scheduled Task with restart-on-failure.

- Worker is WinExe (no console window) with a Serilog rolling file sink and
  a single-instance mutex so the logon task, app ensure-running, and Restart
  button can't fight over the SignalR port.
- Installer replaces the service steps (register/start/stop) with autostart
  task steps, migrates the legacy ClaudeDoWorker service away on update, and
  removes the task on uninstall. ServicePage drops the service-account UI.
- UI gains a WorkerLocator; the app ensures the worker is running at startup
  and the Restart button kills+relaunches this install's worker process.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-05-30 09:39:41 +02:00
parent 1e5b3a6c3e
commit 26c4e5771b
26 changed files with 1244 additions and 265 deletions

View File

@@ -11,8 +11,9 @@ public partial class SettingsViewModel : ObservableObject
{
private readonly InstallContext _context;
private readonly IReleaseClient _releases;
private readonly StopServiceStep _stopService;
private readonly StartServiceStep _startService;
private readonly StopWorkerStep _stopService;
private readonly StartWorkerStep _startService;
private readonly RegisterAutostartStep _registerAutostart;
private readonly DownloadAndExtractStep _downloadStep;
private readonly UninstallRunner _uninstallRunner;
@@ -37,8 +38,9 @@ public partial class SettingsViewModel : ObservableObject
PageResolver resolver,
InstallContext context,
IReleaseClient releases,
StopServiceStep stopService,
StartServiceStep startService,
StopWorkerStep stopService,
StartWorkerStep startService,
RegisterAutostartStep registerAutostart,
DownloadAndExtractStep downloadStep,
UninstallRunner uninstallRunner)
{
@@ -47,6 +49,7 @@ public partial class SettingsViewModel : ObservableObject
_releases = releases;
_stopService = stopService;
_startService = startService;
_registerAutostart = registerAutostart;
_downloadStep = downloadStep;
_uninstallRunner = uninstallRunner;
_selectedPage = Pages.FirstOrDefault();
@@ -154,7 +157,7 @@ public partial class SettingsViewModel : ObservableObject
IsStatusError = false;
var progress = new Progress<string>(msg => StatusMessage = msg);
var steps = new IInstallStep[] { _stopService, _downloadStep, _startService };
var steps = new IInstallStep[] { _stopService, _downloadStep, _registerAutostart, _startService };
foreach (var step in steps)
{