287 lines
12 KiB
PowerShell
287 lines
12 KiB
PowerShell
param(
|
|
[string] $RavenHost = "raven",
|
|
[string] $LocalViliRoot = "E:\Projects\Vili",
|
|
[string] $LocalCultLibRoot = "E:\Projects\CultLib",
|
|
[string] $RemoteViliRoot = "E:\Projects\Vili",
|
|
[int] $Port = 8824,
|
|
[string] $HostName = "0.0.0.0",
|
|
[string] $StateRoot = "E:\Projects\Vili\.vili",
|
|
[string] $IdunnRudpHealth = $(if ($env:VILI_IDUNN_RUDP_HEALTH) { $env:VILI_IDUNN_RUDP_HEALTH } else { $env:IDUNN_RUDP_HEALTH }),
|
|
[string] $IdunnDaemon = "vili",
|
|
[string] $IdunnHealthContract = "vili.cultnet-rudp-animation-health",
|
|
[int] $IdunnHealthIntervalSeconds = 15
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") {
|
|
throw "restart-vili.ps1 is an Idunn actuator body. Redeploy by poking Idunn; direct service restart is not an owned path."
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($IdunnRudpHealth)) {
|
|
throw "Idunn RUDP health endpoint must be supplied by VILI_IDUNN_RUDP_HEALTH, IDUNN_RUDP_HEALTH, or -IdunnRudpHealth; no WireGuard endpoint default is allowed."
|
|
}
|
|
|
|
$localFiles = @(
|
|
(Join-Path $LocalViliRoot "package.json"),
|
|
(Join-Path $LocalViliRoot "package-lock.json"),
|
|
(Join-Path $LocalViliRoot "scripts\idunn-rudp.cjs"),
|
|
(Join-Path $LocalViliRoot "scripts\install-vili-task.ps1"),
|
|
(Join-Path $LocalViliRoot "scripts\restart-vili-daemon.cmd"),
|
|
(Join-Path $LocalViliRoot "scripts\restart-vili-daemon.ps1"),
|
|
(Join-Path $LocalViliRoot "scripts\run-hidden-powershell.vbs"),
|
|
(Join-Path $LocalViliRoot "scripts\run-vili-daemon.ps1"),
|
|
(Join-Path $LocalViliRoot "scripts\start-vili-daemon.ps1"),
|
|
(Join-Path $LocalViliRoot "scripts\vili-daemon.mjs"),
|
|
(Join-Path $LocalCultLibRoot "packages\cultnet-ts"),
|
|
(Join-Path $LocalCultLibRoot "packages\cultcache-ts"),
|
|
(Join-Path $LocalCultLibRoot "node_modules\ajv"),
|
|
(Join-Path $LocalCultLibRoot "node_modules\fast-deep-equal"),
|
|
(Join-Path $LocalCultLibRoot "node_modules\fast-uri"),
|
|
(Join-Path $LocalCultLibRoot "node_modules\json-schema-traverse"),
|
|
(Join-Path $LocalCultLibRoot "node_modules\require-from-string"),
|
|
(Join-Path $LocalViliRoot "node_modules\@msgpack")
|
|
)
|
|
|
|
foreach ($path in $localFiles) {
|
|
if (-not (Test-Path -LiteralPath $path)) {
|
|
throw "Required Vili runtime path not found: $path"
|
|
}
|
|
}
|
|
|
|
$syncId = [guid]::NewGuid().ToString("N")
|
|
$remoteSyncRootPs = "$RemoteViliRoot\.odin-vili-sync-$syncId"
|
|
$remoteSyncRootSftp = ($remoteSyncRootPs -replace "\\", "/")
|
|
$remoteTempPs = "C:\Windows\Temp\odin-raven-vili-restart-$syncId.ps1"
|
|
$remoteTempSftp = ($remoteTempPs -replace "\\", "/")
|
|
$remoteBundlePs = "C:\Windows\Temp\odin-raven-vili-bundle-$syncId.zip"
|
|
$remoteBundleSftp = ($remoteBundlePs -replace "\\", "/")
|
|
$localRemoteScript = Join-Path $env:TEMP "odin-raven-vili-restart-$syncId.ps1"
|
|
$localSftpBatch = Join-Path $env:TEMP "odin-raven-vili-restart-$syncId.sftp"
|
|
$localBundleRoot = Join-Path $env:TEMP "odin-raven-vili-bundle-$syncId"
|
|
$localBundleScripts = Join-Path $localBundleRoot "scripts"
|
|
$localBundleNodeModules = Join-Path $localBundleRoot "node_modules"
|
|
$localBundleZip = Join-Path $env:TEMP "odin-raven-vili-bundle-$syncId.zip"
|
|
|
|
$remoteScript = @"
|
|
`$ErrorActionPreference = "Stop"
|
|
`$ProgressPreference = "SilentlyContinue"
|
|
|
|
`$remoteRoot = "$RemoteViliRoot"
|
|
`$syncRoot = "$remoteSyncRootPs"
|
|
`$scriptsRoot = Join-Path `$remoteRoot "scripts"
|
|
`$stateRoot = "$StateRoot"
|
|
`$nodeModulesRoot = Join-Path `$remoteRoot "node_modules"
|
|
`$pidPath = Join-Path `$stateRoot "vili-daemon.pid"
|
|
|
|
try {
|
|
Expand-Archive -LiteralPath "$remoteBundlePs" -DestinationPath `$syncRoot -Force
|
|
if (-not (Test-Path -LiteralPath `$syncRoot)) {
|
|
throw "Uploaded sync root not found at `$syncRoot"
|
|
}
|
|
|
|
if (Test-Path -LiteralPath `$pidPath) {
|
|
`$existingPid = (Get-Content -LiteralPath `$pidPath -Raw -ErrorAction SilentlyContinue).Trim()
|
|
if (`$existingPid -match '^\d+$') {
|
|
`$process = Get-Process -Id ([int] `$existingPid) -ErrorAction SilentlyContinue
|
|
if (`$null -ne `$process) {
|
|
Stop-Process -Id `$process.Id -Force
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
}
|
|
Remove-Item -LiteralPath `$pidPath -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
New-Item -ItemType Directory -Force -Path `$scriptsRoot | Out-Null
|
|
New-Item -ItemType Directory -Force -Path `$stateRoot | Out-Null
|
|
|
|
Copy-Item -LiteralPath (Join-Path `$syncRoot "package.json") -Destination (Join-Path `$remoteRoot "package.json") -Force
|
|
Copy-Item -LiteralPath (Join-Path `$syncRoot "package-lock.json") -Destination (Join-Path `$remoteRoot "package-lock.json") -Force
|
|
|
|
`$scriptFiles = @(
|
|
"idunn-rudp.cjs",
|
|
"install-vili-task.ps1",
|
|
"restart-vili-daemon.cmd",
|
|
"restart-vili-daemon.ps1",
|
|
"run-hidden-powershell.vbs",
|
|
"run-vili-daemon.ps1",
|
|
"start-vili-daemon.ps1",
|
|
"vili-daemon.mjs"
|
|
)
|
|
|
|
foreach (`$name in `$scriptFiles) {
|
|
Copy-Item -LiteralPath (Join-Path `$syncRoot "scripts\`$name") -Destination (Join-Path `$scriptsRoot `$name) -Force
|
|
}
|
|
|
|
if (Test-Path -LiteralPath `$nodeModulesRoot) {
|
|
Remove-Item -LiteralPath `$nodeModulesRoot -Recurse -Force
|
|
}
|
|
Move-Item -LiteralPath (Join-Path `$syncRoot "node_modules") -Destination `$nodeModulesRoot -Force
|
|
|
|
& powershell.exe @(
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-ExecutionPolicy", "Bypass",
|
|
"-File", (Join-Path `$scriptsRoot "install-vili-task.ps1"),
|
|
"-TaskName", "GameCult\Vili",
|
|
"-Port", "$Port",
|
|
"-HostName", "$HostName",
|
|
"-StateRoot", "$StateRoot",
|
|
"-IdunnRudpHealth", "$IdunnRudpHealth",
|
|
"-IdunnDaemon", "$IdunnDaemon",
|
|
"-IdunnHealthContract", "$IdunnHealthContract",
|
|
"-IdunnHealthIntervalSeconds", "$IdunnHealthIntervalSeconds"
|
|
)
|
|
|
|
& powershell.exe @(
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-ExecutionPolicy", "Bypass",
|
|
"-File", (Join-Path `$scriptsRoot "restart-vili-daemon.ps1"),
|
|
"-Port", "$Port",
|
|
"-HostName", "$HostName",
|
|
"-StateRoot", "$StateRoot",
|
|
"-IdunnRudpHealth", "$IdunnRudpHealth",
|
|
"-IdunnDaemon", "$IdunnDaemon",
|
|
"-IdunnHealthContract", "$IdunnHealthContract",
|
|
"-IdunnHealthIntervalSeconds", "$IdunnHealthIntervalSeconds"
|
|
)
|
|
|
|
`$task = Get-ScheduledTask -TaskName "Vili" -TaskPath "\GameCult\" -ErrorAction Stop
|
|
`$action = @(`$task.Actions)[0]
|
|
if (`$action.Execute -notmatch '(^|\\)wscript\.exe$') {
|
|
throw "GameCult\Vili action executes `$(`$action.Execute), expected wscript.exe"
|
|
}
|
|
if (`$action.Arguments -notlike "*run-hidden-powershell.vbs*") {
|
|
throw "GameCult\Vili action arguments do not reference run-hidden-powershell.vbs"
|
|
}
|
|
`$taskHealthChecks = @(
|
|
(`$action.Arguments -like "*-IdunnRudpHealth*"),
|
|
(`$action.Arguments -like "*$IdunnRudpHealth*"),
|
|
(`$action.Arguments -like "*-IdunnDaemon*"),
|
|
(`$action.Arguments -like "*$IdunnDaemon*"),
|
|
(`$action.Arguments -like "*-IdunnHealthContract*"),
|
|
(`$action.Arguments -like "*$IdunnHealthContract*")
|
|
)
|
|
if (`$taskHealthChecks -contains `$false) {
|
|
throw "GameCult\Vili action arguments do not carry the Idunn RUDP health contract."
|
|
}
|
|
|
|
`$process = Get-CimInstance Win32_Process |
|
|
Where-Object { `$_.CommandLine -like '*vili-daemon.mjs*' } |
|
|
Select-Object -First 1
|
|
if (`$null -eq `$process) {
|
|
throw "Vili process is not running after restart."
|
|
}
|
|
`$processHealthChecks = @(
|
|
(`$process.CommandLine -like "*--idunn-rudp-health*"),
|
|
(`$process.CommandLine -like "*$IdunnRudpHealth*"),
|
|
(`$process.CommandLine -like "*--idunn-daemon*"),
|
|
(`$process.CommandLine -like "*$IdunnDaemon*"),
|
|
(`$process.CommandLine -like "*--idunn-health-contract*"),
|
|
(`$process.CommandLine -like "*$IdunnHealthContract*")
|
|
)
|
|
if (`$processHealthChecks -contains `$false) {
|
|
throw "Vili process command line does not include the Idunn RUDP health arguments."
|
|
}
|
|
|
|
`$operatorStatePath = Join-Path `$stateRoot "operator-state.json"
|
|
`$deadline = (Get-Date).AddSeconds(30)
|
|
`$state = `$null
|
|
while ((Get-Date) -lt `$deadline) {
|
|
if (Test-Path -LiteralPath `$operatorStatePath) {
|
|
try {
|
|
`$state = Get-Content -LiteralPath `$operatorStatePath -Raw | ConvertFrom-Json
|
|
if (`$null -ne `$state.idunnRudpHealth -and `$state.idunnRudpHealth.status -eq "published") {
|
|
break
|
|
}
|
|
} catch {
|
|
}
|
|
}
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
if (`$null -eq `$state -or `$null -eq `$state.idunnRudpHealth) {
|
|
throw "Vili daemon state file did not report idunnRudpHealth after restart."
|
|
}
|
|
if (`$state.idunnRudpHealth.status -ne "published") {
|
|
throw "Vili idunnRudpHealth status is `$(`$state.idunnRudpHealth.status): `$(`$state.idunnRudpHealth.error)"
|
|
}
|
|
|
|
Write-Host "Vili Raven runtime repaired and restarted with Idunn RUDP health."
|
|
Write-Host "ProcessId=`$(`$process.ProcessId)"
|
|
Write-Host "TaskArguments=`$(`$action.Arguments)"
|
|
Write-Host "HealthStatus=`$(`$state.idunnRudpHealth.status)"
|
|
Write-Host "HealthContract=`$(`$state.idunnRudpHealth.contract)"
|
|
} finally {
|
|
Remove-Item -LiteralPath `$syncRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -LiteralPath "$remoteBundlePs" -Force -ErrorAction SilentlyContinue
|
|
}
|
|
"@
|
|
|
|
$batchLines = @(
|
|
"put ""$localBundleZip"" ""$remoteBundleSftp""",
|
|
"put ""$localRemoteScript"" ""$remoteTempSftp"""
|
|
)
|
|
|
|
try {
|
|
$moduleCopies = @(
|
|
@{ Source = (Join-Path $LocalCultLibRoot "packages\cultnet-ts"); Destination = (Join-Path $localBundleNodeModules "cultnet-ts") },
|
|
@{ Source = (Join-Path $LocalCultLibRoot "packages\cultcache-ts"); Destination = (Join-Path $localBundleNodeModules "cultcache-ts") },
|
|
@{ Source = (Join-Path $LocalCultLibRoot "node_modules\ajv"); Destination = (Join-Path $localBundleNodeModules "ajv") },
|
|
@{ Source = (Join-Path $LocalCultLibRoot "node_modules\fast-deep-equal"); Destination = (Join-Path $localBundleNodeModules "fast-deep-equal") },
|
|
@{ Source = (Join-Path $LocalCultLibRoot "node_modules\fast-uri"); Destination = (Join-Path $localBundleNodeModules "fast-uri") },
|
|
@{ Source = (Join-Path $LocalCultLibRoot "node_modules\json-schema-traverse"); Destination = (Join-Path $localBundleNodeModules "json-schema-traverse") },
|
|
@{ Source = (Join-Path $LocalCultLibRoot "node_modules\require-from-string"); Destination = (Join-Path $localBundleNodeModules "require-from-string") },
|
|
@{ Source = (Join-Path $LocalViliRoot "node_modules\@msgpack"); Destination = (Join-Path $localBundleNodeModules "@msgpack") }
|
|
)
|
|
New-Item -ItemType Directory -Force -Path $localBundleScripts, $localBundleNodeModules | Out-Null
|
|
Copy-Item -LiteralPath (Join-Path $LocalViliRoot "package.json") -Destination (Join-Path $localBundleRoot "package.json") -Force
|
|
Copy-Item -LiteralPath (Join-Path $LocalViliRoot "package-lock.json") -Destination (Join-Path $localBundleRoot "package-lock.json") -Force
|
|
$scriptCopies = @(
|
|
"idunn-rudp.cjs",
|
|
"install-vili-task.ps1",
|
|
"restart-vili-daemon.cmd",
|
|
"restart-vili-daemon.ps1",
|
|
"run-hidden-powershell.vbs",
|
|
"run-vili-daemon.ps1",
|
|
"start-vili-daemon.ps1",
|
|
"vili-daemon.mjs"
|
|
)
|
|
foreach ($scriptName in $scriptCopies) {
|
|
Copy-Item -LiteralPath (Join-Path $LocalViliRoot "scripts\$scriptName") -Destination (Join-Path $localBundleScripts $scriptName) -Force
|
|
}
|
|
foreach ($copy in $moduleCopies) {
|
|
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $copy.Destination) | Out-Null
|
|
Copy-Item -LiteralPath $copy.Source -Destination $copy.Destination -Recurse -Force
|
|
}
|
|
if (Test-Path -LiteralPath $localBundleZip) {
|
|
Remove-Item -LiteralPath $localBundleZip -Force
|
|
}
|
|
Compress-Archive -Path (Join-Path $localBundleRoot "*") -DestinationPath $localBundleZip -CompressionLevel Optimal
|
|
Set-Content -LiteralPath $localRemoteScript -Encoding ASCII -Value $remoteScript
|
|
Set-Content -LiteralPath $localSftpBatch -Encoding ASCII -Value ($batchLines -join "`r`n")
|
|
& sftp.exe -b $localSftpBatch $RavenHost
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
|
|
$remoteRunner = @"
|
|
`$ErrorActionPreference = "Stop"
|
|
try {
|
|
& "$remoteTempPs"
|
|
`$code = `$LASTEXITCODE
|
|
} finally {
|
|
Remove-Item -LiteralPath "$remoteTempPs" -Force -ErrorAction SilentlyContinue
|
|
}
|
|
exit `$code
|
|
"@
|
|
$encodedRunner = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($remoteRunner))
|
|
& ssh.exe -o BatchMode=yes -o ConnectTimeout=10 $RavenHost "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand $encodedRunner"
|
|
} finally {
|
|
Remove-Item -LiteralPath $localRemoteScript, $localSftpBatch -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -LiteralPath $localBundleZip -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -LiteralPath $localBundleRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
$restartExit = $LASTEXITCODE
|
|
exit $restartExit
|