diff --git a/contrib/windows/README.md b/contrib/windows/README.md index 2fed2cc7..6f3fda49 100644 --- a/contrib/windows/README.md +++ b/contrib/windows/README.md @@ -29,7 +29,9 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File contrib\windows\collie-c ``` `start` registers a Task Scheduler job (`herdr.collie`, override with `COLLIE_TASK_NAME`) that runs -at logon, restarts after failures, and outlives Herdr. `status` prints the same readiness banner the +at logon, restarts after failures, and outlives Herdr. It launches through `conhost --headless` +so the bridge gets a pseudoconsole with no window: a bare `powershell.exe` action would surface +on Windows 11 as a Windows Terminal tab, which kills the bridge when someone closes it. `status` prints the same readiness banner the POSIX script does; `logs` tails the bridge's stdout/stderr, including the pair preserved from the last crash. Config is the usual `.env` in the plugin config dir ([`.env.example`](../../.env.example) documents every key). diff --git a/contrib/windows/collie-ctl.ps1 b/contrib/windows/collie-ctl.ps1 index 36dcb8b5..dc6ea420 100644 --- a/contrib/windows/collie-ctl.ps1 +++ b/contrib/windows/collie-ctl.ps1 @@ -223,7 +223,13 @@ function Register-CollieTask { $arguments = '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "{0}" -TaskConfigDir "{1}" -TaskSocketPath "{2}" _exec-bridge' -f $ctl, $script:ConfigDir, $script:SocketPath $identity = [Security.Principal.WindowsIdentity]::GetCurrent().Name - $action = New-ScheduledTaskAction -Execute $powershell -Argument $arguments -WorkingDirectory $script:PluginRoot + # No console, so the bridge is not a closable Windows Terminal tab; the launcher is still $powershell. + $conhost = Join-Path $env:SystemRoot "System32\conhost.exe" + $action = if (Test-Path -LiteralPath $conhost) { + New-ScheduledTaskAction -Execute $conhost -Argument ('--headless "{0}" {1}' -f $powershell, $arguments) -WorkingDirectory $script:PluginRoot + } else { + New-ScheduledTaskAction -Execute $powershell -Argument $arguments -WorkingDirectory $script:PluginRoot + } $trigger = New-ScheduledTaskTrigger -AtLogOn -User $identity $principal = New-ScheduledTaskPrincipal -UserId $identity -LogonType Interactive -RunLevel (Get-CollieTaskRunLevel) $settings = New-ScheduledTaskSettingsSet ` diff --git a/contrib/windows/collie-ctl.test.ps1 b/contrib/windows/collie-ctl.test.ps1 index 6899ca9f..93342068 100644 --- a/contrib/windows/collie-ctl.test.ps1 +++ b/contrib/windows/collie-ctl.test.ps1 @@ -148,6 +148,8 @@ COLLIE_HOST="127.0.0.1" Assert-Equal $script:registered.Principal.RunLevel "Limited" "task privilege" Assert-Equal $script:registered.Settings.ExecutionTimeLimit ([TimeSpan]::Zero) "task execution limit" Assert-Equal $script:registered.Settings.RestartCount 999 "task restart policy" + Assert-Contains $script:registered.Action.Execute "conhost.exe" "task launcher is headless" + Assert-Contains $script:registered.Action.Argument "--headless" "task action asks for no console" $env:COLLIE_TASK_RUN_LEVEL = "highest" function Test-Administrator { $true }