Files
christianandClaude Fable 5.1 a33e344cae Steps 4-6: the jobs, the set-up tab with generated scripts, Install the staged folder, site bundles and the campus template
- Overview jobs: Change the playlist, Add or remove a HyperDeck, Add a MIDI link, Add a computer,
  Make a PC program startable; 'Add a key for it' on unwatched Companion connections.
- Computers > Setup: steps with a [how] each and the set-up scripts generated per computer by
  settings/scripts.py from the templates in remote-access/ (no site-shaped value left in them);
  Remove this computer drops the fixes and checks that used it.
- Site setup > Site: the running and staged code, Install the staged folder (a one-shot launchd job
  running install.sh --keep-config, refused while live), Export site bundle (zip), New campus from a
  bundle (this campus's site block stays; every copied computer is flagged until touched).
- sites/: na -> the repo config, _template = the empty campus; install.sh --site <id>.
- Review reads appended checks as 'added'; tests for all of it.
- Deployed page-only to NA Ingest. (config.json, status_panel.py and readiness.py are left to the
  READY-page work in progress in the same tree.)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 14:53:08 -04:00

134 lines
8.2 KiB
Plaintext

# Status panel remote access for {{INGEST_NAME}} - run ONCE on {{PC_NAME}}, logged in as the normal user.
# (Generated by the settings page from the computer's record; run it again after adding a program.)
#
# 1. Save this file as C:\Users\<you>\windows-setup.ps1
# 2. Start menu -> type PowerShell -> right-click -> Run as administrator
# 3. Set-ExecutionPolicy RemoteSigned -Scope CurrentUser (answer Y; once per PC)
# 4. Run it: & "$env:USERPROFILE\windows-setup.ps1"
# 5. Paste the "RESULT" block it prints into the settings page (Computers > Setup). If it says
# RESTART THIS PC, restart, then repeat steps 2 and 4.
#
# It turns on Windows' built-in SSH server, lets {{INGEST_NAME}} log in with its key
# (no password), and creates "start" tasks for the programs listed for this PC
# ({{PROGRAMS}}). The tasks start the program on this desktop, which
# a plain SSH command cannot do on Windows. Safe to run more than once.
$ErrorActionPreference = 'Stop'
$Key = '{{KEY}}'
$KeyMark = '{{KEY_COMMENT}}'
$IngestIPs = @({{INGEST_IPS}}) # {{INGEST_NAME}}'s addresses
$notes = @()
# 1. OpenSSH Server, started now and at every boot, allowed through the firewall
$cap = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*' | Select-Object -First 1
if (-not $cap) { throw 'This Windows has no OpenSSH Server feature to install - note this on the settings page.' }
$restart = $false
if ($cap.State -ne 'Installed') {
Write-Host 'Installing the OpenSSH Server (Windows downloads it - this can take 10 minutes)...'
$restart = [bool](Add-WindowsCapability -Online -Name $cap.Name).RestartNeeded
}
if (-not (Get-Service sshd -ErrorAction SilentlyContinue)) {
$state = (Get-WindowsCapability -Online -Name $cap.Name).State
Write-Host ''
Write-Host '===== RESULT (paste this into the settings page) ====='
Write-Host ('pc=' + $env:COMPUTERNAME + ' sshd=missing openssh=' + $state + ' restart_needed=' + $restart)
Write-Host 'RESTART THIS PC, then run this script again.'
Write-Host '========================================'
return
}
Set-Service -Name sshd -StartupType Automatic
sc.exe failure sshd reset= 86400 actions= restart/5000/restart/5000/restart/30000 | Out-Null # comes back if it stops
Start-Service sshd
if (-not (Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue)) {
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True `
-Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 | Out-Null
}
# Windows' own rule covers "Private" networks only; open it on any network, but to {{INGEST_NAME}} alone.
Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Profile Any -RemoteAddress $IngestIPs
Enable-NetFirewallRule -Name 'OpenSSH-Server-In-TCP'
# 2. {{INGEST_NAME}}'s key. Administrators read C:\ProgramData\ssh\administrators_authorized_keys,
# everyone else their own .ssh\authorized_keys - write both.
$adminKeys = Join-Path $env:ProgramData 'ssh\administrators_authorized_keys'
if (-not (Test-Path $adminKeys) -or -not (Select-String -Path $adminKeys -SimpleMatch $KeyMark -Quiet)) {
Add-Content -Path $adminKeys -Value $Key -Encoding ascii
}
icacls $adminKeys /inheritance:r /grant '*S-1-5-32-544:F' /grant '*S-1-5-18:F' | Out-Null
$userSsh = Join-Path $env:USERPROFILE '.ssh'
New-Item -ItemType Directory -Force -Path $userSsh | Out-Null
$userKeys = Join-Path $userSsh 'authorized_keys'
if (-not (Test-Path $userKeys) -or -not (Select-String -Path $userKeys -SimpleMatch $KeyMark -Quiet)) {
Add-Content -Path $userKeys -Value $Key -Encoding ascii
}
# 3. Start tasks for the apps found on this PC (they run on this user's desktop)
function Find-First($candidates) {
foreach ($c in $candidates) { if ($c -and (Test-Path $c)) { return (Resolve-Path $c).Path } }
return $null
}
$roots = @($env:ProgramFiles, ${env:ProgramFiles(x86)}, (Join-Path $env:LOCALAPPDATA 'Programs')) | Where-Object { $_ }
# Programs can live on any local drive; a program file is looked for under the usual folders, then on each drive.
$drives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Free -ne $null } | ForEach-Object { $_.Root.TrimEnd('\') }
function Find-Exe($name) {
$hit = Get-ChildItem -Path $roots -Filter $name -Recurse -Depth 3 -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch 'uninst|update|crash|helper' } | Select-Object -First 1 -ExpandProperty FullName
if (-not $hit) { $hit = Get-ChildItem -Path $drives -Filter $name -Recurse -Depth 2 -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch 'uninst|update|crash|helper|Windows\\' } | Select-Object -First 1 -ExpandProperty FullName }
return $hit
}
$apps = @(
{{APPS}}
)
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Limited
# A program ticked "Run this program as an administrator" cannot start from a
# Limited task: Windows refuses with 0x800702E4. Its task runs with highest privileges instead.
$elevated = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Highest
$layers = @{}
foreach ($k in 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers',
'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers') {
$p = Get-ItemProperty $k -ErrorAction SilentlyContinue
if ($p) { $p.PSObject.Properties | ForEach-Object { $layers[$_.Name] = "$($_.Value)" } }
}
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
-ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew
$tasks = @()
foreach ($app in $apps) {
if (-not $app.Exe) { continue }
$actionArgs = @{ Execute = $app.Exe; WorkingDirectory = (Split-Path $app.Exe) }
if ($app.Args) { $actionArgs.Argument = $app.Args }
$action = New-ScheduledTaskAction @actionArgs
$runAsAdmin = $layers[$app.Exe] -match 'RUNASADMIN'
$taskArgs = @{ TaskPath = '\StatusPanel\'; TaskName = ('Start ' + $app.Name); Action = $action
Principal = $(if ($runAsAdmin) { $elevated } else { $principal }); Settings = $settings; Force = $true }
if ($app.AtLogon) {
$taskArgs.Trigger = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
$taskArgs.Trigger.Delay = 'PT15S'
}
Register-ScheduledTask @taskArgs | Out-Null
$tasks += ('{0} = {1}{2}' -f $app.Name, $app.Exe, $(if ($app.AtLogon) { ' (also at login)' } else { '' }))
}
# Services the panel starts: if one ever stops unexpectedly, Windows starts it again (5 s, 5 s, then 30 s).
foreach ($svc in @({{SERVICES}})) {
if (Get-Service $svc -ErrorAction SilentlyContinue) {
sc.exe failure $svc reset= 86400 actions= restart/5000/restart/5000/restart/30000 | Out-Null
$notes += ($svc + ' restarts itself if it stops')
}
}
# 4. Wake-on-LAN: let the wired adapter wake the PC (the BIOS setting must be on too)
$nics = Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object Status -eq 'Up'
foreach ($nic in $nics) {
# Only when it is off: changing it can briefly reset the adapter (and the PC's network).
if ((Get-NetAdapterPowerManagement -Name $nic.Name -ErrorAction SilentlyContinue).WakeOnMagicPacket -eq 'Enabled') { continue }
try { Set-NetAdapterPowerManagement -Name $nic.Name -WakeOnMagicPacket Enabled -ErrorAction Stop }
catch { $notes += "Wake-on-LAN not settable on $($nic.Name)" }
}
Write-Host ''
Write-Host '===== RESULT (paste this into the settings page) ====='
Write-Host ('pc=' + $env:COMPUTERNAME + ' user=' + $env:USERNAME + ' domain=' + $env:USERDOMAIN)
Write-Host ('sshd=' + (Get-Service sshd).Status)
foreach ($nic in $nics) { Write-Host ('nic=' + $nic.Name + ' mac=' + $nic.MacAddress + ' ip=' + ((Get-NetIPAddress -InterfaceIndex $nic.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).IPAddress -join ',') + ' network=' + (Get-NetConnectionProfile -InterfaceIndex $nic.ifIndex -ErrorAction SilentlyContinue).NetworkCategory) }
if ($tasks) { $tasks | ForEach-Object { Write-Host ('task: ' + $_) } } else { Write-Host 'task: none (no known apps found)' }
$notes | ForEach-Object { Write-Host ('note: ' + $_) }
Write-Host '========================================'